A reverse proxy is the best way to access lots of containers running on one IP without having to assign tons of IP Addresses. When I create multiple subdomains pointing to the same IP, I am able to use the Reverse Proxy to decide what container traffic from a given subdomain should go to.
Prerequisites:
docker pull nginx
docker network create reverse-proxy
sudo mkdir /dockerdata/reverse-proxy/
sudo mkdir /dockerdata/reverse-proxy/config/
sudo mkdir /dockerdata/reverse-proxy/config/sites-enabled/
sudo mkdir /dockerdata/reverse-proxy/config/sites-available/
sudo mkdir /dockerdata/reverse-proxy/certs/
docker run -d \
--name reverse-proxy \
--restart unless-stopped \
--network reverse-proxy \
-p 80:80 \
-p 443:443 \
-v /dockerdata/reverse-proxy/config/:/etc/nginx/ \
-v /dockerdata/reverse-proxy/certs/:/etc/ssl/private/ \
nginx:latest \
At this point, I copied everything under /etc/nginx/
to /dockerdata/reverse-proxy/config
. I also made a backup of the default config file to be safe.
Editing /dockerdata/reverse-proxy/config/nginx.conf
, I added one server per each container:
http {
...
server {
server_name <yourservername>;
include /etc/nginx/default.d/*.conf;
location / {
proxy_pass http://<youraddress>:<yourport>/;
}
}
}
Remember to restart the container after making changes to NGINX config:
docker restart reverse-proxy
After that, it's just a matter of connecting the containers to the reverse-proxy
network:
docker network connect reverse-proxy <yourcontainername>
Remember to check firewall rules on the docker host to ensure port 80 and/or 443 is exposed!
You can test the routing on the host by running: curl --header "Host: <yourdomainname.com>" localhost
Having created a wildcard certificate using the Certbot Container, I now have these files under /dockerdata/certs/
:
cert1.pem chain1.pem fullchain1.pem privkey1.pem
Not sure if you have to do this, but I renamed privkey1.pem
to privkey1.key
.
I copied those files to the folder mounted by the reverse-proxy
docker container, then updated the reverse-proxy NGINX Config:
server {
listen 443 ssl;
ssl on;
ssl_certificate </your/ssl/cert.pem>;
ssl_certificate_key </your/ssl/cert.key>;
location / {
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
proxy_set_header X-Forwarded-Proto https;
proxy_redirect off;
proxy_pass http://<docker-server-name>:<port>/;
proxy_http_version 1.1;
}
}
I've rolled back the changes shown below but am including them in case we ever decide we want to go this route.
Before realizing that the traffic behind the reverse proxy is all contained within the virtual Docker network and likely won't really help our security that much, I had read the following article and had set up encryption to the containers themselves: https://reinout.vanrees.org/weblog/2017/05/02/https-behind-proxy.html
Apparently this is called re-encrypting.
The only change needed in the proxy's config is to change the proxy_pass
line from http://
to https://
, and update the port if needed as well.
Then in the internal server's NGINX config:
server {
listen 443 ssl;
server_name _;
ssl on;
ssl_certificate /app/certs/fullchain1.pem;
ssl_certificate_key /app/certs/privkey1.key;
client_max_body_size 0;
location = /favicon.ico { access_log off; log_not_found off; }
location /static/ {
root /var/www/;
}
location / {
include uwsgi_params;
uwsgi_pass unix:/app/wsgi.sock;
}
}
However: I'm reading now that encrypting between the reverse proxy and the server behind isn't really needed unless that traffic is exposed somehow. With us having this traffic run between docker containers (inside of a virtual docker network even), I'm thinking we only need to encrypt the part of the journey that is exposed: From client to reverse proxy. See this question for more.
If I did want to do that, it might be better to use the Ubuntu Snakeoil cert rather than repeat the same wildcard cert.
Since we're not going to worry about that, though, then I don't have to worry about changing how things work when using the dev server. Our docker containers behind the reverse proxy don't have to think about or know about the SSL encryption at all!