Я пытаюсь реализовать довольно простой брандмауэр в 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