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