Leveraging Reverse Proxying
Info
This part of the guide has been updated as a standalone article here
TL;DR
In this article, we will:
- Place portainer, cockpit and nginx proxy manager (well at least the admin interface) behind the reverse proxy for ssl termination/ip whitelisting on all our admin interfaces.
Leveraging the Reverse Proxy
Now we can do something cool. With all of our services in the reverse proxy network, we can now get our management interfaces protected! Even the reverse proxy! This is fantastic as it allows us to tunnel all of our admin interfaces behind validated SSL and protect them with IP whitelisting.
Let's make 3 more subdomains in our DNS:
portainer.<your-domain>.<tld>
npm.<your-domain>.<tld>
<your-docker-host>.<your-domain>.<tld>
(this guide usescbdocker02
as it's hostname)
Info
in OPNsense, since they're all the same IP we can do so using aliases
- In the Nginx Proxy Manager, create a proxy host to
npm.<your-domain>.<tld>
on http, pointing tonginx-proxy-manager
on port81
. Set the access list tolocal subnets
(management interfaces should not be exposed to the web, except with great discretion).
- Under SSL, set your wildcard SSL certificate (or generate a new let's encrypt certificate). Press save.
- Now let's do it all again for
portainer.<yourdomain>.<tld>
note the fact that we're proxying to an https endpoint this time.
- Also add or generate your SSL certificate and save.
- Same thing for
<your-host>.<your-domain>.<tld>
for cockpit (on port 9090).
Info
Note that we are reverse proxying to the same domain address (since it should resolve back to our host IP). We are also setting the Scheme to HTTPS, same as portainer.
- Don't forget the SSL
Putting Portainer on the Reverse Proxy
Oops, we missed a step. Even though we are reverse proxying to portainer, portainer isn't on the same network! Let's fix that. in cockpit, navigate to /mnt/containers/portainer
and update the docker-compose.yaml
. Let's comment out the port exposure at the same time:
services:
portainer:
image: portainer/portainer-ce:latest
container_name: portainer
restart: always
privileged: true
volumes:
- /mnt/containers/portainer/container-data/data:/data:Z
- /var/run/docker.sock:/var/run/docker.sock:Z
# ports:
# - 9443:9443
networks:
- reverseproxy-nw
networks:
reverseproxy-nw:
external: true
You can see me do that below using mc and micro:
Info
At the start of the animation, you can see me uncheck use internal editor. This tells mc to default to micro as the editor.
Testing our Setup
Let's now verify that you can access https://portainer.<yourdomain>.<tld>
, https://<your-host>.<your-domain>.<tld>
and https://npm.<yourdomain>.<tld>
. You can see me do that in the animation below:
(/doing-more-with-docker/07-Leveraging-Reverse-Proxying-09.gif)
Success! We are now proxying all of our web admin interfaces behind nginx proxy manager, and restricting those interfaces to local IPs.
Info
The eagle eyed of you may notice the 'login with oauth' prompt for portainer. I may have been reading a bit ahead when creating this animation. We'll get there!
Removing the Port Exposures
Now log into portainer, and comment out the port exposure for nginx-proxy-manager. Now our only method of accessing the nginx-proxy-manager admin portal and portainer is through the reverse proxy.
Info
Note that we did not comment out the port exposure for cockpit. This is because if we screw up the reverse proxy, we want a way to get back into our host on port 9090. This is referred to as an out of band access method.
Documentation
Always. Be. ~~Hustling~~ Documenting.
Update gitea to reflect our updated nginx proxy manager configuration:
and portainer:
Moving On
Alright, we are making good progress. We have most of the tools we need to create a stable, secure docker environment.
One critical part is missing though. We aren't backing up our data! We need backups. Let's address that with Backups up Docker.