Какова лучшая практика docker + ufw под Ubuntu



Я только что попробовал Докера. Это потрясающе, но, кажется, не очень хорошо работает с ufw. По умолчанию docker будет немного манипулировать iptables. Результат не ошибка, но и не то, что я ожидал.
Для получения более подробной информации вы можете прочитать опасности UFW + Docker



Моя цель-создать систему, подобную



    Host (running ufw) -> docker container 1 - nginx (as a reverse proxy)
-> docker container 2 - node web 1
-> docker container 3 - node web 2
-> .......


Я хочу управлять входящим трафиком (например, ограничить доступ) через ufw, поэтому я не хочу, чтобы docker прикасался к моим iptables. Вот мой тест



Окружающая среда:




  • недавно установленная Ubuntu 14.04 (ядро: 3.13.0-53)

  • Докер 1.6.2

  • включена переадресация ufw.(включить переадресацию UFW )


  • --iptables=false был добавлен в демон Docker.


Первая Попытка



docker run --name ghost -v /home/xxxx/ghost_content:/var/lib/ghost -d ghost
docker run --name nginx -p 80:80 -v /home/xxxx/nginx_site_enable:/etc/nginx/conf.d:ro --link ghost:ghost -d nginx


Не повезло. Первая команда хороша, но вторая команда выдаст ошибку



Error response from daemon: Cannot start container


Вторая Попытка



Затем я обнаружил следующее: неспособен чтобы связать контейнеры с --iptables=false #12701



После выполнения следующей команды все выглядит нормально.



sudo iptables -N DOCKER


Однако я заметил, что не могу установить исходящие соединения внутри контейнеров. Например:

xxxxg@ubuntu:~$ sudo docker exec -t -i nginx /bin/bash
root@b0d33f22d3f4:/# ping 74.125.21.147
PING 74.125.21.147 (74.125.21.147): 56 data bytes
^C--- 74.125.21.147 ping statistics ---
35 packets transmitted, 0 packets received, 100% packet loss
root@b0d33f22d3f4:/#


Если я удалю --iptables=false из демона Docker, то подключение контейнеров к интернету вернется в нормальное состояние, но ufw не будет работать "должным образом" (well...by мое определение).



Итак, какова лучшая практика docker + ufw? Мочь кто-нибудь может помочь?



Спасибо.



Барт

896   4  

4 ответов:

У меня была такая проблема несколько месяцев назад, и недавно я решил описать эту проблему вместе с решением в своем блоге. Вот кратчайший путь.

Использование --iptables=false не очень поможет вам в описанном случае. Здесь этого просто недостаточно. По умолчанию ни один из ваших контейнеров не может выполнять исходящее соединение.

Есть небольшой шаг, который вы пропускаете на своем пути, чтобы иметь контейнеры позади UFW здесь. Вы можете использовать --iptables=false или создать /etc/docker/daemon.json файл с содержимым следующим образом
{
  "iptables": false
}

В результат будет тот же, но последний вариант требует перезапуска всей службы docker с помощью service docker restart или даже перезагрузки, если docker имел возможность добавить правила iptables до того, как вы отключили эту функцию.

Когда это будет сделано, просто сделайте еще две вещи:

$ sed -i -e 's/DEFAULT_FORWARD_POLICY="DROP"/DEFAULT_FORWARD_POLICY="ACCEPT"/g' /etc/default/ufw
$ ufw reload

Таким образом, вы устанавливаете политику переадресации по умолчанию в UFW для accept и используете:

$ iptables -t nat -A POSTROUTING ! -o docker0 -s 172.17.0.0/16 -j MASQUERADE

Таким образом, вы добиваетесь того, что отключаете Docker грязное поведение в ваших правилах iptables, и в то же время docker снабжен необходимая маршрутизация, так что контейнеры будут делать исходящие соединения просто отлично. Однако с этого момента правила UFW будут по-прежнему ограничены.

Надеюсь, что это решит проблему для вас и всех, кто приходит сюда в поисках ответа.

Я более подробно описал проблему и ее решение в https://www.mkubaczyk.com/2017/09/05/force-docker-not-bypass-ufw-rules-ubuntu-16-04/

Проблема

Эта проблема была вокруг в течение долгого времени.

Отключение iptables в Docker приведет к другим проблемам.

Откат меняется первым

Если вы изменили свой сервер в соответствии с текущим решением, которое мы находим в интернете, пожалуйста, сначала откатите эти изменения, включая:

  • включите функцию iptables Docker. Удалите все изменения, такие как --iptables=false , включая файл конфигурации /etc/docker/daemon.json.
  • прямое правило UFW по умолчанию возвращается к значению по умолчанию DROP вместо ACCEPT.
  • удалить правила, относящиеся к сети настройки в файле конфигурации сооружения /etc/ufw/after.rules.
  • если вы изменили файлы конфигурации Docker, сначала перезапустите Docker. Мы изменим конфигурацию UFW позже,и тогда мы сможем перезапустить его.

Решение проблем UFW и Docker

Это решение должно изменить только один файл конфигурации UFW, все конфигурации и параметры Docker остаются по умолчанию. Не необходимо отключить функцию docker iptables.

Измените файл конфигурации UFW /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 -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

С помощью команды sudo systemctl restart ufw перезапустить UFW после изменения файла. Теперь публичная сеть не может получить доступ к каким-либо опубликованным портам docker, контейнер и частная сеть могут регулярно посещать друг друга, а контейнеры могут также получать доступ к внешней сети изнутри.

Если вы хотите разрешить публичным сетям доступ к услугам, предоставляемым контейнером Docker, например, порт обслуживания контейнера 80. Выполните следующую команду, чтобы разрешить сетям общего пользования доступ к этой службе:

ufw route allow proto tcp from any to any port 80

Эта команда позволяет публичной сети получить доступ ко всем опубликованным портам, контейнерный порт которых равен 80.

Примечание: если мы публикуем порт с помощью опции -p 8080:80, мы должны использовать контейнерный порт 80, а не Порт хоста 8080.

Если имеется несколько контейнеров со службой порт 80, но мы хотим, чтобы внешняя сеть имела доступ только к определенному контейнеру. Например, если частный адрес контейнера равен 172.17.0.2, используйте следующую команду:

ufw route allow proto tcp from any to 172.17.0.2 port 80

Если сетевой протокол службы является UDP, например, службой DNS, можно использовать следующую команду, чтобы разрешить внешней сети доступ ко всем опубликованным службам DNS:

ufw route allow proto udp from any to any port 53

Аналогично, если только для конкретного контейнера, такого как IP-адрес 172.17.0.2:

ufw route allow proto udp from any to 172.17.0.2 port 53

Как это работает?

Следующие правила позволяют частным сетям иметь возможность посещать друг друга. Как правило, частные сети более надежны, чем публичные.
-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

Следующие правила позволяют UFW управлять тем, разрешено ли сетям общего пользования посещать службы, предоставляемые контейнером Docker. Так что мы можем управлять всеми правилами брандмауэра в одном месте.

-A DOCKER-USER -j ufw-user-forward

Следующие правила блокируют запросы на подключение, инициируемые всеми общедоступными сетями, но разрешают внутренние сети для доступа к внешним сетям. Для протокола TCP он препятствует активному установлению TCP-соединения из общедоступных сетей. Для протокола UDP все доступы к портам, которые меньше 32767, блокируются. Почему это портвейн? Поскольку протокол UDP не имеет состояния, невозможно заблокировать сигнал рукопожатия, который инициирует запрос на соединение, как это делает TCP. Для GNU/Linux мы можем найти диапазон локальных портов в файле /proc/sys/net/ipv4/ip_local_port_range. Диапазон по умолчанию - 32768 60999. При обращении к протоколу UDP служба из запущенного контейнера, локальный порт будет случайным образом выбран один из диапазона портов, и сервер вернет данные в этот случайный порт. Поэтому мы можем предположить, что порт прослушивания протокола UDP внутри всех контейнеров меньше 32768. Это является причиной того, что мы не хотим, чтобы публичные сети имели доступ к портам UDP, которые меньше, чем 32768.

-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

Еще

Https://github.com/chaifeng/ufw-docker

sudo wget -O /usr/local/bin/ufw-docker https://github.com/chaifeng/ufw-docker/raw/master/ufw-docker
chmod +x /usr/local/bin/ufw-docker

Использование

ufw-docker help
ufw-docker install
ufw-docker status
ufw-docker allow webapp
ufw-docker allow webapp 80
ufw-docker allow webapp 53/udp
ufw-docker list webapp
ufw-docker delete allow webapp 80/tcp
ufw-docker delete allow webapp

Обновление: 2018-09-10

Причина выбора ufw-user-forward, а не ufw-user-input

Используя ufw-user-input

Pro:

Проста в использовании и понимании, поддерживает более старые версии Ubuntu.

Например, чтобы разрешить публике посещать опубликованный порт, контейнерным портом которого является 8080, используйте команда:

ufw allow 8080

Кон:

Он не только открывает порты контейнеров, но и открывает порты хоста.

Например, если служба запущена на хосте, а порт 8080. Команда ufw allow 8080 позволяет публичной сети посетить сервис и все опубликованные порты, порт контейнеров которых 8080. Но мы просто хотим представить службу, работающую на хосте, или просто службу, работающую внутри контейнеров, а не то и другое.

Чтобы избежать этой проблемы, мы можем необходимо использовать команду, аналогичную следующей для всех контейнеров:

ufw allow proto tcp from any to 172.16.0.3 port 8080

Используя ufw-user-forward

Pro:

Не может предоставлять службы, работающие на хостах и контейнерах одновременно с помощью одной и той же команды.

Например, если мы хотим опубликовать порт 8080 контейнеров, используйте следующую команду:

ufw route allow 8080
Публичная сеть может получить доступ ко всем опубликованным портам, контейнерными портами которых являются 8080.

Но порт 8080 хоста все еще не является доступ осуществляется по общедоступной сети. Если мы хотим сделать это, выполните следующую команду, чтобы разрешить публичный доступ к порту на хосте отдельно:

ufw allow 8080

Кон:

Не поддерживает старые версии Ubuntu, и команда немного сложнее. Но вы можете использовать мой сценарий https://github.com/chaifeng/ufw-docker .

Заключение

Если мы используем более старую версию Ubuntu, мы можем использовать цепочку ufw-user-input. Но будьте осторожны, чтобы избежать разоблачения услуг, которые не следует подвергаться воздействию.

Если мы используем более новую версию Ubuntu, которая поддерживает субкоманду ufw route, нам лучше использовать цепочку ufw-user-forward и использовать команду ufw route для управления правилами брандмауэра для контейнеров.

Обновление: 6 Октября 2018

Скрипт ufw-docker теперь поддерживает Docker Swarm. Пожалуйста, смотрите последний код для получения дополнительной информации, https://github.com/chaifeng/ufw-docker

Установка для режима Docker Swarm

Мы можем использовать этот скрипт только в менеджере узлы для управления правилами брандмауэра при использовании в режиме Роя.

  • изменение всех after.rules файлов на всех узлах, включая менеджеров и работников
  • развертывание этого скрипта на узлах диспетчера

Запущенный в режиме Docker Swarm, этот скрипт добавит глобальную службу ufw-docker-agent. Образchaifeng/ufw-docker-agent также автоматически создается из этого проекта.

Для чего это стоит, вот дополнение к ответу @mkubaczyk для случая, когда во всей установке задействовано больше мостовых сетей. Они могут быть предоставлены проектами Docker-Compose, и вот как могут быть сгенерированы соответствующие правила, учитывая, что эти проекты управляются systemd.

/etc/systemd/system/[email protected]

[Unit]
Description=Docker-Compose project: %I
After=docker.service
BindsTo=docker.service
AssertPathIsDirectory=/<projects_path>/%I
AssertFileNotEmpty=/<projects_path>/%I/docker-compose.yml

[Service]
Type=simple
Restart=always
WorkingDirectory=/<projects_path>/%I
ExecStartPre=/usr/bin/docker-compose up --no-start --remove-orphans
ExecStartPre=+/usr/local/bin/update-iptables-for-docker-bridges
ExecStart=/usr/bin/docker-compose up
ExecStop=/usr/bin/docker-compose stop --timeout 30
TimeoutStopSec=30
User=<…>
StandardOutput=null

[Install]
WantedBy=multi-user.target

/usr/local/bin/update-iptables-for-docker-bridges

#!/bin/sh

for network in $(docker network ls --filter 'driver=bridge' --quiet); do
  iface=$(docker network inspect --format '{{index .Options "com.docker.network.bridge.name"}}' ${network})
  [ -z $iface ] && iface="br-${network}"
  subnet=$(docker network inspect --format '{{range .IPAM.Config}}{{.Subnet}}{{end}}' ${network})
  rule="! --out-interface ${iface} --source ${subnet} --jump MASQUERADE"
  iptables --table nat --check POSTROUTING ${rule} || iptables --table nat --append POSTROUTING ${rule}
done
Очевидно, что это не будет масштабироваться так хорошо.

Также примечательно, что вся основная концепция будет маскировать источник любого подключение для приложений, работающих в контейнере.

Не совсем уверен, что вы спрашиваете, но из того, что я могу собрать, Вы хотели бы лучше контролировать, кто может получить доступ к вашим приложениям, запущенным внутри Docker? Я ответил на аналогичный вопрос здесь, чтобы управлять трафиком через прокси-сервер переднего плана, а не с помощью таблиц IP блокировать внешний доступ к контейнерам docker

Надеюсь, это поможет

Дилан

Edit

При вышеуказанном подходе вы можете использовать UFW только для разрешения входящих подключений к порту 80 (т. е. прокси). Это держит любое воздействие порта к минимуму с дополнительным бонусом, что вы можете контролировать трафик через конфигурацию прокси & DNS

Comments

    Ничего не найдено.