Skip to content

Instantly share code, notes, and snippets.

@deploy595
Last active June 8, 2025 06:53
Show Gist options
  • Save deploy595/205ea7985fbf41fe66ab9a082021ed6a to your computer and use it in GitHub Desktop.
Save deploy595/205ea7985fbf41fe66ab9a082021ed6a to your computer and use it in GitHub Desktop.
Block all outgoing connections from inside a docker container except http(s) and smtp using firewalld (fixin' Hetzner netscan abuse)

Install, start and enable:

  apt install firewalld
  systemctl start firewalld 
  systemctl enable firewalld

Make sure the file /etc/docker/daemon.json does not contain this line. If it does, delete/comment it. If the file is missing, it is OK, you can skip the step:

{
  //...rest content
  "iptables": true // <-- delete this
}

Add rules for the host:

  firewall-cmd --permanent --zone=public --add-port=22/tcp
  firewall-cmd --permanent --zone=public --add-port=80/tcp
  firewall-cmd --permanent --zone=public --add-port=8080/tcp
  firewall-cmd --permanent --zone=public --add-port=8083/tcp
  firewall-cmd --permanent --zone=public --add-port=443/tcp
  firewall-cmd --permanent --zone=public --add-port=8443/tcp

Add chain for docker:

  firewall-cmd --permanent --direct --remove-rules ipv4 filter DOCKER-USER
  firewall-cmd --permanent --direct --add-chain ipv4 filter DOCKER-USER

Allow internal docker communication (Run docker network inspect <your_network> to obtain network submask):

  firewall-cmd --permanent --direct --add-rule ipv4 filter DOCKER-USER 0 -j RETURN -s 172.18.0.0/16 -m comment --comment "allow internal docker communication"

Accept dns requests:

    firewall-cmd --permanent --direct --add-rule ipv4 filter DOCKER-USER 0 -p udp -m udp --sport 53 --dport 1024:65535 -j RETURN -m comment --comment "accept dns requests"
    firewall-cmd --permanent --direct --add-rule ipv4 filter DOCKER-USER 0 -p udp -m udp --dport 53 --sport 1024:65535 -j RETURN -m comment --comment "accept dns requests"
    firewall-cmd --permanent --direct --add-rule ipv4 filter DOCKER-USER 0 -p tcp -m tcp --dport 53 -j ACCEPT -m comment --comment "accept dns requests"

Accept http(s) traffic:

    firewall-cmd --permanent --direct --add-rule ipv4 filter DOCKER-USER 0 -p tcp -m multiport --dports 80,443,8080,8083,8443 -s 0.0.0.0/0 -j ACCEPT -m comment --comment "accept http(s) traffic"
    firewall-cmd --permanent --direct --add-rule ipv4 filter DOCKER-USER 0 -p tcp -m multiport --sports 80,443,8080,8083,8443 -s 0.0.0.0/0 -j ACCEPT -m comment --comment "accept https(s) traffic"

Accept smtp traffic from docker containers. SMTP typically uses port 25 for unencrypted communication, port 587 for TLS/STARTTLS, and port 465 for SMTPS (SMTP over SSL):

firewall-cmd --permanent --direct --add-rule ipv4 filter DOCKER-USER 0 -p tcp -m multiport --dports 25,465,587 -j ACCEPT -m comment --comment "accept smtp traffic from docker containers"
firewall-cmd --permanent --direct --add-rule ipv4 filter DOCKER-USER 0 -p tcp -m multiport --sports 25,465,587 -j ACCEPT -m comment --comment "accept smtp traffic responses"

Accept our remote postgres:

    firewall-cmd --permanent --direct --add-rule ipv4 filter DOCKER-USER 0 -p tcp -s 5.XXX.XXX.149 --sport 5432 -j ACCEPT -m comment --comment "our remote postgres"
    firewall-cmd --permanent --direct --add-rule ipv4 filter DOCKER-USER 0 -p tcp -d 5.XXX.XXX.149 --dport 5432 -j ACCEPT -m comment --comment "our remote postgres"

Enable logging (access via dmesg command or check /var/log/syslog):

  firewall-cmd --permanent --direct --add-rule ipv4 filter DOCKER-USER 0 -m limit --limit 5/min -j LOG --log-prefix "iptables denied: " --log-level 7

Reject all other out traffic:

firewall-cmd --permanent --direct --add-rule ipv4 filter DOCKER-USER 0 -j REJECT -m comment --comment "reject all other out traffic"

Restart:

  firewall-cmd --reload
  systemctl stop docker && systemctl stop firewalld
  systemctl start firewalld && systemctl start docker

Save this file as check_connection.py changing the IP to your own:

# coding=utf-8
import socket

def _connect(target, port):
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    socket.setdefaulttimeout(1)

    result = s.connect_ex((target, port))
    if result == 0:
        print("You can connect to the port: {}".format(port))
    else:
        print("You CANNOT connect to the port: {}".format(port))
    s.close()

ip = '82.196.4.187' # specify another server of yours for checking
_connect(ip, 80)
_connect(ip, 443)
_connect(ip, 22)

Run this file INSIDE docker container:

python check_connection.py 
# or python3 check_connection.py

Expected results:

$ docker exec -it hello-world-app-1 bash
app@56a9eca011ff:~$ python3 check_connection.py 
You can connect to the port: 80
You can connect to the port: 443
You CANNOT connect to the port: 22

Exit container and run the script from the host console, expected result:

root@hello:/var/www/app $ python3 check_connection.py 
You can connect to the port: 80
You can connect to the port: 443
You can connect to the port: 22
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment