How to Disable Docker Ports for Public Users and Limit Ports to Specific IP Addresses
As we know, UFW (Uncomplicated Firewall) is a frontend for managing iptables rules, and Docker uses iptables directly. Therefore, when we disable a port on UFW, it won't apply to Docker containers. Let's consider the scenario where we want to disable port 5432, the default port for PostgreSQL. We can choose not to publish this port in the Docker Compose file, but what if we want to disable this port (or other ports) for public access through UFW or limit access to a specific IP range? In other words, we want to create a whitelist and blacklist so unauthorized users cannot access specific ports.
Let's consider we are going to limit specific ports to a public IP. in this case iptables can be disabled for docker, and every things can be managed via UFW.
Step 1: Disable iptables for Docker
To disable iptables for Docker, open (or create if it doesn't exist) the file /etc/docker/daemon.json:
sudo nano /etc/docker/daemon.json
Add the following line to the file:
{
"iptables": false
}
Save and close the file, then restart the Docker service:
sudo systemctl restart docker.service
Step 2: Check and Enable UFW
Check the status of UFW:
sudo ufw status
By default, UFW is disabled, so it should be enabled. Before enabling it, ensure that your IP has access to the VM when UFW is activated. Allow SSH access by adding your public/private IP, depending on how you connect to the VM or instance:
sudo ufw allow from x.x.x.x to any port 22
Here, port 22 is the default SSH port. If you've changed the SSH port, replace 22 with your custom port number.
Now, enable UFW:
sudo ufw enable
Step 3: Limit Ports
The best approach is to limit access to all ports and then open only the crucial ones. First, edit the default UFW configuration file:
sudo nano /etc/default/ufw
In this file, you can enable/disable IPv6 and set DEFAULT_INPUT_POLICY to DROP. This setting means UFW will drop (not accept) all requests unless there is a rule to open a specific port, such as port 80.
Step 4: Enable Internet Access for Docker Networks
There is a problem with network interfaces: except for the docker0 interface, other interfaces created in Docker Compose files cannot connect to the internet. To solve this, we need to set up a masquerade type of NAT for a specific IP range, such as 192.168.1.0/24
sudo iptables -t nat -A POSTROUTING -s 192.168.1.0/24 ! -o docker0 -j MASQUERADE
In this command:
This command allows internet access for our custom private IP range. However, note that all iptables commands are temporary and not persistent. They will be deleted after a reboot or network service restart.
Step 5: Make iptables Rules Persistent
To prevent iptables from resetting to the default configuration, create a bash script, e.g., nat.sh, with the following content:
#!/bin/bash
sudo iptables -t nat -A POSTROUTING -s 192.168.1.0/24 ! -o docker0 -j MASQUERADE
Save and close the file, then make it executable:
chmod +x nat.sh
Set up a cron job to run this script on reboot:
crontab -e
Select your desired editor (e.g., nano, which is number 1), and add the following line at the end:
@reboot <your_script_dir>/nat.sh
Additionally, add your MASQUERADE to /etc/ufw/before.rules:
sudo nano /etc/ufw/before.rules
Add the following lines at the beginning of the file, before other rules:
*nat
:POSTROUTING ACCEPT [0:0]
-A POSTROUTING -s 192.168.1.0/24 ! -o docker0 -j MASQUERADE
COMMIT
Reload UFW to apply the changes:
sudo ufw reload
Step 6: Use the IP Range in Docker Compose Files
You can now use this IP range. Define a network in the Docker Compose file and add networks to each Docker container. Here is an example of Docker Compose file with network definitions:
version: '3.8'
services:
nginx:
image: nginx
ports:
- "80:80"
networks:
<yourNetworkName>:
ipv4_address: 192.168.1.20
#------ Define network ------#
networks:
<yourNetworkName>:
name: <yourNetworkName>
driver: bridge
ipam:
config:
- subnet: 192.168.1.0/24
#------ This is the IP range defined in previous steps ------#
Run the Docker Compose file with:
docker compose up -d
Or specify a custom Docker Compose file name:
docker compose -f <docker-compose-custom-name> up -d
You can now log in to your container:
docker exec -it -u0 <container-ID> bash
and try to use
apt update
and if your container with custom IP connected to internet, you could update your apt package manager or ping some public IP such as 8.8.8.8 , and it shows that your containers are connected to the internet.