nginx-proxy: Add an Nginx proxy image
authorW. Trevor King <wking@tremily.us>
Fri, 3 Jan 2014 03:39:54 +0000 (19:39 -0800)
committerW. Trevor King <wking@tremily.us>
Sat, 4 Jan 2014 01:02:50 +0000 (17:02 -0800)
I've got a bunch of HTTP server images, but I need a single proxy
container that supports SNI to expose them to the world.  This
container can link to a colleciton of HTTP-serving containers and
proxy them under arbitrary hostnames using user-supplied, per-host
certificates and keys.

env and sed are both in POSIX 2013 [1,2], and the:

  eval A=\$$B

variable indirection is because POSIX [3] doesn't support Bash's
indirect expansion "A=${!B}".  The 'tcp' -> 'http' replacement maps
Docker's tcp:://${IP}:${PORT} addresses to http://${IP}:${PORT} for
use in the Nginx config.

envsubst is in gettext [4] and not in POSIX [5], so it may be slightly
less portable.  However, envsubst and gettext are in Gentoo's x86 and
amd64 stage1s, our Gentoo-based images will have it.

[1]: http://pubs.opengroup.org/onlinepubs/9699919799/utilities/env.html
[2]: http://pubs.opengroup.org/onlinepubs/9699919799/utilities/sed.html
[3]: http://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_06_02
[4]: http://www.gnu.org/software/gettext/
[5]: http://pubs.opengroup.org/onlinepubs/9699919799/idx/utilities.html

README.md
build.sh
nginx-proxy/Dockerfile.template [new file with mode: 0644]
nginx-proxy/README.md [new file with mode: 0644]
nginx-proxy/create-vhosts-from-environment.sh [new file with mode: 0755]
nginx-proxy/vhost-template.conf [new file with mode: 0644]

index 6c45ce41da080047bb889b3a169c9cfbd68c7bd1..a654a626f17cdc912de4e90c57476f9d491000bb 100644 (file)
--- a/README.md
+++ b/README.md
@@ -23,6 +23,7 @@ The dependency graph is:
                 |-- buildbot  (adds a Buildbot master and slave)
                 |-- memcached  (adds Memcached)
                 |-- nginx  (adds Nginx)
+                |   |-- nginx-proxy  (SSL/TLS proxying via SNI)
                 |   `-- kibana  (adds Kibana)
                 |-- postgresql  (adds PostgreSQL)
                 |-- redis  (adds Redis)
index 197bdbd4a5b9257f55e7e125690f2d1d79d74708..77684c83432e51ccaa13ae859ed3bf340f731356 100755 (executable)
--- a/build.sh
+++ b/build.sh
@@ -46,6 +46,7 @@ REPOS="${REPOS:-
        elasticsearch
        memcached
        nginx
+       nginx-proxy
        kibana
        postgresql
        redis
diff --git a/nginx-proxy/Dockerfile.template b/nginx-proxy/Dockerfile.template
new file mode 100644 (file)
index 0000000..0e1e184
--- /dev/null
@@ -0,0 +1,41 @@
+# Copyright (C) 2014 W. Trevor King <wking@tremily.us>
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
+#
+# * Redistributions of source code must retain the above copyright notice, this
+# list of conditions and the following disclaimer.
+#
+# * Redistributions in binary form must reproduce the above copyright notice,
+# this list of conditions and the following disclaimer in the documentation
+# and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
+
+FROM ${NAMESPACE}/nginx:${TAG}
+MAINTAINER ${MAINTAINER}
+
+# Add a framework for easy virtual hosts
+RUN mkdir /etc/nginx/vhosts
+RUN chown nginx:nginx /etc/nginx/vhosts
+RUN sed -i 's|^\([[:space:]]*\)\(server {\)|\1include vhosts/*.conf;\n\n\1\2|' /etc/nginx/nginx.conf
+ADD vhost-template.conf /etc/nginx/vhosts/TEMPLATE
+RUN chown nginx:nginx /etc/nginx/vhosts/TEMPLATE
+ADD create-vhosts-from-environment.sh /usr/bin/create-vhosts-from-environment
+
+# Uncomment the default HTTPS server
+RUN sed -i 's/^\t#\([^ ]\)/\t\1/' /etc/nginx/nginx.conf
+RUN sed -i 's/listen 127.0.0.1:443;/listen 443 default_server;/' /etc/nginx/nginx.conf
+
+CMD create-vhosts-from-environment && rc default && tail -F /var/log/messages
+EXPOSE 443
diff --git a/nginx-proxy/README.md b/nginx-proxy/README.md
new file mode 100644 (file)
index 0000000..109300d
--- /dev/null
@@ -0,0 +1,31 @@
+While the `nginx` container serves a single, unnamed host from
+`/var/www/localhost/htdocs`, this container uses [Nginx] to proxy a
+collection of virtual servers via hostname.  It listens for both
+plaintext and [TLS][] connections, and uses [Server Name Indication
+(SNI)][SNI] to serve the appropriate [X.509][] certificate.
+
+Run this [Nginx][] image with:
+
+    $ docker run -d -name nginx-a -v /var/www/a.net/htdocs:/var/www/localhost/htdocs wking/nginx
+    $ docker run -d -name nginx-b -v /var/www/b.com/htdocs:/var/www/localhost/htdocs wking/nginx
+    $ docker run -d -name nginx-proxy-0 -link nginx-a:a -e A_NAME=a.com -link nginx-b:b -e B_NAME=b.net -v /etc/ssl/nginx-proxy-0:/etc/ssl/nginx -p 80:80 -p 443:443 wking/nginx-proxy
+
+[volume-mounting][volume-mount] your certificates and keys under the
+container's `/etc/ssl/nginx`.  The `*_NAME` environment variables
+override Docker's [default][link-name] `/${LINKER}/${LINKEE}`.  For
+example, without th `-e A_NAME=a.com` argument, `A_NAME` would be
+`/nginx-proxy-0/a`.  You should avoid link aliases with `-` and `.` in
+them, because `A.NET_NAME` is not a valid shell variable.
+
+[HAProxy][] added native SSL support with [version 1.5-dev12
+(2012-09-10)][HAProxy-v1.5], but v1.5 isn't stable yet so I think
+Nginx is the best tool for this task.
+
+[Nginx]: http://nginx.org/
+[TLS]: http://en.wikipedia.org/wiki/Transport_layer_security
+[SNI]: http://en.wikipedia.org/wiki/Server_Name_Indication
+[X.509]: http://en.wikipedia.org/wiki/X.509
+[volume-mount]: http://docs.docker.io/en/latest/use/working_with_volumes/
+[link-name]: http://docs.docker.io/en/latest/use/working_with_links_names/
+[HAProxy]: http://haproxy.1wt.eu/
+[HAProxy-v1.5]: http://haproxy.1wt.eu/download/1.5/src/CHANGELOG
diff --git a/nginx-proxy/create-vhosts-from-environment.sh b/nginx-proxy/create-vhosts-from-environment.sh
new file mode 100755 (executable)
index 0000000..67590a4
--- /dev/null
@@ -0,0 +1,44 @@
+#!/bin/sh
+#
+# Copyright (C) 2014 W. Trevor King <wking@tremily.us>
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
+#
+# * Redistributions of source code must retain the above copyright notice, this
+# list of conditions and the following disclaimer.
+#
+# * Redistributions in binary form must reproduce the above copyright notice,
+# this list of conditions and the following disclaimer in the documentation
+# and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
+
+# usage: C1_PORT=tcp://192.168.0.1:12345/ C1_NAME=a.com \
+#        C2_PORT=tcp://192.168.0.2:54321/ C2_NAME=b.net \
+#        create-vhosts-from-environment
+
+for NAME_VARIABLE in $(env | sed -n 's/^\([^=]*_NAME\)=.*/\1/p'); do
+       URL_VARIABLE="${NAME_VARIABLE%_NAME}_PORT"
+       eval NAME="\$$NAME_VARIABLE"
+       eval URL="\$$URL_VARIABLE"
+       URL="http://${URL#tcp://}"
+       env -i \
+               NAME="${NAME}" \
+               URL="${URL}" \
+               envsubst '
+                       ${NAME}
+                       ${URL}
+                       ' \
+                       < /etc/nginx/vhosts/TEMPLATE > "/etc/nginx/vhosts/${NAME}.conf"
+done
diff --git a/nginx-proxy/vhost-template.conf b/nginx-proxy/vhost-template.conf
new file mode 100644 (file)
index 0000000..77c403e
--- /dev/null
@@ -0,0 +1,43 @@
+# Copyright (C) 2014 W. Trevor King <wking@tremily.us>
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
+#
+# * Redistributions of source code must retain the above copyright notice, this
+# list of conditions and the following disclaimer.
+#
+# * Redistributions in binary form must reproduce the above copyright notice,
+# this list of conditions and the following disclaimer in the documentation
+# and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
+
+server {
+       listen 80;
+       listen 443 ssl;
+       server_name ${NAME};
+
+       ssl on;
+       ssl_certificate /etc/ssl/nginx/${NAME}.pem;
+       ssl_certificate_key /etc/ssl/nginx/${NAME}.key;
+
+       access_log /var/log/nginx/${NAME}.ssl_access_log main;
+       error_log /var/log/nginx/${NAME}.ssl_error_log info;
+
+       root /var/www/${NAME}/htdocs;
+
+       location / {
+               proxy_pass ${URL};
+               proxy_set_header X-Forwarded-For $remote_addr;
+       }
+}