-
-
Save tehmoon/b1c3ae5e9a67d66186361d4728bed799 to your computer and use it in GitHub Desktop.
#!/bin/sh | |
set -e | |
## SEE https://medium.com/@ebuschini/iptables-and-docker-95e2496f0b45 | |
## You need to add rules in DOCKER-BLOCK AND INPUT for traffic that does not go to a container. | |
## You only need to add one rule if the traffic goes to the container | |
CWD=$(cd "$(dirname "${0}")"; pwd -P) | |
FILE="${CWD}/$(basename "${0}")" | |
chown root:root "${FILE}" | |
chmod o-rwx "${FILE}" | |
set -x | |
install_docker_block() { | |
## One time install rules for the DOCKER-BLOCK chain | |
/sbin/iptables -t nat -N DOCKER-BLOCK && | |
## Deploy the new rules. After this, everything goes to DOCKER-BLOCK then to RETURN | |
/sbin/iptables -t nat -I PREROUTING -m addrtype --dst-type LOCAL -g DOCKER-BLOCK || | |
true | |
} | |
## install the PREROUTING rules for the DOCKER chain in case docker starts after | |
/sbin/iptables -t nat -N DOCKER || true | |
## Block new connections while we restore the first PREROUTING RULES | |
/sbin/iptables -t nat -I PREROUTING -m addrtype --dst-type LOCAL -m state --state NEW -j RETURN | |
install_docker_block | |
## Delete installed rules, we need to ensure they always are at the top | |
## If rules were already installed, it would mean that the second and third rule | |
## are going to be deleted. We still have the RETURN on top. | |
while true; do | |
/sbin/iptables -t nat -D PREROUTING -m addrtype --dst-type LOCAL -j RETURN || break | |
done | |
while true; do | |
/sbin/iptables -t nat -D PREROUTING -m addrtype --dst-type LOCAL -j DOCKER-BLOCK || break | |
done | |
## Re-deploy the right rules on the top. After this, the flow is restored to DOCKER-BLOCK | |
/sbin/iptables -t nat -I PREROUTING -m addrtype --dst-type LOCAL -g DOCKER-BLOCK | |
## Remove the blocking rule, which should be unreachable after deploy_docker_block anyway | |
while true; do | |
/sbin/iptables -t nat -D PREROUTING -m addrtype --dst-type LOCAL -m state --state NEW -j RETURN || break | |
done | |
## Only let established connections go through while we flush the rules | |
/sbin/iptables -t nat -I PREROUTING -m addrtype --dst-type LOCAL -m state --state ESTABLISHED -j DOCKER | |
## Flush the rules of DOCKER-BLOCK, at this point new connections will be blocked | |
/sbin/iptables -t nat -F DOCKER-BLOCK | |
## Add your new rules below, allowing new connections | |
## Don't forget the NEW and ESTABLISHED states | |
#/sbin/iptables -t nat -A DOCKER-BLOCK -p tcp -m tcp --dport 8080 -m state --state NEW,ESTABLISHED -j DOCKER | |
## Restore the flow | |
## Loop trying to delete the rule in case the script failed above, we don't want to add more than one rule | |
while true; do | |
/sbin/iptables -t nat -D PREROUTING -m addrtype --dst-type LOCAL -m state --state ESTABLISHED -j DOCKER || break | |
done | |
## The INPUT chain is set to drop, then we flush it and reinstall the rules. | |
## Finally we restore the policy on the chain | |
## Remember that those rules don't apply to docker | |
/sbin/iptables -t filter -P INPUT DROP | |
/sbin/iptables -t filter -F INPUT | |
/sbin/iptables -t filter -A INPUT -i lo -j ACCEPT | |
# Add your non docker rules here | |
#/sbin/iptables -t filter -A INPUT -p tcp -m tcp --dport 22 -m state --state NEW,ESTABLISHED -j ACCEPT | |
/sbin/iptables -t filter -A INPUT -m state --state ESTABLISHED -j ACCEPT | |
/sbin/iptables -t filter -A INPUT -j DROP | |
/sbin/iptables -t filter -P INPUT ACCEPT |
I think I got a way.
I can put at the place, where I place the normal stuff: /sbin/iptables -t filter -A INPUT -s 172.16.0.0/12 -p tcp -m tcp --dport 8080 -m state --state NEW,ESTABLISHED -j ACCEPT
to give every docker container with standard ips access.
In the future I will possibly make custom networks and name them and give access via -i interface_name
Right now I dont understand the differences between the docker entries and the normal INPUT ones.
for external access it does not matter where I allow the port, both ways it is accessable from the outside.
The only difference is that only if I use the normal INPUT I can access ports from one container from another through a port bound to the host.
@jonathanmmm Thank you for using this! I just wanted to acknowledge that I saw your comments but I want to take time to read them in order for me to better reply to you. Unfortunately I am under the water at the moment. I'll try my best to help you debug this shortly, if you haven't figured it out sooner.
@jonathanmmm Thank you for using this! I just wanted to acknowledge that I saw your comments but I want to take time to read them in order for me to better reply to you. Unfortunately I am under the water at the moment. I'll try my best to help you debug this shortly, if you haven't figured it out sooner.
Thanks for your help, private stuff or work stuff is more important :-)
I am thinking about not using it, I have not figured a good way to let containers communicate with each other.
I wrote a script, will be posting it, it somehow has problems getting all ports, but should be a start.
https://gist.github.com/jonathanmmm/9a6192ec32588bb691ef6f082e33d7aa
You can include it via source
and use it like this
http_ports=(80, 443)
#interfaces or ips with or without submask like 127.0.0.1/8 that should have access to the above list of ports
mariadb_access_interfaces=(lo, 172.16.0.0/12)
allow_acess_to_docker_tcp_port http_ports http_access_interfaces
vpn_ports=(22)
vpn_access_interfaces=(tun0)
allow_acess_to_docker_tcp_port vpn_ports vpn_access_interfaces
also available as udp function.
right now the functions implement
/sbin/iptables -t filter -A INPUT -i $interface -p tcp -m tcp --dport $port -m state --state NEW,ESTABLISHED -j ACCEPT
which might not be the right one
Has anybody tried to run this script on a docker swarm cluster to handle DOCKER-INGRESS
chain as well? I tried some ideas, but the ingress network got entirely blocked...
@tehmoon really nice, saved my well-being :-)
One question, how do you list rules in the chain DOCKER-BLOCK?
Trying to list rules with iptables, does not work:
$ iptables -L DOCKER-BLOCK
iptables v1.8.7 (nf_tables): chain `DOCKER-BLOCK' in table `filter' is incompatible, use 'nft' tool.
And using 'nft tool' (nftable) only show what iptables -L shows, and not your rules.
Please advise !
Update!
I found a way to display the rules:
$ nft list table nat
Is it possible to use this to block outgoing connections? If i only want established input connections, but not allow the container to establish a new outgoing connections.
@fanuelsen, if you give the container a static IP when building it, like 172.17.0.100, then you might be able to block trafic from it by using:
$ /sbin/iptables -t nat -A DOCKER-BLOCK -s 172.17.0.100 -j DROP
Let me know if it worked for you? :-)
For everyone trying to figure an easier way out:
I switched to using the docker included firewall rules. I only publish ports when needed and like on localhost or 0.0.0.0. I use docker-compose and put the containers into internal networks, which makes the containers have no outside connection at all (no internet, just inside the internal network). That way you can put containers behind a proxy, that has outside access, when it is about network access or connect like a database container to another container.
As far I tried, docker blocks access to containers from outside, if they have no port published.
That's one way to fix it and I would also recommend a proxy or firewall. One could also look into setting up a geofence to limit incoming traffic or a cloud firewall if the containers are hosted with a cloud provider.
To get rid of that libvirt error, my permanent workaround in Debian 11 (as a host) with libvirtd daemon is to block the loading of iptables-related modules:
Create a file in /etc/modprobe.d/nft-only.conf
:
# Source: https://www.gaelanlloyd.com/blog/migrating-debian-buster-from-iptables-to-nftables/
#
blacklist x_tables
blacklist iptable_nat
blacklist iptable_raw
blacklist iptable_mangle
blacklist iptable_filter
blacklist ip_tables
blacklist ipt_MASQUERADE
blacklist ip6table_nat
blacklist ip6table_raw
blacklist ip6table_mangle
blacklist ip6table_filter
blacklist ip6_tables
libvirtd
daemon now starts without any error.
Post-analysis: Apparently, I had iptables
module loaded alongside with many nft-related modules; once iptables
was gone, the pesky error message went away.
I found a more layered solution for my use case to this "issue":
- Layer: is having Cloudflare's firewall to stand in front and route traffic
- Layer: is my cloud providers firewall
- Layer: lastly on the hosts, im using this script
You need to add rules in DOCKER-BLOCK AND INPUT for traffic that does not go to a container.
I only have this in my nat table
-A DOCKER-BLOCK -p tcp -m tcp --dport 80 -m state --state NEW,ESTABLISHED -j DOCKER
But still can access my SSH port.
Edit: Just curious. Why not use the mange or raw table instead?
Hi,
Thanks for your nice script I finally found something that works easily :)
But I have some caviats about source ips:
I have a docker that uses -p 80:80 and I can access it via the internet, but the container itself then filters traffic based on the source ip and then forwards this (proxy server) to another port on an interface locally. As I dont want to hassle with the ips of the containers I use another local interface for that, that has nothing to do with docker.
Problem, with this script the proxy can't reach an exposed port from another container.
I even put /sbin/iptables -t nat -A DOCKER-BLOCK -p tcp -m tcp --dport 8080 -m state --state NEW,ESTABLISHED -j DOCKER (the port that the container should reach) but it didnt work, only after I exposed the port in the second place where we should add lines.
The proxy can't reach/does not get a response from the other container via his exposed port via 8080:80 (in another docker network).
What would be the best way to allow a container to access a port from another container without fixed ips, I thought about iptable through network names?
@tehmoon