Building a single IP Docker/NPM/Web host

Assumes you have a single public IP (VPS, DO, whatever) and need to lock down to just specified ports and allow NPM to work internally.

Started with Ubuntu 20.04 server, but could be CentOS or RHEL or whatever.

Selected nothing on install, then installed docker like so:

apt-get install apt-transport-https ca-certificates curl gnupg-agent

apt-get install software-properties-common

curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -

add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable"

apt-get update

apt-get install docker-ce docker-ce-cli containerd.io

apt -y install mc iperf3 iptraf-ng

Start portainer

docker run -d --name portainer --restart unless-stopped -p 9000:9000 -v /var/run/docker.sock:/var/run/docker.sock -v portainer_data:/data portainer/portainer

Fixing UFW

vim /etc/ufw/ufw.conf
(change enabled to yes, save and exit)

systemctl start ufw
systemctl enable ufw

ufw allow ssh
ufw reload

This is a workaround to make ufw do what you expect it to do (block ports by default and allow the ports you want) because it conflicts with Docker networking.

This is directly from here: https://github.com/chaifeng/ufw-docker

Edit the ufw rules by running this:
vim /etc/ufw/after.rules

And after this section:
# don't delete the 'COMMIT' line or these rules won't be processed
COMMIT


Add this new section:

# BEGIN UFW AND DOCKER
*filter
:ufw-user-forward - [0:0]
:ufw-docker-logging-deny - [0:0]
:DOCKER-USER - [0:0]
-A DOCKER-USER -j ufw-user-forward

-A DOCKER-USER -j RETURN -s 10.0.0.0/8
-A DOCKER-USER -j RETURN -s 172.16.0.0/12
-A DOCKER-USER -j RETURN -s 192.168.0.0/16

-A DOCKER-USER -p udp -m udp --sport 53 --dport 1024:65535 -j RETURN

-A DOCKER-USER -j ufw-docker-logging-deny -p tcp -m tcp --tcp-flags FIN,SYN,RST,ACK SYN -d 192.168.0.0/16
-A DOCKER-USER -j ufw-docker-logging-deny -p tcp -m tcp --tcp-flags FIN,SYN,RST,ACK SYN -d 10.0.0.0/8
-A DOCKER-USER -j ufw-docker-logging-deny -p tcp -m tcp --tcp-flags FIN,SYN,RST,ACK SYN -d 172.16.0.0/12
-A DOCKER-USER -j ufw-docker-logging-deny -p udp -m udp --dport 0:32767 -d 192.168.0.0/16
-A DOCKER-USER -j ufw-docker-logging-deny -p udp -m udp --dport 0:32767 -d 10.0.0.0/8
-A DOCKER-USER -j ufw-docker-logging-deny -p udp -m udp --dport 0:32767 -d 172.16.0.0/12

-A DOCKER-USER -j RETURN

-A ufw-docker-logging-deny -m limit --limit 3/min --limit-burst 10 -j LOG --log-prefix "[UFW DOCKER BLOCK] "
-A ufw-docker-logging-deny -j DROP

COMMIT
# END UFW AND DOCKER

Save and exit :wq!

Restart ufw:

systemctl restart ufw
ufw reload
ufw allow ssh
ufw reload

This rule set is slightly weird in that to allow ports inbound, say you started NPM on ports 8080 and 4443 (the container ports, not the host mapped ports) you would need to run these commands to let ufw allow communications on the docker network:

ufw route allow proto tcp from any to any port 8080
ufw route allow proto tcp from any to any port 4443
ufw reload

This is enough if you are going to use npm to front other services you load onto the docker host.

( I set up portainer, custom repo for templates, Nginx Proxy Manager, and added a dns record to resolve properly, added a proxy host in NPM to send that domain name to the box’s IP and portainer port (172.17.0.2:9000) used my own setup guides on this site)

Read that again! It’s important that you use the internal IP within the NPM setup to route traffic correctly.

I also do not have the portainer or NPM admin utilities exposed by default, I use ssh port forwarding to keep them off the internet:

ssh -i “~/.ssh/id_rsa_do” -L 9000:127.0.0.1:9000 -L 8181:127.0.0.1:81 root@my.ip.add.ress

This minimizes the ports to 22, 80 and 443.

Cite your sources…
https://www.linode.com/docs/guides/configure-firewall-with-ufw/
https://stackoverflow.com/questions/30383845/what-is-the-best-practice-of-docker-ufw-under-ubuntu
https://superuser.com/questions/590600/ufw-is-active-but-not-enabled-why