[[ Ограничение доступа в интернет ]]

Docker

Ограничение доступа в интернет

Ubuntu 20.04
Docker version 20.10.16, build aa7e414
docker-compose version 1.25.0

Вводная информация

Будем рассматривать случай, когда контейнер(ы) поднимаются через docker-compose с сетью по умолчанию типа bridge, т.е. самый стандартный случай.
И ограничения должны быть только для этого(их) контейнера(ов).
Доступ в локальную сеть должен быть сохранен.
Оценим несколько возможных вариантов.
Для примера возьмем некий контейнер с приложением testapp и попробуем закрыть ему доступ к интернету.

docker-compose.yml

version: '2.2'

services:
  testapp:
    image: testapp
    container_name: testapp
    ports:
      - 8080:8080



Вариант: Network internal mode

https://docs.docker.com/engine/reference/commandline/network_create/

По умолчанию сеть создается без ограничений, т.е. internal: false
Проверим с помощью команды inspect

$ docker network ls
NETWORK ID     NAME                 DRIVER    SCOPE
5170de027161   bridge               bridge    local
6097d465b2de   host                 host      local
d3abd08bf3ea   none                 null      local
89c7d3e6ebf3   testapp_default      bridge    local

$ docker inspect testapp_default /
...
        "Name": "testapp_default ",
        "Id": "89c7d3e6ebf353793b74404c478a2471d3361e3184be30ee8bdc397141cbb131",  <----- на заметку, id используется в имени моста br-<Id>
...
        "Internal": false,   <----------------------- вот тут
        "Attachable": true,
        "Ingress": false,
        "ConfigFrom": {
            "Network": ""
        },
...

Сделаем её внутренней, добавим соответствующие параметры в нашу конфигурацию

docker-compose.yml

version: '2.2'

networks:
  default:
    internal: true

services:
  testapp:
    image: testapp
    container_name: testapp
    ports:
      - 8080:8080

Смена сети на internal, это по сути другой набор правил iptables, но об этом чуть ниже.

Применим изменения

docker-compose down
docker-compose up -d

Перед применением новой конфигурации приложение должно быть остановлено, т.к. нельзя будет пересоздать сеть

ERROR: Network "testapp_default" needs to be recreated - internal has changed

Что в результате получили:

  • Доступ в интернет нет
  • Доступ в локальную сеть 192.168.0.0/24 нет
  • Возможности обращаться к приложению по порту 8080 нет. Порт просто не открывается.
  • Доступ к localhost через адрес 172.17.0.1 (docker0) есть

Такой вариант ограничения мне совершенно не подходит.



iptables: DOCKER-USER

https://docs.docker.com/network/iptables/

По умолчанию, Docker сам создает и управляет правилами iptables.
Без них контейнер потеряет какую либо сетевую связность.
Посмотрим кусочек как пример, на тот момент где разрешается выход в сеть:

$ iptables-save 
*filter
:INPUT ACCEPT [3384:8529254]
:FORWARD DROP [0:0]
:OUTPUT ACCEPT [3233:5880536]
:DOCKER - [0:0]
:DOCKER-ISOLATION-STAGE-1 - [0:0]
:DOCKER-ISOLATION-STAGE-2 - [0:0]
:DOCKER-USER - [0:0]
-A FORWARD -j DOCKER-USER                                      <------ пользовательская цепочка
-A FORWARD -j DOCKER-ISOLATION-STAGE-1
-A FORWARD -o br-89c7d3e6ebf3 -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
-A FORWARD -o br-89c7d3e6ebf3 -j DOCKER
-A FORWARD -i br-89c7d3e6ebf3 ! -o br-89c7d3e6ebf3 -j ACCEPT   <------ вот тут разрешает выход пакетов в любой интерфейс в т.ч. eth0 локальную сеть и интернет
-A FORWARD -i br-89c7d3e6ebf3 -o br-89c7d3e6ebf3 -j ACCEPT
-A FORWARD -o docker0 -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
-A FORWARD -o docker0 -j DOCKER
-A FORWARD -i docker0 ! -o docker0 -j ACCEPT
-A FORWARD -i docker0 -o docker0 -j ACCEPT
 
...

Docker размещает свои правила первыми в цепочке и при каждом запуске, поднятии или остановки контейнера следит за этим.
Но разработчики оставили нам возможность вмешиваться через цепочку DOCKER-USER, ей содержимое Docker не будет трогать.
Варианты привязки ограничения:

  • по имени моста br-* (поменяется при пересоздании сети)
  • по ip адресу или подсети (поменяется при пересоздании сети, если явно не указать конфиг сети)
  • по uid, под которым выполняется процесс testapp
    он должен быть уникальный и не совпадать с локальными и другими контейнерами

Фиксируем адресное пространство

docker-compose.yml

version: '2.2'

networks:
  default:
    ipam:
      config:
        - subnet: 172.18.0.0/24

services:
  testapp:
    image: testapp
    container_name: testapp
    ports:
      - 8080:8080

Применим изменения

docker-compose down
docker-compose up -d

Добавляем ограничение и логирование для наглядности что бы посмотреть.

iptables -I DOCKER-USER -s 172.18.0.0/24 -o eth0 '!' -d 192.168.0.0/24 -j REJECT
iptables -I DOCKER-USER -m conntrack --ctstate NEW -j LOG --log-prefix "DOCKER-USER: "

Обратите внимание, что правила в цепочку в данном случае вставляются через -I
т.к. должны быть вставлены до RETURN

-A DOCKER-USER -j RETURN
это правило создал сам Docker вместе с цепочкой DOCKER-USER.
А если создать DOCKER-USER до запуска Docker'a, то он добавить его в конец.

kernel: [164310.641078] DOCKER-USER: IN=br-d26212ba35b2 OUT=eth0 PHYSIN=veth17a3e13 MAC=02:42:4e:f1:f6:bd:02:42:ac:12:00:02:08:00 SRC=172.18.0.2 DST=87.250.250.242 LEN=60 TOS=0x00 PREC=0x00 TTL=63 ID=44462 DF PROTO=TCP SPT=40998 DPT=80 WINDOW=64240 RES=0x00 SYN URGP=0 

Что в результате получили:

  • Доступ в интернет нет
  • Доступ в локальную сеть 192.168.0.0/24 есть
  • Возможности обращаться к приложению по порту 8080 есть
  • Доступ к localhost через адрес 172.17.0.1 (docker0) есть

Вот это то, что было мне нужно.

Вот эти команды или сохраненные правила через iptables-restore нужно добавить в автозагрузку до запуска Docker'a

iptables -N DOCKER-USER
iptables -F DOCKER-USER
iptables -A DOCKER-USER -s 172.18.0.0/24 -o eth0 '!' -d 192.168.0.0/24 -j REJECT
Автозагрузка правил iptables на Ubuntu Server 20.04



iptables: false

Выключить автоматическое управление правилами iptables и все прописывать руками.

/etc/docker/daemon.json

{
  "iptables": false
}



Примечание

Входящие пакеты на порт 8080 гипотетического приложения не проходят через цепочку INPUT

kernel: [ 3154.917601] DOCKER-USER: IN=eth0 OUT=br-81494b6a085f MAC=24:b6:fd:14:58:f4:12:0d:7f:4c:d0:9c:08:00 SRC=192.168.0.1 DST=172.18.0.2 LEN=60 TOS=0x00 PREC=0x00 TTL=63 ID=62257 DF PROTO=TCP SPT=46034 DPT=8080 WINDOW=29200 RES=0x00 SYN URGP=0 

Поэтому, когда поднимаете приложение в Docker'e, то будьте бдительны.
Т.к. его порт будет «торчать наружу».
Даже если у вас включен firewall и политика DROP в INPUT - это не помешает.
Запрещать надо в FORWARD в цепочке DOCKER-USER
Или делайте bind на localhost

    ports:
      - 127.0.0.1:8080:8080






Обсуждение

Ваш комментарий. Вики-синтаксис разрешён:
71 +10 =
 
howto/docker/docker_restrict_internet_access.txt · Последнее изменение: 2022/09/12 15:37 — lexa
Gentoo Linux Gentoo Linux Driven by DokuWiki