84

I have a situation to restrict internet access of the container in load balancer network. for example in that below picture

easy for your reference

Only container4 connects to the Internet; other three only communicate through container4 with the outside world. For example if container1 needs smtp support, it will forward smtp request to container4 to get access.

No container other than container4 should be allowed to access the Internet directly! This should be enforced on Docker level.

I believe it will be configurable on docker network creation, can any one explain how to achieve this?

knaperek
  • 2,113
  • 24
  • 39
Bilal Usean
  • 2,322
  • 3
  • 22
  • 45

5 Answers5

99

As found here, I got this to work with docker-compose. Save as docker-compose.yml:

version: '3'

services:
  outgoing-wont-work:
    image: alpine
    networks:
      - no-internet
    command: ping -c 3 google.com # will crash

  internal-will-work:
    image: alpine
    networks:
      - no-internet
    command: ping -c 3 internal-and-external

  internal-and-external:
    image: alpine
    networks:
      - no-internet
      - internet
    command: ping -c 3 google.com

networks:
  no-internet:
    driver: bridge
    internal: true
  internet:
    driver: bridge

Then run docker-compose up -d, docker-compose ps will show something like this after a few seconds:

              Name                            Command               State    Ports
----------------------------------------------------------------------------------
dco_inet_internal-and-external_1   ping -c 3 google.com             Exit 0        
dco_inet_internal-will-work_1      ping -c 3 internal-and-ext ...   Exit 0        
dco_inet_outgoing-wont-work_1      ping -c 3 google.com             Exit 1      
exic
  • 2,220
  • 1
  • 22
  • 29
  • 1
    I wish this worked for me. We're running Traefik as a reverse proxy in front of the application containers and Squid as a proxy for outgoing HTTP(S) requests _from_ the application container to the internet. I tried your solution but the app container still has access to the internet, I think through the network it shares with Traefik. – Tobias Sep 02 '21 at 16:52
43

Network creation for access internet

docker network create --subnet=172.19.0.0/16 internet

Network creation for block internet access

docker network create --internal --subnet 10.1.1.0/24 no-internet

If you want to connect docker container into internet

docker network connect internet container-name

If you want to block internet access

docker network connect no-internet container-name

Note

in internal network we can't expose ports to connect outside world, please refer this question for more details

Community
  • 1
  • 1
Bilal Usean
  • 2,322
  • 3
  • 22
  • 45
5

Another option, if you need to expose ports on a container without internet access, but want to let it talk to other containers would be to provide a bogus DNS configuration. This isn't a perfect solution though, since it doesn't prevent direct IP access to the outside world.

docker-compose.yaml

version: '3'

services:
  service1:
    image: alpine
    command: sh -c 'ping service2 -c 1; ping google.com -c 1'
    dns: 0.0.0.0
  service2:
    image: alpine
    command: sh -c 'ping service1 -c 1; ping google.com -c 1'
    dns: 0.0.0.0
isolated> docker-compose up
Recreating isolated_service1_1 ... done                                                                                 Recreating isolated_service2_1 ... done                                                                                 Attaching to isolated_service2_1, isolated_service1_1
service1_1  | PING service2 (172.18.0.2) 56(84) bytes of data.
service1_1  | 64 bytes from isolated_service2_1.isolated_default (172.18.0.2): icmp_seq=1 ttl=64 time=0.038 ms
service1_1  |
service1_1  | --- service2 ping statistics ---
service1_1  | 1 packets transmitted, 1 received, 0% packet loss, time 0ms
service1_1  | rtt min/avg/max/mdev = 0.038/0.038/0.038/0.000 ms
service2_1  | PING service1 (172.18.0.3) 56(84) bytes of data.
service2_1  | 64 bytes from isolated_service1_1.isolated_default (172.18.0.3): icmp_seq=1 ttl=64 time=0.093 ms
service2_1  |
service2_1  | --- service1 ping statistics ---
service2_1  | 1 packets transmitted, 1 received, 0% packet loss, time 0ms
service2_1  | rtt min/avg/max/mdev = 0.093/0.093/0.093/0.000 ms
service1_1  | ping: google.com: Temporary failure in name resolution
service2_1  | ping: google.com: Temporary failure in name resolution
isolated_service1_1 exited with code 2
isolated_service2_1 exited with code 2
Keegan
  • 11,345
  • 1
  • 25
  • 38
  • It isn't perfect but yes it might mitigate most of internet access attempts from softwares. – secavfr May 08 '20 at 13:43
  • 3
    I would like to disagree it dose not restrict anything and therefor so far from perfect... – Lenard Jul 18 '20 at 22:01
  • This works well for requests that require DNS resolution and is a good workaround for cases where the network based restriction isn't feasible. Our setup involves a Traefik instance in front of the application container and in that case, the other solutions don't seem to work. – Tobias Sep 02 '21 at 16:50
5

As stated in Bilal's answer, the internal network is a good solution if you do not need to expose the ports.

If you do need to expose the ports, the below solution using iptables does the job for my requirements:

docker network create --subnet 172.19.0.0/16 no-internet
sudo iptables --insert DOCKER-USER -s 172.19.0.0/16 -j REJECT --reject-with icmp-port-unreachable
sudo iptables --insert DOCKER-USER -s 172.19.0.0/16 -m state --state RELATED,ESTABLISHED -j RETURN

Then add

--network no-internet

when you run your docker container. For instance:

$ docker run -it --network no-internet ubuntu:focal /bin/bash
root@9f2181f79985:/# apt update
Err:1 http://archive.ubuntu.com/ubuntu focal InRelease
  Temporary failure resolving 'archive.ubuntu.com'
beledouxdenis
  • 171
  • 2
  • 4
  • Do you know how to do this on windows host machine? – dkregen Dec 11 '21 at 04:03
  • 1
    I can confirm this works, so thank you! 2 questions: 1) Why does the REJECT come before the RELATED,ESTABLISHED line? Doesn't this order mean that REJECT takes precedence and renders latter rule useless? 2) Why icmp-port-unreachable for the reject reason? – John Lee Mar 03 '22 at 15:43
2

For blocking outgoing (internet) while exposing ports to the internal LAN network

--internal or internal:true does not allow exposing ports to the internal network.

  1. backup your current rules
sudo iptables-save > iptables.backup
  1. (optional) flush your existing rules under DOCKER-USER
sudo iptables -F DOCKER-USER
  1. add these rules in this order (assuming the docker IP range is 10.0.1.0/24)
sudo iptables -A DOCKER-USER -s 10.0.1.0/24 -d 192.168.0.0/16 -j RETURN
sudo iptables -A DOCKER-USER -s 10.0.1.0/24 -d 172.16.0.0/12 -j RETURN
sudo iptables -A DOCKER-USER -s 10.0.1.0/24 -d 10.0.0.0/8 -j RETURN
sudo iptables -A DOCKER-USER -s 10.0.1.0/24 -j REJECT --reject-with icmp-port-unreachable
sudo iptables -A DOCKER-USER -j RETURN
  1. sudo iptables --list --line-numbers should show:
Chain DOCKER-USER (1 references)
num  target     prot opt source               destination
1    RETURN     all  --  10.0.1.0/24          192.168.0.0/16
2    RETURN     all  --  10.0.1.0/24          172.16.0.0/12
3    RETURN     all  --  10.0.1.0/24          10.0.0.0/8
4    REJECT     all  --  10.0.1.0/24          anywhere             reject-with icmp-port-unreachable
5    RETURN     all  --  anywhere             anywhere

docker-compose.yml:

networks:
  no_internet:
    driver: bridge
    ipam:
      config:
        - subnet: 10.0.1.0/24

services:
  some-service:
    image: some-image
    networks:
      - no_internet
    ports:
      - '123:123'
mastrchee
  • 21
  • 2