├── Dockerfile ├── README.md └── data ├── Procfile ├── docker-entrypoint.sh └── nginx.tmpl /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM resin/rpi-raspbian 2 | MAINTAINER Ludovic Roguet 3 | 4 | # Install wget and install/updates certificates 5 | RUN apt-get update \ 6 | && apt-get install -y -q --no-install-recommends \ 7 | ca-certificates \ 8 | git \ 9 | mercurial \ 10 | golang \ 11 | nginx \ 12 | wget \ 13 | && apt-get clean \ 14 | && rm -r /var/lib/apt/lists/* 15 | 16 | # Configure Nginx and apply fix for very long server names 17 | RUN echo "daemon off;" >>/etc/nginx/nginx.conf \ 18 | && sed -i 's/^http {/&\n server_names_hash_bucket_size 128;/g' /etc/nginx/nginx.conf 19 | 20 | # Install Forego 21 | ENV GOPATH /opt/go 22 | ENV PATH $PATH:$GOPATH/bin 23 | RUN go get -u github.com/ddollar/forego 24 | 25 | ENV DOCKER_GEN_VERSION 0.7.3 26 | 27 | RUN wget https://github.com/jwilder/docker-gen/releases/download/$DOCKER_GEN_VERSION/docker-gen-linux-armhf-$DOCKER_GEN_VERSION.tar.gz \ 28 | && tar -C /usr/local/bin -xvzf docker-gen-linux-armhf-$DOCKER_GEN_VERSION.tar.gz \ 29 | && rm /docker-gen-linux-armhf-$DOCKER_GEN_VERSION.tar.gz 30 | 31 | COPY data /app/ 32 | WORKDIR /app/ 33 | 34 | ENV DOCKER_HOST unix:///tmp/docker.sock 35 | 36 | VOLUME ["/etc/nginx/certs"] 37 | 38 | ENTRYPOINT ["/app/docker-entrypoint.sh"] 39 | CMD ["forego", "start", "-r"] 40 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![lroguet/rpi-nginx-proxy](https://img.shields.io/docker/pulls/lroguet/rpi-nginx-proxy.svg)](https://hub.docker.com/r/lroguet/rpi-nginx-proxy/) 2 | [![lroguet/rpi-nginx-proxy](https://images.microbadger.com/badges/version/lroguet/rpi-nginx-proxy.svg)](https://hub.docker.com/r/lroguet/rpi-nginx-proxy/) [![lroguet/rpi-nginx-proxy](https://images.microbadger.com/badges/image/lroguet/rpi-nginx-proxy.svg)](https://hub.docker.com/r/lroguet/rpi-nginx-proxy/) 3 | 4 | # NGINX reverse proxy Docker image for Raspberry Pi 5 | 6 | ## Description 7 | lroguet/rpi-nginx-proxy sets up a container running NGINX and [docker-gen](https://github.com/jwilder/docker-gen). docker-gen generates reverse proxy configurations for NGINX and reloads NGINX when containers are started and stopped. lroguet/rpi-nginx-proxy is a **Raspberry Pi** compatible version of [jwilder/nginx-proxy](https://github.com/jwilder/nginx-proxy). 8 | 9 | See [Nginx reverse proxy, Docker and a Raspberry Pi](https://fourteenislands.io/2016/04/nginx-reverse-proxy-docker-and-a-raspberry-pi/) for a more detailed explanation. 10 | 11 | ## Usage 12 | ``` 13 | $ docker run -d -p 80:80 -v /var/run/docker.sock:/tmp/docker.sock:ro lroguet/rpi-nginx-proxy 14 | ``` 15 | 16 | ## Show some love 17 | If you find `lroguet/rpi-nginx-proxy` useful please consider making a donation. 18 | 19 | Bitcoin (BTC). `1JU59RHfmdEZCPgetf3rjrWU8JQiFeGPaL` 20 | Ethereum (ETH). `0x5BbaAb38Be768F281Bc08Ee380735FC5C8cc5CD0` 21 | 22 | ## Links 23 | * [Docker Hub](https://hub.docker.com/r/lroguet/rpi-nginx-proxy/) 24 | * [In action](https://fourteenislands.io/2016/04/nginx-reverse-proxy-docker-and-a-raspberry-pi/) 25 | -------------------------------------------------------------------------------- /data/Procfile: -------------------------------------------------------------------------------- 1 | nginx: nginx 2 | dockergen: docker-gen -watch -notify "nginx -s reload" /app/nginx.tmpl /etc/nginx/conf.d/default.conf 3 | -------------------------------------------------------------------------------- /data/docker-entrypoint.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | 4 | # Warn if the DOCKER_HOST socket does not exist 5 | if [[ $DOCKER_HOST == unix://* ]]; then 6 | socket_file=${DOCKER_HOST#unix://} 7 | if ! [ -S $socket_file ]; then 8 | cat >&2 <<-EOT 9 | ERROR: you need to share your Docker host socket with a volume at $socket_file 10 | Typically you should run your jwilder/nginx-proxy with: \`-v /var/run/docker.sock:$socket_file:ro\` 11 | See the documentation at http://git.io/vZaGJ 12 | EOT 13 | socketMissing=1 14 | fi 15 | fi 16 | 17 | # If the user has run the default command and the socket doesn't exist, fail 18 | if [ "$socketMissing" = 1 -a "$1" = forego -a "$2" = start -a "$3" = '-r' ]; then 19 | exit 1 20 | fi 21 | 22 | exec "$@" 23 | -------------------------------------------------------------------------------- /data/nginx.tmpl: -------------------------------------------------------------------------------- 1 | {{ $CurrentContainer := where $ "ID" .Docker.CurrentContainerID | first }} 2 | 3 | {{ define "upstream" }} 4 | {{ if .Address }} 5 | {{/* If we got the containers from swarm and this container's port is published to host, use host IP:PORT */}} 6 | {{ if and .Container.Node.ID .Address.HostPort }} 7 | # {{ .Container.Node.Name }}/{{ .Container.Name }} 8 | server {{ .Container.Node.Address.IP }}:{{ .Address.HostPort }}; 9 | {{/* If there is no swarm node or the port is not published on host, use container's IP:PORT */}} 10 | {{ else if .Network }} 11 | # {{ .Container.Name }} 12 | server {{ .Network.IP }}:{{ .Address.Port }}; 13 | {{ end }} 14 | {{ else if .Network }} 15 | # {{ .Container.Name }} 16 | server {{ .Network.IP }} down; 17 | {{ end }} 18 | {{ end }} 19 | 20 | # If we receive X-Forwarded-Proto, pass it through; otherwise, pass along the 21 | # scheme used to connect to this server 22 | map $http_x_forwarded_proto $proxy_x_forwarded_proto { 23 | default $http_x_forwarded_proto; 24 | '' $scheme; 25 | } 26 | 27 | # If we receive Upgrade, set Connection to "upgrade"; otherwise, delete any 28 | # Connection header that may have been passed to this server 29 | map $http_upgrade $proxy_connection { 30 | default upgrade; 31 | '' close; 32 | } 33 | 34 | gzip_types text/plain text/css application/javascript application/json application/x-javascript text/xml application/xml application/xml+rss text/javascript; 35 | 36 | log_format vhost '$host $remote_addr - $remote_user [$time_local] ' 37 | '"$request" $status $body_bytes_sent ' 38 | '"$http_referer" "$http_user_agent"'; 39 | 40 | access_log off; 41 | 42 | {{ if (exists "/etc/nginx/proxy.conf") }} 43 | include /etc/nginx/proxy.conf; 44 | {{ else }} 45 | # HTTP 1.1 support 46 | proxy_http_version 1.1; 47 | proxy_buffering off; 48 | proxy_set_header Host $http_host; 49 | proxy_set_header Upgrade $http_upgrade; 50 | proxy_set_header Connection $proxy_connection; 51 | proxy_set_header X-Real-IP $remote_addr; 52 | proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; 53 | proxy_set_header X-Forwarded-Proto $proxy_x_forwarded_proto; 54 | 55 | # Mitigate httpoxy attack (see README for details) 56 | proxy_set_header Proxy ""; 57 | {{ end }} 58 | 59 | server { 60 | server_name _; # This is just an invalid value which will never trigger on a real hostname. 61 | listen 80; 62 | access_log /var/log/nginx/access.log vhost; 63 | return 503; 64 | } 65 | 66 | {{ if (and (exists "/etc/nginx/certs/default.crt") (exists "/etc/nginx/certs/default.key")) }} 67 | server { 68 | server_name _; # This is just an invalid value which will never trigger on a real hostname. 69 | listen 443 ssl http2; 70 | access_log /var/log/nginx/access.log vhost; 71 | return 503; 72 | 73 | ssl_session_tickets off; 74 | ssl_certificate /etc/nginx/certs/default.crt; 75 | ssl_certificate_key /etc/nginx/certs/default.key; 76 | } 77 | {{ end }} 78 | 79 | {{ range $host, $containers := groupByMulti $ "Env.VIRTUAL_HOST" "," }} 80 | 81 | upstream {{ $host }} { 82 | {{ range $container := $containers }} 83 | {{ $addrLen := len $container.Addresses }} 84 | 85 | {{ range $knownNetwork := $CurrentContainer.Networks }} 86 | {{ range $containerNetwork := $container.Networks }} 87 | {{ if eq $knownNetwork.Name $containerNetwork.Name }} 88 | ## Can be connect with "{{ $containerNetwork.Name }}" network 89 | 90 | {{/* If only 1 port exposed, use that */}} 91 | {{ if eq $addrLen 1 }} 92 | {{ $address := index $container.Addresses 0 }} 93 | {{ template "upstream" (dict "Container" $container "Address" $address "Network" $containerNetwork) }} 94 | {{/* If more than one port exposed, use the one matching VIRTUAL_PORT env var, falling back to standard web port 80 */}} 95 | {{ else }} 96 | {{ $port := coalesce $container.Env.VIRTUAL_PORT "80" }} 97 | {{ $address := where $container.Addresses "Port" $port | first }} 98 | {{ template "upstream" (dict "Container" $container "Address" $address "Network" $containerNetwork) }} 99 | {{ end }} 100 | {{ end }} 101 | {{ end }} 102 | {{ end }} 103 | {{ end }} 104 | } 105 | 106 | {{ $default_host := or ($.Env.DEFAULT_HOST) "" }} 107 | {{ $default_server := index (dict $host "" $default_host "default_server") $host }} 108 | 109 | {{/* Get the VIRTUAL_PROTO defined by containers w/ the same vhost, falling back to "http" */}} 110 | {{ $proto := or (first (groupByKeys $containers "Env.VIRTUAL_PROTO")) "http" }} 111 | 112 | {{/* Get the HTTPS_METHOD defined by containers w/ the same vhost, falling back to "redirect" */}} 113 | {{ $https_method := or (first (groupByKeys $containers "Env.HTTPS_METHOD")) "redirect" }} 114 | 115 | {{/* Get the first cert name defined by containers w/ the same vhost */}} 116 | {{ $certName := (first (groupByKeys $containers "Env.CERT_NAME")) }} 117 | 118 | {{/* Get the best matching cert by name for the vhost. */}} 119 | {{ $vhostCert := (closest (dir "/etc/nginx/certs") (printf "%s.crt" $host))}} 120 | 121 | {{/* vhostCert is actually a filename so remove any suffixes since they are added later */}} 122 | {{ $vhostCert := trimSuffix ".crt" $vhostCert }} 123 | {{ $vhostCert := trimSuffix ".key" $vhostCert }} 124 | 125 | {{/* Use the cert specified on the container or fallback to the best vhost match */}} 126 | {{ $cert := (coalesce $certName $vhostCert) }} 127 | 128 | {{ $is_https := (and (ne $cert "") (exists (printf "/etc/nginx/certs/%s.crt" $cert)) (exists (printf "/etc/nginx/certs/%s.key" $cert))) }} 129 | 130 | {{ if $is_https }} 131 | 132 | {{ if eq $https_method "redirect" }} 133 | server { 134 | server_name {{ $host }}; 135 | listen 80 {{ $default_server }}; 136 | access_log /var/log/nginx/access.log vhost; 137 | return 301 https://$host$request_uri; 138 | } 139 | {{ end }} 140 | 141 | server { 142 | server_name {{ $host }}; 143 | listen 443 ssl http2 {{ $default_server }}; 144 | access_log /var/log/nginx/access.log vhost; 145 | 146 | ssl_protocols TLSv1 TLSv1.1 TLSv1.2; 147 | ssl_ciphers 'ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA:ECDHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA256:DHE-RSA-AES256-SHA:ECDHE-ECDSA-DES-CBC3-SHA:ECDHE-RSA-DES-CBC3-SHA:EDH-RSA-DES-CBC3-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:DES-CBC3-SHA:!DSS'; 148 | 149 | ssl_prefer_server_ciphers on; 150 | ssl_session_timeout 5m; 151 | ssl_session_cache shared:SSL:50m; 152 | ssl_session_tickets off; 153 | 154 | ssl_certificate /etc/nginx/certs/{{ (printf "%s.crt" $cert) }}; 155 | ssl_certificate_key /etc/nginx/certs/{{ (printf "%s.key" $cert) }}; 156 | 157 | {{ if (exists (printf "/etc/nginx/certs/%s.dhparam.pem" $cert)) }} 158 | ssl_dhparam {{ printf "/etc/nginx/certs/%s.dhparam.pem" $cert }}; 159 | {{ end }} 160 | 161 | {{ if (ne $https_method "noredirect") }} 162 | add_header Strict-Transport-Security "max-age=31536000"; 163 | {{ end }} 164 | 165 | {{ if (exists (printf "/etc/nginx/vhost.d/%s" $host)) }} 166 | include {{ printf "/etc/nginx/vhost.d/%s" $host }}; 167 | {{ else if (exists "/etc/nginx/vhost.d/default") }} 168 | include /etc/nginx/vhost.d/default; 169 | {{ end }} 170 | 171 | location / { 172 | {{ if eq $proto "uwsgi" }} 173 | include uwsgi_params; 174 | uwsgi_pass {{ trim $proto }}://{{ trim $host }}; 175 | {{ else }} 176 | proxy_pass {{ trim $proto }}://{{ trim $host }}; 177 | {{ end }} 178 | {{ if (exists (printf "/etc/nginx/htpasswd/%s" $host)) }} 179 | auth_basic "Restricted {{ $host }}"; 180 | auth_basic_user_file {{ (printf "/etc/nginx/htpasswd/%s" $host) }}; 181 | {{ end }} 182 | {{ if (exists (printf "/etc/nginx/vhost.d/%s_location" $host)) }} 183 | include {{ printf "/etc/nginx/vhost.d/%s_location" $host}}; 184 | {{ else if (exists "/etc/nginx/vhost.d/default_location") }} 185 | include /etc/nginx/vhost.d/default_location; 186 | {{ end }} 187 | } 188 | } 189 | 190 | {{ end }} 191 | 192 | {{ if or (not $is_https) (eq $https_method "noredirect") }} 193 | 194 | server { 195 | server_name {{ $host }}; 196 | listen 80 {{ $default_server }}; 197 | access_log /var/log/nginx/access.log vhost; 198 | 199 | {{ if (exists (printf "/etc/nginx/vhost.d/%s" $host)) }} 200 | include {{ printf "/etc/nginx/vhost.d/%s" $host }}; 201 | {{ else if (exists "/etc/nginx/vhost.d/default") }} 202 | include /etc/nginx/vhost.d/default; 203 | {{ end }} 204 | 205 | location / { 206 | {{ if eq $proto "uwsgi" }} 207 | include uwsgi_params; 208 | uwsgi_pass {{ trim $proto }}://{{ trim $host }}; 209 | {{ else }} 210 | proxy_pass {{ trim $proto }}://{{ trim $host }}; 211 | {{ end }} 212 | {{ if (exists (printf "/etc/nginx/htpasswd/%s" $host)) }} 213 | auth_basic "Restricted {{ $host }}"; 214 | auth_basic_user_file {{ (printf "/etc/nginx/htpasswd/%s" $host) }}; 215 | {{ end }} 216 | {{ if (exists (printf "/etc/nginx/vhost.d/%s_location" $host)) }} 217 | include {{ printf "/etc/nginx/vhost.d/%s_location" $host}}; 218 | {{ else if (exists "/etc/nginx/vhost.d/default_location") }} 219 | include /etc/nginx/vhost.d/default_location; 220 | {{ end }} 221 | } 222 | } 223 | 224 | {{ if (and (not $is_https) (exists "/etc/nginx/certs/default.crt") (exists "/etc/nginx/certs/default.key")) }} 225 | server { 226 | server_name {{ $host }}; 227 | listen 443 ssl http2 {{ $default_server }}; 228 | access_log /var/log/nginx/access.log vhost; 229 | return 500; 230 | 231 | ssl_certificate /etc/nginx/certs/default.crt; 232 | ssl_certificate_key /etc/nginx/certs/default.key; 233 | } 234 | {{ end }} 235 | 236 | {{ end }} 237 | {{ end }} 238 | --------------------------------------------------------------------------------