У меня есть новое приложение, которое я создал с помощью файла docker-compose. Этот файл содержит 2 сети:
version: '2.1'
# ----------------------------------
# Services
# ----------------------------------
services:
application:
image: tianon/true
volumes:
- ${APPLICATION_PATH}:/var/www
nginx:
build:
context: ./docker/nginx
volumes_from:
- application
volumes:
- ${DOCKER_STORAGE}/nginx-logs:/var/log/nginx
- ${NGINX_SITES_PATH}:/etc/nginx/sites-available
ports:
- "${NGINX_HTTP_PORT}:80"
- "${NGINX_HTTPS_PORT}:443"
networks:
- frontend
- backend
redis:
build:
context: ./docker/redis
volumes:
- ${DOCKER_STORAGE}/redis:/data
ports:
- "${REDIS_PORT}:6379"
networks:
- backend
# ----------------------------------
# Networks
# ----------------------------------
networks:
frontend:
driver: "bridge"
backend:
driver: "bridge"
# ----------------------------------
# Volumes
# ----------------------------------
volumes:
redis:
driver: "local"
Вы заметите, что у меня здесь 2 сети, frontend
и backend
. Вопрос простой:
frontend
миру, но позвольте backend
сервисы общаться друг с другом, не открывая их миру? (Я предполагаю использовать iptables или указываю в докере, какую сеть я хочу предоставить хосту)За щедрость: В докере явно есть способ определить несколько оверлейных сетей. Я предполагал, что будет просто открыть одну сеть для внешнего мира. Я пытаюсь найти простой способ сделать это в docker или iptables. Текущий ответ дает мне некоторое направление, но требует ручных правил для каждого порта. Я хотел бы получить правильный ответ о том, как защитить мою "внешнюю" сеть, без необходимости указывать конкретные порты в iptables.
Хорошо, если вы хотите, чтобы ваш redis был доступен только с определенных хостов, вам нужно использовать iptables.
iptables -I FORWARD -i public_iface -p tcp --dport 6379 -j DROP
iptables -I FORWARD -i public_iface -s allowed_ip -p tcp --dport 6379 -j ACCEPT
Поскольку я не знаю, есть ли у вас что-нибудь в iptables, я решил вставить правила вверху.
Параметр -I вставляет правило в начало цепочки, поэтому сначала вставьте правило, чтобы запретить весь трафик для redis, затем вставьте другое правило перед этим, чтобы разрешить трафик с определенного хоста.
Здесь мы используем цепочку FORWARD, а не цепочку INPUT, потому что трафик не предназначен напрямую для хоста докеров.
Вы можете проверить результат с помощью
iptables -L
И вы, возможно, захотите узнать, как сделать защиту от перезагрузки правил межсетевого экрана.
Чтобы завершить ответ @BMitch по Вашему запросу ..
Вы можете сделать это с помощью дополнительной межконтейнерной сети (или нескольких, если вы видите необходимость или использование в этом ...), потому что для такой настройки для надежной работы важно, чтобы каждому контейнеру была назначена только одна не внутренняя сеть. В противном случае вы не можете быть действительно уверены, какая мостовая сеть будет выбрана для входящего трафика на сопоставленном порту (или, по крайней мере, это не действительно настраивается).
Пример составного файла, который показывает такую настройку:
version: '3.6'
services:
c1:
container_name: c1
image: "centos:latest"
entrypoint: /bin/bash -c "while sleep 10; do sleep 10; done"
ports:
- "5000:5000"
networks:
- front
- inter
c2:
container_name: c2
image: "centos:latest"
entrypoint: /bin/bash -c "while sleep 10; do sleep 10; done"
ports:
- "5001:5001"
networks:
- inter
c3:
container_name: c3
image: "centos:latest"
entrypoint: /bin/bash -c "while sleep 10; do sleep 10; done"
ports:
- "5002:5002"
networks:
- back
- inter
c4:
container_name: c4
image: "centos:latest"
entrypoint: /bin/bash -c "while sleep 10; do sleep 10; done"
ports:
- "5003:5003"
networks:
- back
- inter
networks:
front:
name: front
driver: bridge
driver_opts:
com.docker.network.bridge.name: dockerfront
back:
name: back
driver: bridge
driver_opts:
com.docker.network.bridge.name: dockerback
inter:
name: inter
driver: bridge
internal: true
driver_opts:
com.docker.network.bridge.name: dockerinter
Он создает один мост ([докер] спереди) для ваших общедоступных сервисов, один мост ([докер] сзади) для ваших внутренних сервисов и один внутренний мост (который не публикует порты даже при запросе и, таким образом, может быть безопасно добавлен в качестве дополнительной сети) ([docker] intern) и назначьте их контейнерам.
Фактически c2 не будет доступен для порта 5001 извне, поэтому порты для этого контейнера можно не указывать. Просто чтобы показать результат:
# iptables -nvL DOCKER -t nat
Chain DOCKER (2 references)
pkts bytes target prot opt in out source destination
0 0 RETURN all -- dockerback * 0.0.0.0/0 0.0.0.0/0
0 0 RETURN all -- dockerfront * 0.0.0.0/0 0.0.0.0/0
0 0 RETURN all -- docker0 * 0.0.0.0/0 0.0.0.0/0
0 0 DNAT tcp -- !dockerback * 0.0.0.0/0 0.0.0.0/0 tcp dpt:5002 to:172.27.0.2:5002
0 0 DNAT tcp -- !dockerfront * 0.0.0.0/0 0.0.0.0/0 tcp dpt:5000 to:172.25.0.2:5000
0 0 DNAT tcp -- !dockerback * 0.0.0.0/0 0.0.0.0/0 tcp dpt:5003 to:172.27.0.3:5003
Теперь вы можете добавить правило доступа для dockerback
мост к DOCKER-USER
цепочка:
# iptables -I DOCKER-USER ! -s 10.0.0.0/24 -o dockerback -j DROP
# iptables -nvL DOCKER-USER
Chain DOCKER-USER (1 references)
pkts bytes target prot opt in out source destination
3 156 DROP all -- * dockerback !10.0.0.0/24 0.0.0.0/0
10 460 RETURN all -- * * 0.0.0.0/0 0.0.0.0/0
Будьте осторожны: в текущей версии 19.03.3 есть ошибка, которая DOCKER-USER
цепочка не создается, она уже исправлена на 19.03.4, которая скоро станет доступна. Если у вас есть эта версия, вы можете добавить цепочку самостоятельно (это нужно делать после каждого перезапуска демона докера):
iptables -N DOCKER-USER
iptables -I FORWARD -j DOCKER-USER
iptables -A DOCKER-USER -j RETURN
Возможно, вы найдете более подходящее место для таких правил, но докер предлагает такой вариант. (Я мог бы подумать о фиксации сетей для мостов и использовать правило в общем правиле переадресации, не применимое к манипуляциям с докерами при перезапуске, воссоздании интерфейса и т. Д.)
В дополнение (или вместо этого) вы можете использовать дополнительный IP-адрес на своем компьютере, чтобы привязать серверную службу к другому IP-адресу, а затем интерфейсные службы и фильтровать на этом уровне. Кроме того, означает ту же настройку докера, что и выше, но с дополнительным указанием com.docker.network.bridge.host_binding_ipv4
driver-option (который является просто IP по умолчанию для опубликованных портов) - или вместо этого используйте только одну сеть и укажите адрес привязки в соответствии с типом службы. Затем заблокируйте FORWARD в DOCKER-USER или, где это возможно, с помощью ctorigdst модулей conntrack, соответствующих входящему интерфейсу.
Docker не предоставляет никакой сети внешнему миру, сети докеров используются для того, чтобы контейнеры могли взаимодействовать друг с другом. EXPOSE
также не изменяет связь с внешним миром или даже между контейнерами, это просто метаданные, которые чаще всего используются в качестве документации между создателем образа и лицом, развертывающим контейнеры.
Что действительно позволяет общаться с внешним миром, так это публикация порта, которая создает порт, перенаправляющий из пространства имен сети хоста докера в порт прослушивания контейнера. Следовательно, для достижения своей цели вы должны публиковать только те порты, которые должны быть доступны извне. А затем используйте сети докеров для управления связью между контейнерами.
Если вы хотите как опубликовать порт, так и ограничить доступ к нему с помощью iptables, используйте Сеть ДОКЕР-ПОЛЬЗОВАТЕЛЬ - рекомендуемый метод. Обратите внимание: недавно я видел несколько выпусков, в которых это поведение исправлялось, поэтому следите за примечаниями к выпуску и открытыми проблемами, если вы идете в этом направлении. Также обратите внимание, что цепочка запускается после того, как пакет обрабатывается для контейнера, и поэтому имеет целевой порт контейнера, а не целевой порт хоста. Для фильтрации по порту хоста вам необходимо использовать conntrack, например:
iptables -I DOCKER-USER -i eth0 -s 10.0.0.0/24 -p tcp \
-m conntrack --ctorigdstport 8080 -j ACCEPT
iptables -I DOCKER-USER -i eth0 ! -s 10.0.0.0/24 -p tcp \
-m conntrack --ctorigdstport 8080 -j DROP
Просто удалите часть «порт» в вашем контейнере Redis, его единственная функция - открыть его миру. Поскольку вы настроили свои 2 контейнера для работы в бэкэнд-сети, они все равно смогут обмениваться данными.