Unable to append rules in iptable in docker - docker

I am trying to add a rule which looks like:
Chain DOCKER (2 references)
target prot opt source destination
ACCEPT tcp -- anywhere 172.18.0.8 tcp dpt:21102
I am using the following command:
sudo iptables -A DOCKER -t tcp -s anywhere -d 172.18.0.8 --dport 21102 -j ACCEPT
However, I am getting the following error:
table 'tcp' does not exist
Perhaps iptables or your kernel needs to be upgraded.
Can someone please guide me where am I going wrong ?

-t : Specifies the packet matching table such as nat, filter, raw.etc
-p : Sets the IP protocol for the rule, which can be either icmp, tcp, udp, or all.
So the command should be:
sudo iptables -A DOCKER -p tcp -s {source_ip} -d 172.18.0.8 --dport 21102 -j ACCEPT

Related

iptables: Access from local machine to docker container is not possible

I have an issue in regards to my iptables setup. My goal is to reach the https based webserver inside a docker container from the server machine itself.
The setup is the following:
The server is connected to the internet via eth0 and serves http via port 443.
Any users from the outside (internet) reach the server via the ip address 1.2.3.4.
It is connected to the internal network via eth1 and serves dhcp, dns and some more services.
Any users from the inside (intranet) reach the server via the ip address 10.0.0.1.
The docker container is connected via docker1 on the server. The later has the ip address 10.8.0.2 inside the docker network.
The docker container serves the webserver on port 1443, but iptables forwards (NAT) requests on port 443 to its address 10.8.0.1 and the destination port 1443.
What is working:
The webserver is perfectly reachable from the internet and the intranet.
The webserver can be reached from the server itself using the address 10.8.0.1:1443.
What is not working:
Any client which is working directly on the server can not reach the docker webserver using https://example.com:443. Using https://10.8.0.1:8443 would work, but fails due to a certificate error. It is not a goal to skip the certificate check as a workaround.
Excerpt of the iptable configuration:
iptables -P INPUT DROP
iptables -P OUTPUT ACCEPT
iptables -P FORWARD DROP
iptables -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
iptables -A OUTPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
iptables -A FORWARD -m state --state ESTABLISHED,RELATED -j ACCEPT
iptables -A FORWARD -i docker1 -o docker1 -j ACCEPT
iptables -A PREROUTING -t nat -p tcp -d 1.2.3.4 --dport 443 -j DNAT --to-destination 10.1.0.1:1443
iptables -A FORWARD -o docker1 -p tcp --dport 1443 -j ACCEPT
iptables -A INPUT -i docker1 -j DROP
iptables -A FORWARD -i docker1 -j DROP
Due to that "complicated" setup I am no longer able to understand which of the iptable rules and chains need to be applied to make this work so I am seeking for your help to solve that issue.
Brainstorming about the issue using a simplified model and my understanding of the iptable chains the way of the packages might/should look like this:
Origin is a local application (wget).
The packages go through the OUTPUT table.
The packages go through the POSTROUTING table.
Magic happens...
The packages arrive again in the PREROUTING table.
The packages might go trough INPUT again.
The packages might arrive at the target application (webserver).

Can't delete docker container's default iptables rule

If I type iptables -L there is this line in the output :
Chain DOCKER (1 references)
target prot opt source destination
ACCEPT tcp -- anywhere 172.17.0.2 tcp dpt:http-alt
My container is exposed publicly and I can request a dummy http server from everywhere (tested). I try to remove that rule so only 80 is only exposed inside my server (localhost:80). I tried :
root#ns25252:~# iptables -D DOCKER --destination 172.17.0.2 -p tcp --dport 80 -j ACCEPT
iptables: Bad rule (does a matching rule exist in that chain?).
As the error implies, it can't find the matching rule.. How should I type to remove the line ?
It's usually easier to delete by number, unless there is a chance that the number could change between the time you listed the rules and the time you delete the rule.
Here's how to delete by line number:
# iptables -L --line-numbers
(snip)
Chain DOCKER (2 references)
num target prot opt source destination
1 ACCEPT tcp -- anywhere 172.17.0.2 tcp dpt:http
(snip)
# iptables -D DOCKER 1
Alternatively, you can get the full specification by doing iptables -S. Example:
# iptables -S
(snip)
-A DOCKER -d 172.17.0.2/32 -p tcp -m tcp --dport 80 -j ACCEPT
(snip)
Turn the -A into a -D and use this as the args to iptables to delete the rule:
# iptables -D DOCKER -d 172.17.0.2/32 -p tcp -m tcp --dport 80 -j ACCEPT
NOTE: This answer perplexingly still gets upvotes from time to time. I have no idea what everyone is trying to actually accomplish, I just blindly answered an iptables-related question. If you want to start a Docker container that is not accessible to the outside world, that's an entirely different topic, and this is not an appropriate answer in your case. (Maybe start by not exposing/publishing the port.)
This is a bit old but in case someone else is looking for how to remove docker completely from your iptables rules here's how I did it, also keep in mind this is on debian so your files/paths may differ.
edit your /etc/iptables.up.rules file, back up file then remove everything with docker in it - there may also be a few additional lines with the local docker subnet (mine was 172.17.x and 172.19.x) - remove them all
flush iptables: iptables -P INPUT ACCEPT && iptables -P OUTPUT ACCEPT && iptables -P FORWARD ACCEPT && iptables -F
reload iptables rules: iptables-restore < /etc/iptables.up.rules
verify/check your rules: iptables -L -n (should no longer have any docker chains or rules)
If you have deleted the docker package than just restart iptables service and it will deleted default docker iptables-
systemctl restart iptables.service

Logspout can't connect to papertrail

I can't get logspout to connect to papertrail. I get the following error:
!! lookup logs5.papertrailapp.com on 127.0.0.11:53: read udp 127.0.0.1:46185->127.0.0.11:53: i/o timeout
where 46185 changes every time I run the container. It seems like a DNS error, but nslookup logs5.papertrailapp.com gives the expected output, as does docker run busybox nslookup logs5.papertrailapp.com.
Beyond that, I don't even know how to interpret that error message, let alone address it. Any help debugging this would be hugely appreciated.
My Docker Compose file:
version: '2'
services:
logspout:
image: gliderlabs/logspout
command: "syslog://logs5.papertrailapp.com:12345"
volumes:
- /var/run/docker.sock:/var/run/docker.sock
sleep:
image: benwhitehead/env-loop
Where 12345 is the actual papertrail port. Result is the same whether using syslog:// or syslog-tls://.
From https://docs.docker.com/engine/userguide/networking/configure-dns/:
the docker daemon implements an embedded DNS server which provides built-in service discovery for any container
It looks like your container is unable to connect to this DNS server. If your container is on the default bridge network, it won't reach the embedded DNS server. You can either set --dns to be an outside source or update /etc/resolv.conf. It doesn't sound like a Papertrail issue, at all.
(source)
Docker and iptables got in a fight. So I spun up a new machine, failed to set up iptables, and the problem was solved: no firewall at all to get in the way of Docker's connections!
Just kidding, don't do that. I got a toy database hacked that way.
Fortunately, it's now relatively easy to get iptables and Docker to live in harmony, using the DOCKER_USER iptables chain.
The solution, excerpted from my blog:
Configure Docker with iptables=true, and append to iptables configuration:
iptables -A DOCKER-USER -i eth0 -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
iptables -A DOCKER-USER -i eth0 -p tcp -m tcp --dport 80 -j ACCEPT
iptables -A DOCKER-USER -i eth0 -p tcp -m tcp --dport 443 -j ACCEPT
iptables -A DOCKER-USER -i eth0 -j DROP

Why is this docker image allowed past the firewall?

I'm running a docker container that has the port 9000:9000 binding to the host, but I also have ufw enabled. The only ports I've allowed are 22, 80, 443.
So why is it that I'm able to connect to this container using the host's IP address? Shouldn't port 9000 be blocked by ufw?
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
14417c4f71fb dockerui/dockerui "/dockerui" 2 seconds ago Up 2 seconds 0.0.0.0:9000->9000/tcp docker_ui
root#docker:~# ufw status
Status: active
To Action From
-- ------ ----
22 ALLOW Anywhere
80 ALLOW Anywhere
443 ALLOW Anywhere
22 (v6) ALLOW Anywhere (v6)
80 (v6) ALLOW Anywhere (v6)
443 (v6) ALLOW Anywhere (v6)
Aren't all ports blocked by default when you enable ufw?
Docker silently modifies iptables. You can start the Docker daemon with the --iptables=false option by editing DOCKER_OPTS in /etc/default/docker
Docker modifies iptables and UFW is not aware of this.
This answer describes what is happening and how to fix:
https://stackoverflow.com/a/46266757
Please follow it.
adding "--iptables=false" is not a good solution
as I said here https://stackoverflow.com/a/54486957/1712906
a better solution is to add these lines to /etc/ufw/after.rules
# BEGIN UFW AND DOCKER
*filter
:ufw-user-forward - [0:0]
:DOCKER-USER - [0:0]
-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-user-forward
-A DOCKER-USER -j DROP -p tcp -m tcp --tcp-flags FIN,SYN,RST,ACK SYN -d 192.168.0.0/16
-A DOCKER-USER -j DROP -p tcp -m tcp --tcp-flags FIN,SYN,RST,ACK SYN -d 10.0.0.0/8
-A DOCKER-USER -j DROP -p tcp -m tcp --tcp-flags FIN,SYN,RST,ACK SYN -d 172.16.0.0/12
-A DOCKER-USER -j DROP -p udp -m udp --dport 0:32767 -d 192.168.0.0/16
-A DOCKER-USER -j DROP -p udp -m udp --dport 0:32767 -d 10.0.0.0/8
-A DOCKER-USER -j DROP -p udp -m udp --dport 0:32767 -d 172.16.0.0/12
-A DOCKER-USER -j RETURN
COMMIT
# END UFW AND DOCKER
As mentioned by #kliew, Docker will add its own iptables chain, granting access to all exposed ports and pre-empting any rules that you define using ufw.
If you're not comfortable completely disabling Docker's control over iptables, though, there is another documented way to have your own rules respected. From the same docs:
If you need to add rules which load before Docker’s rules, add them to the DOCKER-USER chain. These rules are applied before any rules Docker creates automatically.
Source: https://docs.docker.com/network/iptables/#add-iptables-policies-before-dockers-rules
This would preclude the use of ufw, though, because ufw only operates within its own iptables chains and doesn't give you control over the DOCKER-USER chain. You'll have to do this using iptables directly.

Docker ignores iptable rules when using "-p <port>:<port>"

Just realized a few days ago that Docker seems to bypass my iptable rules. I am not incredible experienced with Docker nor iptables. Tried a lot of different things the last days. Also saw that there was big change in recent docker versions with a special DOCKER-chain that should allow me to do that. However not sure what I am doing wrong but it never does what I expect it to do.
So what I want is quite simple. I want that it behaves like expected. That if I have an ACCEPT-Rule to go through and if not it gets blocked.
My iptable looked originally like that (so before my many unsuccessful attempts):
*filter
:INPUT ACCEPT [0:0]
:FORWARD ACCEPT [0:0]
:OUTPUT ACCEPT [779:162776]
-A INPUT -i lo -j ACCEPT
-A INPUT -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
-A INPUT -p tcp -m tcp --dport 22 -j ACCEPT
-A INPUT -p tcp -m tcp --dport 80 -j ACCEPT
-A INPUT -s 1.2.3.4 -p tcp -m tcp --dport 123 -j ACCEPT
-A INPUT -j DROP
COMMIT
Hoped that it does exactly what I want. Just allow access to ports 22 and 80 and also allow port 123 from the ip 1.2.3.4. However If I create a container with "-p 123:123" everybody can access it. Can anybody help me and tell me how I have to change the above file?
Thanks!
Docker-Version: 1.6.2
Edit:
Left initially my different tries out to not overcomplicate the question. However adding at least one of them could maybe be helpful.
*nat
:PREROUTING ACCEPT [319:17164]
:INPUT ACCEPT [8:436]
:OUTPUT ACCEPT [0:0]
:POSTROUTING ACCEPT [16:960]
:DOCKER - [0:0]
COMMIT
*filter
:INPUT ACCEPT [0:0]
:FORWARD ACCEPT [0:0]
:OUTPUT ACCEPT [779:162776]
:DOCKER - [0:0]
-A INPUT -i lo -j ACCEPT
-A INPUT -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
-A INPUT -p tcp -m tcp --dport 22 -j ACCEPT
-A INPUT -p tcp -m tcp --dport 80 -j ACCEPT
-A DOCKER -s 1.2.3.4 -p tcp -m tcp --dport 123 -j ACCEPT
-A DOCKER -j DROP
-A INPUT -j DROP
COMMIT
The above kind of works. However get then a lot of other problems. For example do I get problems with container linking, DNS does not work anymore, and so on. So then end up adding a lot of additional rules to fix that issues but I get never to a state where it runs properly. So I guess there most be better and easier solution out there.
Solution:
Ended up doing more or less exactly what larsks said. Just did not add it to the FORWARD chain, I added it to the DOCKER chain instead. The problem with the FORWARD chain is that Docker adds its stuff in there when it restarts in first position. Which results in having my rules getting pushed down and not having any effect. However for the DOCKER chain it seems Docker appends only additional rules so mine stay in effect. So when I save my rules and then restart the server everything still works fine.
So now it looks more or less like that:
*filter
:INPUT ACCEPT [0:0]
:FORWARD ACCEPT [0:0]
:OUTPUT ACCEPT [779:162776]
:DOCKER - [0:0]
# That I can access from IP 1.2.3.4
-A DOCKER -s 1.2.3.4/32 -p tcp -m tcp --dport 123 -j ACCEPT
# That I can access from other Docker containers
-A DOCKER -o docker0 -p tcp -m tcp --dport 123 -j ACCEPT
# Does not allow it for anything else
-A DOCKER -p tcp --dport 123 -j DROP
-A INPUT -i lo -j ACCEPT
-A INPUT -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
-A INPUT -p tcp -m tcp --dport 22 -j ACCEPT
-A INPUT -p tcp -m tcp --dport 80 -j ACCEPT
-A INPUT -j DROP
COMMIT
I'm not an expert on iptables but I know that if you run the container with -p 127.0.0.1:123:123 then the port won't be exposed on all interfaces, just on the loopback.
Your iptables configuration looks a little broken right now, as if you cleared it out at some point without restarting Docker. For example, you have a DOCKER chain available in both the filter and nat tables, but no rules that reference it, so rules placed in that chain will have no affect.
In general, if you want to implement iptables rules that affect your Docker containers they need to go in the FORWARD chain of the filter table. Each container has it's own ip address, which means that your host is simply accepting packets and then FORWARDing them to the container address.
Rules in the INPUT chain are only for packets with a final destination of an address on an interface in the host's global network namespace.
However, I'm not sure that iptables is actually your problem.
If you are trying to expose services in containers such that they are available to other systems, you need to publish those ports using the -p flag to docker run. You can read more about that
in this section of the documentation.
If you want to update your question with a specific example of what you are trying to accomplish I can provide a more targeted answer.
Update
It's true that when you publish a container port using -p it will generally be available to any source ip address. In order to restrict access to a published port you would need to add a new rule to your FORWARD chain. For example, if I start a web server:
docker run --name web -p 80:8080 larsks/mini-httpd
The web server in the container is now available on port 8080 on my host. If I want to block access to this port, I need to insert a rule into the FORWARD chain that blocks access to port 80 on the container ip. So first I need the container ip address:
$ web_ip=$(docker inspect --format '{{ .NetworkSettings.IPAddress }}' web)
$ echo $web_ip
172.17.0.5
The rule I create in the FORWARD chain needs to come before the rules that docker creates, so I will need to specify an explicit position:
iptables -I FORWARD 1 -d $web_ip -p tcp --dport 80 \! -s 192.168.1.10 -j DROP
This would block all traffic from hosts other than 192.168.1.10.
If you want a rule to apply to all containers, rather than a specific container, you can bind it to the docker0 interface rather than a specific ip address:
-A FORWARD -o docker0 -p tcp --dport 80 \! -s 192.168.1.10 -j DROP
This would prohibit access to port 80 on any container.
Ended up doing more or less exactly what larsks said. Just did not add
it to the FORWARD chain, I added it to the DOCKER chain instead.
I've found the same in the docs: https://docs.docker.com/v1.5/articles/networking/#the-world
Docker will not delete or modify any pre-existing rules from the
DOCKER filter chain. This allows the user to create in advance any
rules required to further restrict access to the containers.
Docker's forward rules permit all external source IPs by default. To
allow only a specific IP or network to access the containers, insert a
negated rule at the top of the DOCKER filter chain. For example, to
restrict external access such that only source IP 8.8.8.8 can access
the containers, the following rule could be added:
$ iptables -I DOCKER -i ext_if ! -s 8.8.8.8 -j DROP
To use iptables on published ports from docker containers, you need a combination of things:
DOCKER-USER table: docker uses this table for iptables rules that affect containers and is reserved specifically for user provided rules that won't be overwritten by the docker engine when it restarts.
conntrack: port forwarding can publish on one port and forward to another in the container. You can have multiple containers all listening on port 80 with different published ports on the host.
To use these, the resulting iptables rule looks like:
iptables -I DOCKER-USER -i eth0 -s 10.0.0.0/24 -p tcp \
-m conntrack --ctorigdstport 8080 -j ACCEPT
iptables -I DOCKER-USER -i eth0 ! -s 10.0.0.0/24 -p tcp \
-m conntrack --ctorigdstport 8080 -j DROP
This handles requests to the published port 8080/tcp (that's on the host, the container could be listening on 80 or any other port), and only accepts the requests from the 10.0.0.0/24 subnet. Everything outside of that subnet is dropped.
Note that the DOCKER-USER table has a default rule to immediately return, so all changes should be inserted before that default rule in the table.
Given: Debian stretch, docker 18.06, and a docker process created via
docker run ... -p 5678:1234 ...
Required: Access to the docker container restricted to multiple external subnetworks.
Solution: (using some example subnetworks)
iptables -I DOCKER-USER -p tcp --dport 1234 -j REJECT
iptables -I DOCKER-USER -s 18.204.0.0/16 -p tcp --dport 1234 -j RETURN
iptables -I DOCKER-USER -s 34.192.0.0/16 -p tcp --dport 1234 -j RETURN
iptables -I DOCKER-USER -s 35.153.0.0/16 -p tcp --dport 1234 -j RETURN
iptables -I DOCKER-USER -s 13.56.63.0/24 -p tcp --dport 1234 -j RETURN
Persist the changed rules using iptables-save:
iptables-save > /etc/iptables/rules.v4
Result:
iptables -L DOCKER-USER -n -v --line-numbers
Chain DOCKER-USER (1 references)
pkts bytes target prot opt in out source destination
0 0 RETURN tcp -- * * 13.56.63.0/24 0.0.0.0/0 tcp dpt:1234
0 0 RETURN tcp -- * * 35.153.0.0/16 0.0.0.0/0 tcp dpt:1234
0 0 RETURN tcp -- * * 34.192.0.0/16 0.0.0.0/0 tcp dpt:1234
0 0 RETURN tcp -- * * 18.204.0.0/16 0.0.0.0/0 tcp dpt:1234
0 0 REJECT tcp -- * * 0.0.0.0/0 0.0.0.0/0 tcp dpt:1234 reject-with icmp-port-unreachable
Background 1: Rules
All rules are added to the chain DOCKER-USER as recommended by the current docker documentation.
The first rule targets REJECT and will end up as the last rule, since the other rules are added on top (option -I without a position number corresponds to adding a rule at position 1). All packages with destination port 1234 reaching this rule will be rejected.
The other rules target RETURN, i.e. a package with destination port 1234 and source IP from one of the given subnetworks will be returned to the calling chain, which is the FORWARD chain.
iptables -L FORWARD -n -v
Chain FORWARD (policy ACCEPT 0 packets, 0 bytes)
pkts bytes target prot opt in out source destination
16471 4568K DOCKER-USER all -- * * 0.0.0.0/0 0.0.0.0/0
16413 4565K DOCKER-ISOLATION-STAGE-1 all -- * * 0.0.0.0/0 0.0.0.0/0
7173 2060K ACCEPT all -- * docker0 0.0.0.0/0 0.0.0.0/0 ctstate RELATED,ESTABLISHED
45 2340 DOCKER all -- * docker0 0.0.0.0/0 0.0.0.0/0
From the FORWARD chain, it will be processed by the DOCKER chain, where it is forwarded as desired to the docker container:
iptables -L DOCKER -n -v
Chain DOCKER (1 references)
pkts bytes target prot opt in out source destination
45 2340 ACCEPT tcp -- !docker0 docker0 0.0.0.0/0 172.17.0.2 tcp dpt:1234
Background 2: Port value
We don't use the external port value 5678 in the forward rule, because the destination port is changed via a rule automatically created by docker and applied before the forward chain is executed. See top section of /etc/iptables/rules.v4:
-A PREROUTING -m addrtype --dst-type LOCAL -j DOCKER
...
-A DOCKER ! -i docker0 -p tcp -m tcp --dport 5678 -j DNAT --to-destination 172.17.0.2:1234

Resources