iptables_docker

Ansible Роль: iptables для Docker (и Docker Swarm)

Добавьте правила брандмауэра на сервер с помощью iptables для Docker и Docker Swarm. Это действительно защитит ваши контейнеры Docker!
Эта Ansible роль необходима, потому что firewalld и Docker (и Docker Swarm) не ладят друг с другом.

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

Сценарий использования данного решения: Позволить доверенным IP-адресам подключаться к контейнерам Docker (и контейнерам Docker Swarm), наряду с другими открытыми портами ОС. С опцией открывать указанные порты для публичного доступа (Docker/Docker Swarm и ОС). Эти доверенные IP-адреса могут находиться в другой подсети или даже в другом диапазоне IP-адресов.

Это должно быть просто. Защитите Docker с помощью брандмауэра. Но, к сожалению, это не так. Я старался сделать это как можно проще.

Могут возникнуть неизвестные проблемы с этим.. используйте на свой риск!

Смотрите также: https://ryandaniels.ca/blog/secure-docker-with-iptables-firewall-and-ansible/
И о использовании цепи INPUT в Docker: https://ryandaniels.ca/blog/docker-iptables-input-chain/

В данный момент протестировано и работает на:

  • CentOS/RHEL 7
  • Ubuntu 18.04
  • Ubuntu 20.04

Особенности

  • Работает с Docker и Docker Swarm (также известным как Docker SwarmKit).
  • Безопасно по умолчанию. После настройки только IP-адреса Docker могут получать доступ ко всем контейнерам и всем другим процессам ОС, имеющим открытые порты на сервере.
  • Как можно проще. Чем меньше правил iptables, тем лучше производительность (по крайней мере, теоретически).
  • Автоматически. Нет необходимости вручную добавлять порты в конфигурацию брандмауэра (если вы используете доверенный набор IP-адресов).
  • Добавьте "доверенные" IP-адреса, которые могут общаться со всеми контейнерами Docker и всеми другими процессами ОС, имеющими открытые порты на сервере.
  • Открытые указанные порты контейнеров Docker или порты ОС для общего доступа (всем) через брандмауэр, например, для SSH.
  • Можно также указать интерфейсы. По умолчанию все интерфейсы фильтруются. Вы можете отфильтровать конкретные сетевые интерфейсы и разрешить доступ всем другим интерфейсам (укажите только недоверенный интерфейс).
  • Все выполняется в "офлайн" режиме. Поэтому не должно быть проблем с Docker, когда правила iptables активированы.
  • Вам не нужно быть экспертом в iptables, чтобы использовать это.
  • Работает с незафиксированным использованием iptables в Docker Swarm и зашифрованными наложенными сетями. (правила iptables добавляются в цепь INPUT).

Это решение использует iptables в качестве брандмауэра и ipset, чтобы позволить iptables иметь список допустимых IP-адресов. ipset также позволяет использовать непоследовательный диапазон IP-адресов.

Цепи iptables, которые используются, и их назначение:
INPUT, не очищена. Правило вставлено на верх для перехода к пользовательской цепи для правил, связанных с ОС.
DOCKER-USER, очищена. Все правила, относящиеся к Docker (и Docker Swarm), находятся здесь для блокировки контейнеров от открытия для всех по умолчанию. По умолчанию разрешены только IP-адреса сервера Docker. Другие IP-адреса и порты контейнеров могут быть добавлены пользователем.
FILTERS, очищена. Пользовательская цепь для процессов на сервере (которые не принадлежат Docker). По умолчанию разрешены только IP-адреса сервера Docker. Другие IP-адреса и порты контейнеров могут быть добавлены пользователем.

Руководство по iptables: http://ipset.netfilter.org/iptables.man.html

Предостережения

Не заблокируйте себя на своем сервере. Это изменяет ваш брандмауэр. Всегда имейте другой способ войти, например "консоль".

Примечание об IP-адресах: Это только для IPv4. IPv6 не был протестирован. Лучше отключить IPv6 на ваших серверах.

Другие соображения безопасности:
Если вы используете не Swarm (обычный Docker), подумайте о привязке порта к внутреннему IP для лучшей безопасности.
Если вы используете Swarm, подумайте о использовании конкретных IP-адресов для связи Docker Swarm.
Например: docker swarm init --advertise-addr 192.168.100.100 --listen-addr=192.168.100.100 --data-path-addr=192.168.100.100

Важная заметка: Docker и firewalld не ладят друг с другом. В этой Ansible роли включена проверка, которая завершает выполнение роли, если служба firewalld запущена или включена.
Для получения дополнительной информации о firewalld и Docker:
https://success.docker.com/article/why-am-i-having-network-problems-after-firewalld-is-restarted
https://www.tripwire.com/state-of-security/devops/psa-beware-exposing-ports-docker/
https://docs.docker.com/network/iptables/

Ошибка SELinux:
В настоящее время существует ошибка с SELinux, из-за которой нельзя сохранить правила iptables в файл iptables.save.
Воздействие: Вторичная попытка сохранения правил iptables завершится неудачей.
Добавлено обходное решение, чтобы SELinux разрешил chmod взаимодействовать с файлом iptables.save.
В качестве альтернативы, вы могли бы отключить SELinux, но это не рекомендуется.
Отчет об ошибке: https://bugs.centos.org/view.php?id=12648
Смотрите ниже для получения дополнительной информации о ручном выполнении обходного решения.

ПРЕДУПРЕЖДЕНИЕ:
Убедитесь, что вы сначала протестировали в непроизводственной среде, я не могу дать никаких гарантий и не несу ответственность.
Будьте осторожны, это удалит и добавит правила iptables в ОС. Используйте с осторожностью.
Существующие правила iptables могут быть удалены! Подтвердите, что вы настроили перед запуском этого.

Могут возникнуть неизвестные проблемы с этим.. используйте на свой риск!

Версии Docker, протестированные

Docker Engine - версия Community Edition:

  • 19.03.8
  • 19.03.9
  • 19.03.12

Протестировано в обычном режиме Docker и с кластером Docker Swarm из 3 узлов.

Протестированные дистрибутивы

  • CentOS: 7.7, 7.8
  • Ubuntu 18.04
  • Ubuntu 20.04

Зависимости

  • iptables & iptables-services

Протестировано с v1.4.21 (последняя доступная версия в CentOS 7)

  • ipset & ipset-service

Протестировано с v7.1 (последняя доступная версия в CentOS 7)

Настройки по умолчанию

  • Включить отладку
debug_enabled_default: false
  • Прокси (необходим, когда устанавливаете необходимые пакеты, если за прокси)
proxy_env: []
  • Роль отключена по умолчанию. Измените на true в group_vars или playbook и т.д.
iptables_docker_managed: false
  • Проверить, работает ли (Docker) служба или включена, и завершить выполнение роли
iptables_docker_check_problem_service_managed: true
  • Службы для проверки и завершения выполнения роли
iptables_docker_check_problem_service:
  - docker.service
  • Показать конфигурацию из переменных
iptables_docker_show_config: true
  • Запустить службу iptables
iptables_docker_start: true
  • Установить пакет iptables
iptables_docker_managed_pkg: true
iptables_docker_packages:
  - iptables
  - iptables-services
  - ipset
  - ipset-service
  - policycoreutils-python # требуется для semodule
  • Принудительно копировать файл ipset, чтобы вызвать перезагрузку ipset
iptables_docker_copy_ipset_force: false
  • Принудительно копировать файл iptables, чтобы вызвать перезагрузку iptables
iptables_docker_copy_iptables_force: false
  • Местоположение сохраненной конфигурации iptables
iptables_docker_iptables_config_save: /etc/sysconfig/iptables
  • Местоположение сохраненной конфигурации ipset
iptables_docker_ipset_config_dir: /etc/sysconfig/ipset.d
  • Максимальное количество элементов ipset (IP-адресов в списке разрешенных)

    Если изменено после первого создания, необходимо удалить и создать вручную. 64к IP-адресов должно быть достаточно.

iptables_docker_ipset_maxelem: 65536

Настройки пользователя

  • Переопределить IP-адреса сервера Docker (необязательно)

    При желании укажите IP-адреса сервера Docker. Если не указано, IP-адреса будут определены из группы docker_hosts в инвентаре Ansible.

# iptables_docker_server_ip_allow_set:
#   - 192.168.100.100
#   - 192.168.100.101
#   - 192.168.100.102
  • IP-адреса, разрешенные использовать все открытые порты контейнеров Docker и все открытые порты процессов сервера.
# iptables_docker_ip_allow_set: []
iptables_docker_ip_allow_set:
  - 192.168.100.1
  - 192.168.101.0/24
  - 192.168.102.0/24
  • Сетевой адаптер, для которого следует ограничить правила ОС

    Будут заблокированы только перечисленные адаптеры. Остальным будет разрешен проход. По умолчанию блокирует все (с '+').
    Если вы хотите ограничить только конкретный сетевой интерфейс, используйте точное имя.
    Если вы хотите ограничить все интерфейсы одного типа, используйте "interface+", чтобы соответствовать каждому интерфейсу, так как + является подстановочным знаком для iptables.
    Например, чтобы ограничить интерфейсы ethX, используйте "eth+". "eth+" является подстановочным знаком для всего, что начинается с eth.
    НЕ используйте "*". Это не подстановочный знак и не соответствует ничему!
    Чем меньше адаптеров, тем лучше. Безопаснее блокировать все ('+'), но если не удается, добавьте сетевые адаптеры с высоким трафиком в первую очередь.
    Локальный (lo) здесь не нужен.

iptables_docker_external_network_adapter:
  - "+" #Подстановочный знак для всего
  # - "eth+"
  # - "enp0s+"
  # - "wlp1s+"
  • Открытые порты TCP ОС для общего доступа

    Порты, чтобы позволить всем подключиться (будут общедоступны). Порты здесь позволят весь TCP-трафик на эти порты на уровне iptables.
    Только для портов на ОС, не для контейнеров Docker.

iptables_docker_global_ports_allow_tcp:
  - 22                   # SSH
  • Открытые порты UDP ОС для общего доступа

    Порты, чтобы позволить всем подключиться (будут общедоступны). Порты здесь позволят весь UDP-трафик на эти порты на уровне iptables.
    Только для портов на ОС, не для контейнеров Docker.

iptables_docker_global_ports_allow_udp: []
  • Сетевой адаптер, для которого следует ограничить правила Docker

    По умолчанию используется тот же набор, что и сетевой адаптер для ОС.

iptables_docker_swarm_network_adapter: "{{ iptables_docker_external_network_adapter }}"
# iptables_docker_swarm_network_adapter:
#   - "+" #Подстановочный знак для всего
#   # - "eth+"
  • Открытые порты TCP контейнеров Docker для общего доступа

    Добавьте TCP-порты контейнеров Docker, которые вы хотите открыть для всех. Для Docker и Docker Swarm. Порты Docker Swarm здесь не нужны.

iptables_docker_swarm_ports_allow_tcp: []
# iptables_docker_swarm_ports_allow_tcp:
#   - 9000
  • Открытые порты UDP контейнеров Docker для общего доступа

    Добавьте UDP-порты контейнеров Docker, которые вы хотите открыть для всех. Для Docker и Docker Swarm. Порты Docker Swarm здесь не нужны.

iptables_docker_swarm_ports_allow_udp: []
  • Имя сети моста Docker (docker0) и диапазон IP (для разрешения источника iptables DOCKER-USER)
iptables_docker_bridge_name: docker0
iptables_docker_bridge_ips: 172.17.0.0/16
  • Диапазон IP моста Docker Swarm (docker_gwbridge) (для разрешения источника iptables DOCKER-USER)
iptables_docker_swarm_bridge_name: docker_gwbridge
iptables_docker_swarm_bridge_ips: 172.18.0.0/16

Пример конфигурационного файла (inventories/dev-env/group_vars/all.yml)

Из приведенного ниже примера:
IP-адреса будут добавлены в список доверенных:

  • 192.168.100.1
  • 192.168.101.0/24

Все сетевые интерфейсы будут ограничены, так как используется подстановочный знак '+' для iptables_docker_external_network_adapter.

Порт 22 будет открыт для общего доступа.

---
iptables_docker_ip_allow_set:
  - 192.168.100.1
  - 192.168.101.0/24

iptables_docker_external_network_adapter:
  - "+" #Подстановочный знак для всего

iptables_docker_global_ports_allow_tcp:
  - 22                   # SSH

Пример файла инвентаря

[docker_hosts]
centoslead1 ansible_host=192.168.100.100
centoswork1 ansible_host=192.168.100.101
centoswork2 ansible_host=192.168.100.102

Пример playbook iptables_docker.yml

---
- hosts: '{{ inventory }}'
  become: yes
  vars:
    # Используйте эту роль
    iptables_docker_managed: true
  roles:
  - ryandaniels.iptables_docker

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

Перед запуском убедитесь, что вы уже используете iptables! Ничего не должно быть перезаписано/удалено, если вы не используете те же цепи iptables, что и в данной роли.

По умолчанию задачи не будут выполнены, если вы не установите iptables_docker_managed=true. Это сделано специально, чтобы предотвратить случайные ошибки у людей, которые не читают документацию.

ansible-playbook iptables_docker.yml --extra-vars "inventory=centos7 iptables_docker_managed=true" -i hosts-dev

Пропустить установку пакетов (если они уже известны - ускоряет выполнение)

ansible-playbook iptables_docker.yml --extra-vars "inventory=centos7 iptables_docker_managed=true" -i hosts --skip-tags=iptables_docker_pkg_install

Показать более подробную информацию (отладочные данные)

ansible-playbook iptables_docker.yml --extra-vars "inventory=centos7 iptables_docker_managed=true debug_enabled_default=true" -i hosts-dev

Не запускать службу iptables или добавлять правила для iptables

ansible-playbook iptables_docker.yml --extra-vars "inventory=centos7 iptables_docker_managed=true iptables_docker_start=false" -i hosts-dev

Принудительно обновить ipset и iptables

ansible-playbook iptables_docker.yml --extra-vars "inventory=centos7 iptables_docker_managed=true iptables_docker_copy_ipset_force=true iptables_docker_copy_iptables_force=true" -i hosts-dev

Показать только конфигурацию (из переменных)

ansible-playbook iptables_docker.yml --extra-vars "inventory=centos7 iptables_docker_managed=true iptables_docker_show_config=true" -i hosts --tags "iptables_docker_show_config"

Заметка о лимите размера ipset

Важно: Обратите внимание на размер "Количество записей". Если это число близко к размеру maxelem (65536), то вам нужно удалить ipset "ip_allow" и создать его заново с большим максимальным размером.
64К должно быть достаточно для любого.

Файл находится в: templates/ip_allow.set.j2

create -exist ip_allow hash:ip family inet hashsize 1024 maxelem 65536

Проверьте размер списка ipset:

ipset list |grep "Количество записей"

Важный вывод:

Количество записей: 3

Ручной обход ошибки SELinux для iptables и chmod

Подробности об ошибке: https://bugs.centos.org/view.php?id=12648

Проблема заключается в том, что при повторном сохранении iptables, SELinux блокирует это, поскольку chmod сталкивается с проблемой с файлом iptables.save.
Используйте ниже приведенное решение, чтобы разрешить chmod изменить файл iptables.save, если вы не используете Ansible роль.
Чтобы воспроизвести, перезапустите службу iptables после установки конфигурации iptables, чтобы сохранить ее после перезапуска/остановки,

yum install audit policycoreutils policycoreutils-python
ausearch -m AVC,USER_AVC,SELINUX_ERR,USER_SELINUX_ERR -i|tail -55
ausearch -c 'chmod' --raw | audit2allow -M iptables_save_chmod
#или grep "iptables.save" /var/log/audit/audit.log|tail | audit2allow -M iptables_save_chmod
semodule -i iptables_save_chmod.pp

Справочник команд iptables

Больше команд можно найти в документации по iptables: http://ipset.netfilter.org/iptables.man.html

Список активных команд iptables:

iptables -nvL --line-numbers

Полезные команды для CentOS/RHEL:

cat /etc/sysconfig/ipset.d/ip_allow.set
systemctl restart ipset
ipset list | head

iptables -F DOCKER-USER
iptables -F FILTERS
iptables-restore -n < ansible_iptables_docker-iptables

grep -v "^#" ansible_iptables_docker-iptables
iptables -S INPUT
iptables -S DOCKER-USER
iptables -S FILTERS

Полезные команды для Ubuntu:

vi /etc/iptables/ipsets
#Вручную добавьте 'flush' перед добавлением, если вы удаляете IP-адреса вручную.

sudo /usr/sbin/netfilter-persistent reload

cat /etc/iptables/ipsets

cat /etc/iptables/rules.v4

Ручные команды (CentOS/RHEL)

Проверьте, какие правила iptables у вас уже есть. Сделайте заметки на случай их утери!

iptables -nvL --line-numbers

Установите необходимые пакеты:

yum install iptables iptables-services ipset ipset-service

При использовании SELinux также установите:

yum install policycoreutils-python

Настройте ipset с вашими IP-адресами сервера и другими доверенными IP-адресами:

mkdir -p /etc/sysconfig/ipset.d
cat > /etc/sysconfig/ipset.d/ip_allow.set  << 'EOF'
create -exist ip_allow hash:ip family inet hashsize 1024 maxelem 65536
add ip_allow 192.168.1.123
add ip_allow 192.168.101.0/24
add ip_allow 192.168.102.0/24
EOF

Запустите и включите службу ipset:

systemctl status ipset
systemctl start ipset
systemctl enable ipset

Посмотрите, что в загруженной конфигурации ipset:

ipset list | head

Правила iptables, которые добавляются (по умолчанию порт 22 открыт для всех):

cat > ansible_iptables_docker-iptables << 'EOF'
*filter
:DOCKER-USER - [0:0]
:FILTERS - [0:0]
# Нельзя очищать INPUT. очистка удаляет зашифрованные надстройки Docker Swarm
#-F INPUT
# Используйте ansible или выполните вручную один раз, чтобы добавить -I INPUT -j FILTERS
#-I INPUT -j FILTERS
-A DOCKER-USER -m state --state RELATED,ESTABLISHED -j RETURN
-A DOCKER-USER -i docker_gwbridge -j RETURN
-A DOCKER-USER -s 172.18.0.0/16 -j RETURN
-A DOCKER-USER -i docker0 -j RETURN
-A DOCKER-USER -s 172.17.0.0/16 -j RETURN
# Ниже указанные порты Docker открыты для всех, если не закомментированы
#-A DOCKER-USER -p tcp -m tcp -m multiport --dports 8000,8001 -j RETURN
#-A DOCKER-USER -p udp -m udp -m multiport --dports 9000,9001 -j RETURN
-A DOCKER-USER -m set ! --match-set ip_allow src -j DROP
-A DOCKER-USER -j RETURN
-F FILTERS
# Потому что зашифрованная сеть Docker Swarm просто добавляет правила в INPUT. К сожалению, она должна быть вверху
-A FILTERS -p udp -m policy --dir in --pol ipsec -m udp --dport 4789 -m set --match-set ip_allow src -j RETURN
-A FILTERS -m state --state RELATED,ESTABLISHED -j ACCEPT
-A FILTERS -p icmp -j ACCEPT
-A FILTERS -i lo -j ACCEPT
# Ниже указанные порты ОС открыты для всех, если не закомментированы
-A FILTERS -p tcp -m state --state NEW -m tcp -m multiport --dports 22 -j ACCEPT
#-A FILTERS -p udp -m udp -m multiport --dports 53,123 -j ACCEPT
-A FILTERS -m set ! --match-set ip_allow src -j DROP
-A FILTERS -j RETURN
COMMIT

EOF

Используйте iptables-restore, чтобы добавить вышеуказанные правила в iptables. Очень важно установить флаг -n. Это гарантирует, что мы не очистим правила iptables, если у нас уже есть правила в Docker (или Docker Swarm).

iptables-restore -n < ansible_iptables_docker-iptables

Затем добавьте правило в цепь INPUT, чтобы начать использовать новые правила в FILTERS. Оно должно быть вверху и нужно добавлять только один раз:

iptables -I INPUT 1 -j FILTERS

Сохраните правила iptables:

/usr/libexec/iptables/iptables.init save

Запустите и включите службу iptables:

systemctl status iptables
systemctl start iptables
systemctl enable iptables

Если вы хотите настроить правила iptables для разрешения большего количества портов для общего доступа, просто добавьте порт в соответствующее правило в файле iptables (tcp или udp), а затем повторите те же команды, что и выше:

iptables-restore -n < ansible_iptables_docker-iptables
/usr/libexec/iptables/iptables.init save

Не забывайте о Предупреждениях выше! Особенно об SELinux.

Ручные команды (Ubuntu 20.04)

Проверьте, какие правила iptables у вас уже есть. Сделайте заметки на случай их утери!
Ubuntu 18.04 почти такая же. За исключением того, что пакет ipset-persistent отсутствует в Ubuntu 18.04, поэтому пропустите этот пакет и скопируйте файлы из files/ubuntu/iptables-persistent*/plugins/*-ipset в /usr/share/netfilter-persistent/plugins.d/.

iptables -nvL --line-numbers

Установите необходимые пакеты:

apt install iptables iptables-persistent netfilter-persistent ipset ipset-persistent

Настройте ipset с вашими IP-адресами сервера и другими доверенными IP-адресами:

mkdir -p /etc/iptables
cat > /etc/iptables/ipsets  << 'EOF'
create -exist ip_allow hash:ip family inet hashsize 1024 maxelem 65536
flush
add ip_allow 192.168.1.123
add ip_allow 192.168.101.0/24
add ip_allow 192.168.102.0/24
EOF

Перезагрузите ipset:

/usr/sbin/netfilter-persistent reload

Посмотрите, что в загруженной конфигурации ipset:

ipset list | head

Правила iptables, которые добавляются (по умолчанию порт 22 открыт для всех):

Повторите ту же команду, что и для CentOS/RHEL.

Используйте iptables-restore, чтобы добавить вышеуказанные правила в iptables. Очень важно установить флаг -n. Это гарантирует, что мы не очистим правила iptables, если у нас уже есть правила в Docker (или Docker Swarm).

iptables-restore -n < ansible_iptables_docker-iptables

Затем добавьте правило в цепь INPUT, чтобы начать использовать новые правила в FILTERS. Оно должно быть вверху и нужно добавлять только один раз:

iptables -I INPUT 1 -j FILTERS

Сохраните правила iptables:

/usr/sbin/netfilter-persistent save

Запустите и включите службу iptables:

systemctl status netfilter-persistent
systemctl start netfilter-persistent
systemctl enable netfilter-persistent

Если вы хотите настроить правила iptables для разрешения большего количества портов для общего доступа, просто добавьте порт в соответствующее правило в файле iptables (tcp или udp), а затем повторите те же команды, что и выше:

iptables-restore -n < ansible_iptables_docker-iptables
/usr/sbin/netfilter-persistent save

Не забывайте о Предупреждениях выше!

ЗАДАЧИ

  • Проверить наличие firewalld и завершить, если запущен или включен
  • Проблема с сохранением правил Docker в iptables? Должно быть в порядке.
  • iptables_docker_ip_allow_set не может быть пустым. Если это так, в этом нет смысла, так как ничего не заблокировано!
  • Добавить проверку в сетевых адаптерах для * и ошибку
  • Добавить автоматический список IP-адресов Docker в разрешенный список (использует IP-адреса из группы инвентаря docker_hosts)
  • Изменить доверенные IP-адреса сервера Docker для переопределения
  • Подтвердить, что "when" и "tags" в порядке
  • Ubuntu? Ubuntu не имеет iptables-services или ipset-service. имеет iptables-persistent и ipset-? Нет поддержки ufw
  • IPv6?? Это только для ipv4
  • протестировать работу TCP, UDP портов контейнера и OS
  • протестировать исходящий трафик из контейнеров Docker
  • добавить тест? Молекула? Только одноузловой режим Swarm? как протестировать, чтобы подключение не работало из "недоверенного" ip?

Автор

Райан Дэниелс

О проекте

Manage iptables configuration to secure Docker (including Docker Swarm).

Установить
ansible-galaxy install ryandaniels/ansible-role-iptables-docker
Лицензия
mit
Загрузки
2681
Владелец
Ansible all the things