Почему firewalld разрешает публичный трафик к моим непубличным портам, привязанным к контейнерам Docker?

Почему firewalld разрешает публичный трафик к моим непубличным портам, привязанным к контейнерам Docker?

Я пытаюсь реализовать довольно простой брандмауэр в Fedora, где публичный интернет может получить доступ к SSH, HTTP, HTTPS и Cockpit, но больше ни к чему. Между тем, серверы запускают микросервисы через Docker, которые могут общаться друг с другом через порты 8000-8999.

Диаграмма сети

Я настроил это на свежеустановленном Fedora Server с помощью следующих команд:

firewall-cmd --zone=public --add-service=cockpit
firewall-cmd --zone=public --add-service=http
firewall-cmd --zone=public --add-service=https

firewall-cmd --zone=internal --add-source=192.168.1.65
firewall-cmd --zone=internal --add-source=192.168.1.66

firewall-cmd --zone=internal --add-port=8000-8999/tcp

firewall-cmd --runtime-to-permanent

Когда я проверяю свою конфигурацию с помощью --list-all, все выглядит правильно:

> firewall-cmd --list-all --zone=internal
internal (active)
  target: default
  icmp-block-inversion: no
  interfaces:
  sources: 192.168.1.65 192.168.1.66
  services: dhcpv6-client ssh
  ports: 8000-8999/tcp
  protocols:
  forward: no
  masquerade: no
  forward-ports:
  source-ports:
  icmp-blocks:
  rich rules:

> firewall-cmd --list-all --zone=public
public (active)
  target: default
  icmp-block-inversion: no
  interfaces: enp2s0
  sources:
  services: cockpit dhcpv6-client http https ssh
  ports:
  protocols:
  forward: no
  masquerade: no
  forward-ports:
  source-ports:
  icmp-blocks:
  rich rules:

Однако, когда я это тестирую, я могу попастьhttp://192.168.1.65:8080. Я могу сделать это с машины в той же внутренней сети (192.168.128.128), а также сделать публичный запрос с внешней машины. Поскольку ни один из них не был указан в internal' s sources, я предположил, что firewalld не пропустит запросы.

У меня есть автоматически настроенная dockerзона с docker0интерфейсом, но ее удаление, похоже, не влияет на мою возможность доступа к внутреннему порту.

Почему я могу успешно запросить :8080 из источников, которые не указаны на internal?

решение1

Ну, оказывается, проблема в том, что я использую Docker для внутренних портов. Чтобы упростить процесс взаимодействия контейнеров с миром и друг с другом, Docker решил взять на себя большую часть контроля над вашими брандмауэрами/сетями. Это означает, что если вынехотите, чтобы ваши контейнеры были общедоступныИвам необходимо контролировать публичный доступ через брандмауэр на той же машине, где находится ваш демон Docker, вам необходимо настроить брандмауэры немного по-другому.У Docker есть официальная документация о том, как это сделать.. По сути, у вас есть следующие варианты:

  • Настройте отдельную машину только для вашего брандмауэра. Это, вероятно, будет проще всего, поскольку Docker и ваш брандмауэр не должны будут делить ресурсы.
  • Добавьте свои iptablesправила в DOCKER-USERцепочку (это скорее ответ для iptablesпользователей; я не уверен, как воспроизвести firewalldэтот подход)
  • Отключите все это, настроив iptables=falseконфигурацию службы Docker. (этот пост в блогеобсуждает этот вариант)

Я такженашел постчто я думал, что это хорошая вариация на тему DOCKER-USERцепной опции. По сути, вы создаете iptables.confфайл, который можете загрузить без сброса, используя iptables-restore -n. К сожалению, это iptablesрешение, а не firewalldрешение.

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

решение2

Мне удалось решить эту проблему, воссоздав DOCKER-USERцепочку, и я хотел бы поделиться этим опытом, возможно, он окажется кому-то полезным.

Это основано на решении, которое я нашел на Github (https://github.com/firewalld/firewalld/issues/869) и в блоге какого-то гения (https://roosbertl.blogspot.com/2019/06/securing-docker-ports-with-firewalld.html) но мне пришлось его немного адаптировать/доработать.

# 1. Stop Docker
systemctl stop docker.socket
systemctl stop docker.service

# 2. Recreate DOCKER-USER iptables chain with firewalld. Ignore warnings, do not ignore errors
firewall-cmd --permanent --direct --remove-chain ipv4 filter DOCKER-USER
firewall-cmd --permanent --direct --remove-rules ipv4 filter DOCKER-USER
firewall-cmd --permanent --direct --add-chain ipv4 filter DOCKER-USER

# 3. Add iptables rules to DOCKER-USER chain - unrestricted outbound, restricted inbound to private IPs
firewall-cmd --permanent --direct --add-rule ipv4 filter DOCKER-USER 1 -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT -m comment --comment 'Allow containers to connect to the outside world'
firewall-cmd --permanent --direct --add-rule ipv4 filter DOCKER-USER 1 -j RETURN -s 127.0.0.0/8 -m comment --comment 'allow internal docker communication, loopback addresses'
firewall-cmd --permanent --direct --add-rule ipv4 filter DOCKER-USER 1 -j RETURN -s 172.16.0.0/12 -m comment --comment 'allow internal docker communication, private range'

# 3.1 optional: for wider internal networks
firewall-cmd --permanent --direct --add-rule ipv4 filter DOCKER-USER 1 -j RETURN -s 10.0.0.0/8 -m comment --comment 'allow internal docker communication, private range'
firewall-cmd --permanent --direct --add-rule ipv4 filter DOCKER-USER 1 -j RETURN -s 192.168.0.0/16 -m comment --comment 'allow internal docker communication, private range'

# 4. Block all other IPs. This rule has lowest precedence, so you can add rules before this one later.
firewall-cmd --permanent --direct --add-rule ipv4 filter DOCKER-USER 10 -j REJECT -m comment --comment 'reject all other traffic to DOCKER-USER'

# 5. Activate rules
firewall-cmd --reload

# 6. Start Docker
systemctl start docker.socket
systemctl start docker.service

Связанный контент