I try to accomplish the following thing: From Service B I want to call Service A
Working:
Not working (No feedback from curl, no error):
So I did try to debug this, but curl and traefik dont show logs, where the catch is. CURL is just hanging.
nslookup of sub.example.org => ok
ping of sub.example.org => ok
curl of sub.example.org => hangs not output beside: connecting to ip
Given that DNS and ping
work, this most likely is a firewall issue. DNS probably does not resolve to the private ip of the container that was assigned by docker, but to the host ip. The firewall must allow traffic from the docker bridge network to the host ip on port 443. But when mapping container ports (443:443
) docker will only configure the firewall to allow traffic from outside the bridge network to the private container ip in the bridged network - which is not enough to support the desired use case. So, let's understand firewalls again and fix it! :)
Allow https traffic from Service B
ip/network to the host ip, for example using ufw
:
sudo ufw allow from <service-B-ip or docker-bridge-network> to <host-ip> port 443
You can find the subnet of the docker bridge network using docker network inspect <network-name> | grep Subnet
, e.g. 172.18.0.0/16
. Assuming the host ip is 10.0.0.4
this would allow all services on the bridged network to communicate to 443 (traefik):
sudo ufw allow from 172.18.0.0/16 to 10.0.0.4 port 443
Also make sure ufw
is enabled: sudo ufw enable
.
Firewalls test where a request is going (network interface, target IP, port) and where it comes from (network interface, ip) and some other stuff. So let's check what rules we need to allow the traffic desired in the use case and then check if the firewall is configured accordingly.
Run docker network ls
and you will find a network of type bridge with some id for your network, e.g. cd0af4596aee
. You can also check ifconfig
and will find that this network interface was created by docker once you created the network (br-cd0af4596aee
).
When mapping a port of a container to a host port (e.g. 443:443
for traefik) docker sets some firewall rules. Docker does so by manipulating iptables
on linux based systems, let's check what docker does:
$ sudo iptables -L -v -n | grep 443
pkts bytes target prot opt in out source destination
13 780 ACCEPT tcp -- !br-cd0af4596aee br-cd0af4596aee 0.0.0.0/0 172.18.0.4 tcp dpt:443
The rule allows any source but only via interfaces that are not the bridge interface of the network (!br-cd0af4596aee
) and only to the private ip 172.18.0.4
. But the traffic we want to allow originates from the network br-cd0af4596aee
and targets the host ip to which DNS resolves (which probably is not the private ip of the container that was assigned by docker).
Understanding this, you probably now understand what a solution will look like: We need to allow traffic from the network itself to the host ip. Because it is cumbersome to hassle with the network interfaces, we can also add a rule that just allows all in
and out
interfaces but only for specific ips/networks. For this we can use ufw
which is available on most ubuntu distributions. ufw
is a frontend to iptables
that is much easier to use than iptables
. Note that docker rules in iptables live before ufw
rules - effectively making docker bypassing ufw rules that try to block specific traffic. See here for more information.
Assuming 172.18.0.0/16
is the ip range of the network and 10.0.0.4
the host ip:
# sudo ufw allow from <service-B-ip or network-of-the-service> to <host-ip> port 443
sudo ufw allow from 172.18.0.0/16 to 10.0.0.4 port 443
Check iptables
again:
$ sudo iptables -L -v -n | grep 443
13 780 ACCEPT tcp -- !br-cd0af4596aee br-cd0af4596aee 0.0.0.0/0 172.18.0.4 tcp dpt:443
1 60 ACCEPT tcp -- * * 172.18.0.0/16 0.0.0.0/0 tcp dpt:443
0 0 ACCEPT udp -- * * 172.18.0.0/16 0.0.0.0/0 udp dpt:443
This should fix the issue.
DNS: By default containers have internet access (i.e. the firewall allows containers to communicate with external hosts) and thereby the DNS resolution works fine. DNS is just a request saying "what is the ip of that domain?".
ping: Ping uses the Internet Control Message Protocol (ICMP). Let's check iptables
:
$ iptables -L -v -n | grep icmp
0 0 ACCEPT icmp -- * * 0.0.0.0/0 0.0.0.0/0 icmptype 3
0 0 ACCEPT icmp -- * * 0.0.0.0/0 0.0.0.0/0 icmptype 11
0 0 ACCEPT icmp -- * * 0.0.0.0/0 0.0.0.0/0 icmptype 12
0 0 ACCEPT icmp -- * * 0.0.0.0/0 0.0.0.0/0 icmptype 8
0 0 ACCEPT icmp -- * * 0.0.0.0/0 0.0.0.0/0 icmptype 3
0 0 ACCEPT icmp -- * * 0.0.0.0/0 0.0.0.0/0 icmptype 11
0 0 ACCEPT icmp -- * * 0.0.0.0/0 0.0.0.0/0 icmptype 12
3 252 ACCEPT icmp -- * * 0.0.0.0/0 0.0.0.0/0 icmptype 8
0 0 REJECT all -- * * 0.0.0.0/0 0.0.0.0/0 reject-with icmp-port-unreachable
The firewall is configured to allow icmp (which AFAIK is done by ufw
when it is enabled; if ufw
is disabled the REJECT
rule would also be removed).