Moby: Сеть Docker обходит брандмауэр, нет возможности отключить

Созданный на 14 апр. 2016  ·  114Комментарии  ·  Источник: moby/moby

Вывод docker version :

Client:
 Version:      1.10.3
 API version:  1.22
 Go version:   go1.5.3
 Git commit:   20f81dd
 Built:        Thu Mar 10 15:54:52 2016
 OS/Arch:      linux/amd64

Server:
 Version:      1.10.3
 API version:  1.22
 Go version:   go1.5.3
 Git commit:   20f81dd
 Built:        Thu Mar 10 15:54:52 2016
 OS/Arch:      linux/amd64

Вывод docker info :

Containers: 14
 Running: 5
 Paused: 0
 Stopped: 9
Images: 152
Server Version: 1.10.3
Storage Driver: aufs
 Root Dir: /var/lib/docker/aufs
 Backing Filesystem: extfs
 Dirs: 204
 Dirperm1 Supported: false
Execution Driver: native-0.2
Logging Driver: json-file
Plugins: 
 Volume: local
 Network: bridge null host
Kernel Version: 3.13.0-58-generic
Operating System: Ubuntu 14.04.4 LTS
OSType: linux
Architecture: x86_64
CPUs: 8
Total Memory: 7.793 GiB
Name: brm-pheonix-dev
ID: Y6Z4:6D53:RFOL:Z3CM:P7ZK:H6HL:RLV5:JT73:LZMC:DTBD:7ILK:2RS5
Username: benjamenmeyer
Registry: https://index.docker.io/v1/

Дополнительные сведения о среде (AWS, VirtualBox, физическая и т. Д.):
Rackspace Cloud Server, Ubuntu 14.04, но это не имеет особого значения

Действия по воспроизведению проблемы:

  1. Настройте систему с заблокированным брандмауэром
  2. Создайте набор контейнеров докеров с открытыми портами
  3. Проверьте брандмауэр; docker будет использовать «где угодно» в качестве источника, таким образом, все контейнеры будут доступны публике.

Опишите полученные результаты:
root @ brm-pheonix-dev : ~ / rse # iptables --list ДОКЕР
Цепной DOCKER (1 ссылка)
target prot opt ​​источник назначения
ПРИНЯТЬ tcp - где угодно 172.17.0.2 tcp dpt : 6379

Опишите ожидаемые результаты:
root @ brm-pheonix-dev : ~ / rse # iptables --list ДОКЕР
Цепной DOCKER (1 ссылка)
target prot opt ​​источник назначения
ПРИНЯТЬ tcp - 127.0.0.0/24 172.17.0.2 tcp dpt : 6379
ПРИНЯТЬ tcp - 172.16.0.0/16 172.17.0.2 tcp dpt : 6379

Дополнительная информация, которую вы считаете важной (например, проблема возникает только изредка):

По умолчанию докер изменяет брандмауэр таким образом, чтобы нарушить безопасность - он позволяет всему трафику со всех сетевых устройств получать доступ к открытым портам в контейнерах. Рассмотрим сайт с двумя контейнерами: контейнер A предоставляет доступ к 443 запущенному Nginx, а контейнер B запускает API на порту 8000. Желательно открыть контейнер A для всеобщего использования, но полностью скрыть контейнер B, чтобы он мог общаться только с localhost. (для тестирования пользователем) и сеть докеров (для общения с контейнером A). В целях тестирования также может быть желательно, чтобы контейнер C был базой данных, используемой контейнером B с такими же ограничениями.

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

Наилучшей практикой безопасности было бы по умолчанию ограничить работу сети, как в приведенном выше примере желаемого эффекта, а затем позволить пользователю добавить соответствующий брандмауэр и т. Д. Правила, чтобы переопределить такое поведение, или иметь возможность вернуться к текущему поведению. Я знаю, что по устаревшим причинам это маловероятно, поскольку при обновлении многие вещи ломаются; поэтому, по крайней мере, возможность включить вышеупомянутое, которое можно включить сейчас, было бы хорошим первым шагом, и, возможно, позже, после долгого предупреждения, сделайте это поведением по умолчанию. Предполагая, что поведение по умолчанию является безопасным, наличие функциональности для управления этим (firewall-> enable public port, ip) в yml-файле docker-compose было бы отличным способом наглядно показать, что происходит.

Я нашел параметр --iptables = false, однако я не хочу, чтобы мне приходилось устанавливать все правила самостоятельно. Единственное, против чего я возражаю, - это исходная установка правил.

Хотя я не проверял это, я подозреваю, что все брандмауэры, поддерживаемые докером, будут иметь ту же проблему.

arenetworking versio1.10

Самый полезный комментарий

Проблема, которую раскрывает Бен, реальна и удивительна (плохая комбинация). Многие администраторы, как и я, используют проверенный брандмауэр ufw. Docker выполняет и завершает работу с ufw, и изменение правил iptables происходит таким образом, что 1) ufw неверно сообщает текущий статус правил фильтрации пакетов и 2) предоставляет общедоступные сервисы, казалось бы, частные. Чтобы docker оставался в благосклонности сообщества системных администраторов, необходимо разработать другой подход. Прямо сейчас есть много администраторов, которые, как Бен и я, непреднамеренно открыли порты для более широкого Интернета. Однако, в отличие от меня и Бена, они еще этого не поняли.

Все 114 Комментарий

Примечание: я заметил в https://github.com/docker/docker/blob/master/vendor/src/github.com/docker/libnetwork/iptables/iptables.go, что нет даже существующей опции для установки источника , поэтому он просто использует значения по умолчанию iptables для исходного ip / устройства.

Это не совсем # 14041, поскольку в этом выпуске говорится об открытых портах. Предоставление портов предназначено для того, чтобы сделать их общедоступными, поскольку именно так вы предоставляете услуги внешнему миру. Если вы работаете в среде разработки, вы можете либо отключить доступ к портам извне вашего компьютера с помощью брандмауэра хоста, либо просто не открывать порты и не обращаться к службам напрямую или из других контейнеров в той же сети.

Я бы порекомендовал вам использовать новые сетевые функции докеров для настройки частных сетей для служб, которые вы вообще не хотите открывать, см. Https://docs.docker.com/engine/userguide/networking/

Это то, о чем я подумал в первую очередь; но был немного сбит с толку, потому что _exposing_ порта ( EXPOSE ) на самом деле ничего не делает, но _publishing_ порта ( -p / -P ) фактически раскрывает его на хозяин.

Если вы на самом деле говорите о _публикации_, то это так, как задумано;

В вашем примере контейнеры B и C не должны публиковать свои порты, а контейнер A может связываться с ними через сеть Docker, например, g.

docker network create mynet

docker run -d --net=mynet --name=api api-image
docker run -d --net=mynet --name=db database-image
docker run -d --net=mynet --name=web -p 443:443 nginx

Это публикует только "веб-контейнер" для хоста. Веб-контейнер может получить доступ к контейнерам "API" и "база данных" через их имя (т.е. http: // api : 80 / и db: 3306 (при условии MySQL))

@justincormack, поэтому я не думаю, что использование частной сети решит проблему. В моем случае я использую частную сеть между контейнерами, и они все еще публично доступны, потому что брандмауэр хоста не настроен для ограничения доступа к частной сети.

@thaJeztah проблема по-прежнему сводится к поддержке брандмауэра - в докере нет поддержки брандмауэра, чтобы ограничить его определенной сетью. Вероятно, вы все еще можете получить доступ к этим контейнерам из другой системы, поскольку брандмауэр не будет препятствовать другим системам получить доступ к порту на хосте.

Теперь я запускаю это через docker-compose; однако это не совсем проблема создания докеров, поскольку функциональность libnetwork не имеет возможности _no_ ограничивать сеть в правилах брандмауэра - правила iptables не имеют спецификации источника, поэтому независимо от того, как настраивается сеть, пока вы полагаетесь на докер для создания правила брандмауэра (которые следует использовать, потому что они с большей вероятностью будут правильными), тогда это становится проблемой. Рассмотрим следующее в файле docker-compose.yml:

nginx:
    build: ./docker/nginx/.
    ports:
        - "127.0.0.1:8080:80"
        - "127.0.0.1:443:443"
    environment:
        DESTINATION_HOST: repose
    links:
        - repose
repose:
    build: ./docker/repose/.
    ports:
        - "127.0.0.1:80:8080"
    environment:
        DESTINATION_HOST: phoenix
        DESTINATION_PORT: 8888
    links:
        - phoenix
curryproxy:
    build: ./docker/curryproxy/.
    ports:
        - "127.0.0.1:8081:8081"
    external_links:
        - rse_rse_1
        - rse_rse_2
        - rse_rse_3
phoenix:
    build: .
    ports:
        - '127.0.0.1:88:8888'
    links:
        - curryproxy:curry
    external_links:
        - rse_rse_1:rse
        - rse_rse_2
        - rse_rse_3
        - rse_cache_1:cache
    volumes:
        - .:/home/phoenix

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

Я не уверен, как это соотносится с вашей номенклатурой ... возможно, это часть аспекта "публикации", и возможности публикации должны быть расширены, чтобы делать то, что я говорю.

Если это сделано намеренно, то это плохая модель безопасности, поскольку теперь вы подвергаете всех разработчиков экстремальным рискам при работе в незнакомых сетях (например, в поездках).

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

Тогда я немного запутался, не могли бы вы привести несколько примеров того, к чему вы можете подключиться? Бэкэнд-службы будут (по умолчанию) в сети 172.17.0.0/16 , к которой вы не сможете получить доступ извне, я бы не подумал сначала, потому что у вас не будет маршрута к тому, который определен с внешнего хоста.

Существует потенциальная проблема, если ваш внешний IP-адрес также является частным IP, трафик которого не будет отбрасываться, который направляется во внутренние сети (тогда как он должен быть от общедоступного к частному) - проблема в этом?

@justincormack, поэтому я в первую очередь настраиваю правильное проксирование, чтобы некоторые службы могли быть

firewall:
    external:
        ports:
            - 80
            - 443

Теперь ситуацию можно несколько смягчить, ограничив отображение сети на _host_ значением 127.0.0.1 вместо карты по умолчанию 0.0.0.0. Обратите внимание, что это то, что действительно смягчает его, потому что в противном случае мост будет перенаправлять порт хоста в сеть докеров.

И да, я убедился, что это ограничение работает; однако он по-прежнему оставляет потенциальные уязвимости на месте, а правила брандмауэра не соответствуют тому, что делается на самом деле.

В качестве другого примера, недавно была уязвимость ядра Linux (в настоящий момент возникли проблемы с ее обнаружением), которая была связана с портами, которые были отмечены в IPtables как открытые для использования приложениями, но на самом деле не были подключены к приложению. - например, находясь на локальном хост-порту, но не на общедоступном IP-порту. Это потенциально устанавливает это, и было бы лучше ограничить правила IPtables ожидаемыми сетями, а не оставлять их открытыми для подключения из любого места. Как я уже сказал, по крайней мере есть возможность указать. Вероятно, они исправили эту конкретную проблему, но зачем оставлять такую ​​возможность открытой?

IOW, все дело в безопасности.

@BenjamenMeyer, если вы не хотите, чтобы другие сервисы были доступны, зачем вы вообще публикуете их порты? т.е. "127.0.0.1:8081:8081" не требуется, если доступ к нему осуществляется только через сеть докеров (другие службы подключаются напрямую через сеть докеров)

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

Например, я запускаю среду Jenkins в паре контейнеров. Главный узел "опубликован", но мне нужно создать несколько довольно запутанных правил iptables, чтобы заблокировать его, чтобы только 2 офиса, которые у нас есть, могли получить к нему доступ.

Есть ли способ обойти это в настоящее время в Docker? Или, по крайней мере, рекомендуемая практика? Я видел в документации, как можно ограничить доступ к 1 IP-адресу; но не несколько. Другая проблема заключается в том, что если у вас есть сервер, на котором уже есть конфигурация iptables, вы можете сбросить все правила перед применением своих правил (отсюда запутанные правила, которые мне пришлось установить).

У меня проблема, аналогичная той, о которой сообщил @SeerUK. Когда существующие правила брандмауэра не применяются к опубликованным портам контейнера, происходит резкое нарушение ожидания. Желаемое поведение выглядит следующим образом (по крайней мере, для меня)

  1. Попытка подключения пользователя фильтруется на основе конфигураций INPUT и т. Д.
  2. Затем переадресация трафика происходит как обычно на основе правил FORWARD, добавленных докером.

Есть ли краткий способ добиться этого в iptables, или он не позволяет легко создать такую ​​конструкцию. Я особенно ограничен в своих знаниях iptables, так что потерпите меня. Я только недавно узнал об этом, пытаясь понять, как с ним взаимодействует докер.

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

@thaJeztah Я хочу иметь доступ к нему из локальной системы и легко проверять его. Например, настройка RESTful HTTP API с конечной точкой работоспособности и возможность надежно запускать curl против него с помощью localhost (я должен задокументировать это для других, и наличие IP-адресов, которые меняются, ненадежно ). В большинстве случаев для моей среды разработки я хочу, чтобы контейнеры взаимодействовали друг с другом, но я также хочу иметь доступ к ним с хоста.

В случае @SeerUK возможность установить IP-блок (5.5.0.0/16 - допустимый параметр для исходного адреса в правилах iptables) была бы очень хорошей вещью. IPtables уже имеет возможность выполнять ограничение, но докер не пользуется этим.

@thaJeztah Я "127.0.0.1:8081:8081" чтобы он не попал во внешнюю сеть; Я нашел журналы в своих контейнерах докеров от людей, пытающихся взломать контейнеры через открытые порты.

Моя работа прямо сейчас заключается в том, чтобы отключить контейнеры докеров, прежде чем я уйду на день, потому что я не могу гарантировать, что среда, которую я хочу быть внешней, действительно _is_ внешней, или что среда должным образом ограничена в целях безопасности.

@BenjamenMeyer один из способов сделать это - запустить эти тесты в контейнере, например

docker run --net -it --rm --net=mynetwork healthchecker 

Проблема, которую раскрывает Бен, реальна и удивительна (плохая комбинация). Многие администраторы, как и я, используют проверенный брандмауэр ufw. Docker выполняет и завершает работу с ufw, и изменение правил iptables происходит таким образом, что 1) ufw неверно сообщает текущий статус правил фильтрации пакетов и 2) предоставляет общедоступные сервисы, казалось бы, частные. Чтобы docker оставался в благосклонности сообщества системных администраторов, необходимо разработать другой подход. Прямо сейчас есть много администраторов, которые, как Бен и я, непреднамеренно открыли порты для более широкого Интернета. Однако, в отличие от меня и Бена, они еще этого не поняли.

@thaJeztah, который предполагает, что я делаю это через командную строку, а не использую другой инструмент, где мне нужно только установить IP-адрес.

Например, я работаю над API. У меня есть инструмент, который я могу использовать для работы с этим API в производственной среде, чтобы поддерживать его; для разработки инструмента и API я хочу просто указать инструмент на dockerized API. Инструмент ничего не знает о докере, да и не должен. И я не обязательно хочу помещать инструмент в докер только для того, чтобы использовать его - достаточно указать его на порт, доступный только для локального хоста.

@jcheroske Я согласен, но не знаю, есть ли хорошее решение для этого аспекта. Для этого, вероятно, нужно сделать ufw умнее, чтобы иметь возможность искать и сообщать о правилах, в создании которых он не участвовал. Существует много программного обеспечения, которое может настраивать правила iptables образом, о котором ufw (или AFAIK firewalld и т. Д.) Не знает. На самом деле нет простого решения исправить это.

Тем не менее, было бы неплохо, если бы Docker мог интегрироваться с ними, чтобы выгружать соответствующие файлы конфигурации, чтобы иметь возможность включать / отключать их, или интегрироваться с этими инструментами, чтобы он подключался и выгружал информацию соответствующим образом, однако, учитывая есть лучшие решения, я не думаю, что этот аспект действительно будет решен. Здесь речь идет скорее о простом ограничении объема генерируемых правил iptables , чтобы хотя бы минимизировать потенциальные воздействия, разрешив спецификацию источника (lo, eth0, 127.0.0.0/24 и т. Д.).

Если вы хотите это сделать, использование iptables делает это вполне возможным.

Это урезанный пример того, как вы можете его использовать: https://gist.github.com/SeerUK/b583cc6f048270e0ddc0105e4b36e480

Вы можете видеть, что справа внизу, 1.2.3.4 явно предоставляется доступ к порту 8000 (который предоставляется Docker), а затем все остальное для этого порта отбрасывается. Цепочка PRE_DOCKER вставляется перед цепочкой DOCKER, поэтому она выполняется первой, то есть DROP останавливает заблокированные запросы от попадания в цепочку DOCKER.

Немного раздражает, что Docker не имеет встроенной этой функции, но прямо сейчас ее можно обойти.

Другой альтернативой было бы использование внешнего брандмауэра. Некоторые места, такие как AWS и Scaleway, предлагают такие вещи, как группы безопасности, где вы можете управлять доступом к своим ящикам извне, оттуда каждый порт ведет себя одинаково.

Мне так и не удалось понять, как это работает с UFW. Хотя пока я доволен использованием iptables в качестве решения. Похоже, у меня пока все идет хорошо.

Очевидно, это не лучшее решение, если вы уже создали достаточно сложный набор правил брандмауэра для UFW. Тем не менее, это позволяет легко использовать такие вещи, как iptables-persistent. Вы также можете использовать альтернативные способы разрешения доступа, которые кажутся более «нормальными» в iptables.

@BenjamenMeyer думали ли вы об использовании определяемого пользователем docker network с параметром subnet & ip-range и назначении статического IP-адреса для контейнеров и их использовании для локальной разработки, чтобы вам не приходилось зависеть от виртуальный статический ip, такой как 127.0.0.1? Это позволит избежать необходимости иметь все вместе сопоставление портов для тех контейнеров, которые являются частными для хоста.

docker network create --subnet=30.1.0.0/16 --ip-range=30.1.0.0/24 mynetwork
docker run --net=mynetwork --ip=30.1.1.1 --name=myservice1 xxxx
docker run --net=mynetwork --ip=30.1.1.2 --name=myservice2 yyyy

При такой настройке myservice2 может обращаться к myservice1 по имени myservice1 и нет необходимости даже зависеть от статического IP-адреса. Также хост может свободно подключаться к статическому IP без необходимости отображения портов.

Также в compose 1.7 вы можете указать статический IP-адрес для контейнеров и указать сетевые подсети и диапазоны.

Я нашел простой обходной путь.

1) Отредактируйте / etc / default / docker: DOCKER_OPTS="--iptables=false"

2) Добавить правило ufw: ufw allow to <private_ip> port <port>

Настолько просто, что меня действительно заставляет задуматься, почему опция --iptables=false не используется по умолчанию. Зачем создавать такую ​​ситуацию, когда все, что нужно сделать докеру, - это сказать: «Эй, если вы используете брандмауэр, вам придется пробить в нем дыру!» Что мне не хватает?

https://fralef.me/docker-and-iptables.html
http://blog.viktorpetersson.com/post/101707677489/the-dangers-of-ufw-docker

Я не могу заставить докер перестать изменять iptables для спасения моей жизни. Пробовал обновить / etc / default / docker, но безрезультатно в Ubuntu 16.04

@enzeart Попробуйте /lib/systemd/system/docker.service .

@SeerUK Благослови свою душу

@enzeart для настройки демона, работающего на хосте, который использует systemd, лучше не редактировать сам файл docker.unit, а использовать файл «drop-in». Таким образом, вы не столкнетесь с проблемами при обновлении docker (если есть более новый файл docker.unit). См. Https://docs.docker.com/engine/admin/systemd/#custom -docker-daemon-options для получения дополнительной информации.

Вы также можете использовать файл конфигурации daemon.json, см. Https://docs.docker.com/engine/reference/commandline/daemon/#daemon -configuration-file

@mavenugo Докер-сеть уже есть.

@jcheroske, который работает, но, как я уже отмечал, это будет означать, что _end-user_ (я) затем должен будет убедиться, что все правила iptables были правильными, что не оптимально и не так вероятно, как имея docker делать это автоматически, поэтому эта проблема.

Привет, пожалуйста. Думаю, это тоже проблема. Цепочка контейнеров в Iptables должна располагаться после основных правил и по умолчанию не должна быть доступна миру.

Я действительно хотел бы, чтобы Docker (и docker-compose) имел возможность заносить в белый или черный список IP-адреса, которые могут получить доступ к этому порту.

Возьмем, к примеру:

nginx:
    ports:
      - "8000:8000"
    whitelist:
      - 10.6.20.2

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

@StefanPanait Мне очень нравится эта идея. Он также может работать с синтаксисом, аналогичным томам и спискам доступа / запрета, например:

nginx:
  access:
  - "10.0.1.6:allow"
  - "deny"

Конечно, все равно придется разрешить такие вещи, как межконтейнерная связь.

Межконтейнерная связь

Хотя я думаю, что правильный способ сделать это - разделить сети докеров ... тем не менее, я думаю, что можно сделать что-то вроде:

nginx: access: - "10.0.1.6:allow" - "webapi:allow" - "database:deny" - "deny"
Может быть полезно ... вопрос в том, достаточно ли это полезно, чтобы оправдать реализацию в такой степени? Я не знаю.

В настоящее время я хотел бы, чтобы исходная проблема была решена, а затем можно было бы добавить такие функции, если они не имеют смысла для разработки в разрешении для начала (они могли бы).

почему нельзя запретить одному контейнеру разговаривать с ним? Это может быть чрезвычайно полезно для отладки

Для чего нужен docker network disconnect ? Вы можете отключить контейнер от сети для отладки и повторно подключить его с помощью docker network attach

Для тех, кто только что обнаружил, что на их серверах, открытых в Интернете, открыты тонны портов, после использования UFW я копал и копал и обнаружил следующее:

Ubuntu 16.04 с UFW и Docker ставит новые задачи. Я выполнил все шаги, как показано здесь: https://svenv.nl/unixandlinux/dockerufw, НО я НЕ смог заставить docker plus UFW работать 16.04. Другими словами, что бы я ни делал, все порты докеров стали глобально доступными для Интернета. Пока я не нашел это: http://blog.samcater.com/how-to-set-docker-1-12-to-not-interfere-with-iptables-firewalld/
Мне пришлось создать файл: /etc/docker/daemon.json и вставить в него следующее:

{
"iptables": ложь
}

Затем я выполнил sudo service docker stop затем sudo service docker start НАКОНЕЦ, докер просто следует соответствующим правилам в UFW.

Дополнительные данные: https://chjdev.com/2016/06/08/docker-ufw/

@thaJeztah

почему нельзя запретить одному контейнеру разговаривать с ним? Это может быть чрезвычайно полезно для отладки
Для чего нужно отключение сети докеров? Вы можете отключить контейнер от сети для отладки и повторно подключить его с помощью сетевого подключения докеров.

А что, если желательно, чтобы все еще оставалось подключение к сети? Пример: Тестирование сбоя проверки работоспособности с сервера в контейнере B для контейнера A при сохранении услуг, предоставляемых контейнерами C, D и E. Легче просто запретить переход контейнера B в контейнер A, чем закрыть всю сеть - контейнеры C также могут зависит от доступа к контейнеру B для прохождения проверки его работоспособности.

Тем не менее, это не выдерживает требования «давайте исправим исходную проблему».

@ gts24 интересная находка.

ИМХО, вся проблема в том, что Docker, как и буквально все другие программы, вообще не должен трогать брандмауэр (iptables или иначе). Когда я устанавливаю (например) apache и приказываю ему прослушивать 0.0.0.0:80 , я принимаю решение открывать порт 80 на брандмауэре или нет, где я могу указать для него любые правила, которые я хочу.

Вместо того, чтобы заново изобретать правила брандмауэра для файлов конфигурации docker (и / или compose), вся функция PUBLISH должна быть устаревшей, а новая функция LISTEN должна работать, как и все другие программы. В лучшем случае докер может создавать отключенные по умолчанию службы firewalld для каждого порта / контейнера в системах, которые его используют.

@gcscaglia устанавливает --iptables=false на демоне, и вы должны это получить

@thaJeztah Эта функция бесполезна, потому что Docker не предлагает альтернативы для получения необходимых правил брандмауэра (или более конкретных перехватчиков сценария запуска с соответствующими параметрами) от демона. Его едва ли можно использовать, если у вас есть статические контейнеры и вы никогда ничего не меняете в них (например, опубликованные порты), но во всех остальных случаях вы можете забыть об этом.

@thaJeztah именно то, что сказал Таладар.

--iptables=false следует переименовать в --networking=false поскольку даже внутренняя сеть между контейнерами не работает с отключенной функцией. Возможность, чтобы контейнеры прослушивали некоторую комбинацию порта / интерфейса, не пробивая дыры во входящих правилах брандмауэра (например, LISTEN ), решила бы все это, была бы обратно совместимой и позволила бы использовать --iptables=true .

В таком режиме прослушивания те, кто хочет, чтобы он «просто работал», могут продолжать использовать PUBLISH , а те, кому нужен контроль, могут использовать LISTEN .

@gcscaglia хорошо, так что если вы хотите, чтобы докер устанавливал основные правила и обрабатывал сеть контейнер-контейнер, но не "публикацию". Вы можете оставить --iptables включенным, но _не_ используйте -p / --publish . У вас должна быть возможность вручную установить правила IPTables для перенаправления портов в контейнеры. Контейнер уже прослушивает свой собственный частный IP-адрес и порты, которые прослушивает служба в вашем контейнере.

@thaJeztah Нет, не можешь. Потому что вы даже не подозреваете, что существует контейнер, который только что запустился и которому нужны правила брандмауэра. Вы также не можете сказать тому, кто использовал API для его запуска, на каком хост-порту он прослушивается.

Простой пример. Мы используем контейнер Docker для запуска заданий Jenkins. Они открывают свой порт SSH, чтобы Docker мог с ними связаться. Они уничтожаются, как только работа сделана. Docker не предлагает никакого способа заставить это работать с --iptables = false, потому что у вас нет возможности сообщить Jenkins (используя Docker API для запуска контейнера) порт хоста, и у вас нет возможности даже запустить скрипт для настройки необходимые правила брандмауэра.

Ваша идея работает только для смехотворно простого случая использования постоянных, никогда не меняющихся контейнеров, запускаемых вручную. Даже простой параметр --restart = always в контейнере нарушит эту настройку, если контейнер не имеет статического IP-адреса.

@taladar Я отвечаю на пример использования --iptables=false , но оставить его включенным, а просто не использовать функцию -p / --publish (которая сообщает докеру, что эти порты доступны через IPTables).

@thaJeztah Хотя вы правильно поняли мой вариант использования, AFAIK предлагаемое решение не будет работать для меня по тем же причинам, по которым оно не будет работать для случая таладара: один перезапуск чего-либо, и внезапно мои ручные правила должны быть обновлены. Нет никаких крючков или триггеров, которые я мог бы использовать для уведомления о перезапуске, поэтому я могу автоматизировать такое обновление (не говоря уже о том, что я изобретал колесо).

В настоящее время единственное решение для моего случая - установить другой брандмауэр (который докер не может коснуться), расположенный между хостом докера и всеми внешними сетями. Но если бы Docker просто делал все, что он уже делает, кроме открытия портов для мира, я мог бы полностью отказаться от второго брандмауэра.

Я думаю, у тебя не может быть всего, но это, безусловно, расстраивает. Считаете ли вы, что стоит открывать запрос функции о моей LISTEN идее, иначе команда Docker не заинтересуется такой функцией?

Что бы сделал LISTEN ? Есть EXPOSE , который позволяет вам аннотировать, какие порты прослушивает контейнер. Для триггеров вы можете прослушать докер events .

Не говорю, что здесь нет места для улучшений (я знаю, что на это смотрят), просто интересно, чего вы ожидаете

В настоящее время, как вы сказали, все службы, работающие в контейнере, привязываются к частному IP-адресу контейнера (если вы не используете --net=host или аналогичный). Это хорошо и желательно, поскольку такая изоляция между хостом и контейнерами является точной точкой продажи Docker.

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

  1. Создает правила iptables для перенаправления выбранного порта на интерфейсе выбранного хоста на выбранный порт на частном IP-адресе контейнера (чего мы все ожидаем, так как это то, что мы просили)
  2. Указывает iptables разрешить любому из любой точки мира доступ к этому порту на интерфейсе выбранного хоста (что не требуется для работы переадресации и, следовательно, застает многих из нас врасплох)

Я предлагаю функцию (названную LISTEN или иначе), которая делает только «1» и позволяет «2» на усмотрение пользователя, как обычно делают все другие службы / программы.

Что касается EXPOSE , AFAIK это только метаданные в образах Docker, поэтому демон знает, что делать, когда пользователь указывает -P (опубликовать все). Возможно, я ошибаюсь, и "открытые" порты могут быть перенаправлены с защитой от перезапуска (без доступа по всему миру)?

--не по теме--

IMHO OP этой проблемы спрашивает, почему -p делает "2" и как остановить Docker от этого. Хотя настройка на уровне демона (кроме отключения сети) может решить эту проблему, для сохранения обратной совместимости лучше всего использовать новую функцию (например, LISTEN или другое имя).

Хотя многим пользователям нравится этот эффект «работает из коробки», ни один системный администратор не ожидает, что программы, отличные от их iptables / firewalld, будут обходить порты на межсетевом экране, тем более что их программное обеспечение для управления межсетевым экраном не сообщает об этом.

Я вижу три основных проблемы:

  1. Способ публикации / раскрытия портов Docker позволяет обойти общие правила брандмауэра, такие как UFW.
  2. Вышеупомянутый факт, по-видимому, плохо документирован (это совершенно неожиданно, поскольку я не знаю других служб Linux, которые бы обходили правила брандмауэра сами).
  3. Не существует простого способа адаптировать iptables к способу публикации / раскрытия портов Docker или к тому, что люди знают, что автоматизировать их нелегко, и ни один из них не задокументирован.

Возможно, технически невозможно реализовать отображение портов в таблице FILTER, и поэтому исправить 1 невозможно. По крайней мере, это должно получить где-то большое предупреждение (исправление 2), но в идеале 3 можно было бы решить с помощью новых опций, которые предлагали здесь люди, например allow или deny которые добавили бы дополнительные правила брандмауэра. автоматически, чтобы разрешить или запретить определенные IP-адреса для открытых / опубликованных портов.

Например, allow: "tcp:{your_trusted_ip}" для контейнера с именем "elasticsearch" порт публикации 9200 может делать что-то вроде:

iptables -t mangle -N DOCKER-elasticsearch
iptables -t mangle -A DOCKER-elasticsearch -s {your_trusted_ip} -j RETURN
iptables -t mangle -A DOCKER-elasticsearch -j DROP
iptables -t mangle -I PREROUTING -p tcp --dport 9200 -j DOCKER-elasticsearch

Я нашел это очень полезным из документов Docker: https://docs.docker.com/engine/userguide/networking/default_network/container-communication/#communicating -to-the-external-world

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

$ iptables -I DOCKER -i ext_if! -s 8.8.8.8 -j ПАДЕНИЕ

@jmimico да, я уже сталкивался с этим раньше. Как бы вы ограничили доступ с 2-х и более IP-адресов?

Что же такого сложного в Docket, просто добавив опции для запуска сценария оболочки в любом месте, где он теперь создает правила iptables со всей внутренней информацией Docker, переданной сценарию в качестве параметров? Это позволило бы каждому создавать именно те правила, которые им нужны. Добавьте способ запустить повторное выполнение сценариев для активных контейнеров, которые люди могут вызывать после того, как они выполнили iptables-restore или сбросили цепочки по некоторым другим причинам, и все готово.

Docker не нужно воссоздавать все виды заранее созданных сценариев брандмауэра, как предлагают некоторые здесь, которые могут быть построены поверх такой системы с помощью наборов дистрибутивов сценариев ловушек. В лучшем случае может иметь смысл что-то вроде раскрытия только на локальном хосте и глобального раскрытия (как это делает сейчас Docker). IPTables обладает слишком большой гибкостью, чтобы Docker мог когда-либо надеяться смоделировать все сценарии прямо в настройках.

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

@StefanPanait Хороший вопрос. Я держу пари, что вам нужно будет использовать использование групп объектов. Заполните группы объектов IP-адресами из белого списка, а затем используйте эту группу объектов в первой строке цепочки DOCKER.

Пример:
iptables -N docker-allow
iptables -A docker-allow -s 1.1.1.1 -j ПРИНЯТЬ
iptables -A docker-allow -s 2.2.2.2 -j ПРИНЯТЬ
iptables -A docker-allow -s 3.3.3.3 -j ПРИНЯТЬ
iptables -A docker-allow -j DROP

iptables -I ДОКЕР -i ext_if -j docker-allow

Разве правила, добавленные в цепочку DOCKER, никогда не затирались такими вещами, как перезапуск демона? Я думаю, что проблема ручных решений заключается в том, что их сложно сделать правильно, и поэтому есть веские основания для более прямой поддержки общих случаев (блокировать / разрешать отдельные IP-адреса).

Возможно, плагин будет подходящим местом для поддержки этого? Например, возможно, плагин «ufw» мог бы добавлять правила ufw-совместимым способом, чтобы пользователь мог эффективно управлять брандмауэром с помощью своей обычной инструментальной цепочки брандмауэра, а службы докеров вели бы себя больше как обычные службы хоста.

Разве правила, добавленные в цепочку DOCKER, никогда не затирались такими вещами, как перезапуск демона? Я думаю, что проблема ручных решений заключается в том, что их сложно сделать правильно, и поэтому есть веские основания для более прямой поддержки общих случаев (блокировать / разрешать отдельные IP-адреса).

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

В Docker v.17.06 появилась новая цепочка iptables под названием DOCKER-USER. Это для ваших пользовательских правил, см. Мой ответ на serverfault: https://serverfault.com/questions/704643/steps-for-limiting-outside-connections-to-docker-container-with-iptables/886257#886257

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

С другой стороны, он все еще придерживается менталитета «Docker - единственный пользователь iptables», который отстой для людей, которые действительно хотят использовать iptables не только для Docker. Так что это плохо для всего круга потенциальных пользователей, за исключением, может быть, тех, кто посвящает целые хосты только Docker.

Хорошо, поэтому использование DOCKER-USER решает проблему порядка вставок, гарантируя, что он всегда входит в цепочку перед другими правилами, связанными с Docker. Однако это не упрощает добавление трафика к контейнеру по номеру порта в белый список, поскольку на этом этапе --dport - это порт службы внутри контейнера докеров, а не открытый порт. Пример:

Опубликуйте порт 9900, чтобы открыть службу Docker для внутреннего прослушивания на 9000.

$ sudo iptables -A DOCKER-USER -m limit --limit 20/min -j LOG --log-prefix "IPTables: "
$ docker run --rm -it -p '192.168.56.101:9900:9000' alpine nc -l 9000

С другой машины в сети:

$ telnet 192.168.56.101 9900

Что регистрируется:

IPTables: IN=enp0s8 OUT=docker0 MAC=08:00:27:b6:8d:d6:0a:00:27:00:00:04:08:00 SRC=192.168.56.1 DST=172.17.0.2 LEN=52 TOS=0x00 PREC=0x00 TTL=127 ID=14127 DF PROTO=TCP SPT=51208 DPT=9000 WINDOW=64240 RES=0x00 SYN URGP=0
IPTables: IN=docker0 OUT=enp0s8 PHYSIN=veth05ba007 MAC=02:42:0f:f9:76:4c:02:42:ac:11:00:02:08:00 SRC=172.17.0.2 DST=192.168.56.1 LEN=40 TOS=0x00 PREC=0x00 TTL=63 ID=23041 DF PROTO=TCP SPT=9000 DPT=51208 WINDOW=0 RES=0x00 ACK RST URGP=0

Итак, как вы видите, на данный момент нет возможности фильтровать трафик на порт 9900. Конечно, я мог бы фильтровать трафик до 9000, но это проблема, потому что внутренний порт может непреднамеренно перекрываться между несколькими контейнерами или даже службами, работающими на хосте. Это одно из преимуществ Docker: вы можете запускать несколько сервисов на одном хосте и не беспокоиться о конфликтах портов. Итак, многие контейнеры предназначены просто для прослушивания порта, и пользователь может использовать параметр --publish чтобы изменить, какой порт доступен для какого интерфейса:

$ docker run -d -p 7777:6379 --name data1 redis
$ docker run -d -p 8888:6379 --name data2 redis

Однако я не могу использовать DOCKER-USER (поправьте меня, если я ошибаюсь), чтобы влиять на трафик к data1, не влияя также на трафик data2, если я не использую какой-то самоанализ для обнаружения IP-адреса контейнера назначения, который является временным, что возвращает вас к решите, что нужно найти простой и надежный способ защиты опубликованных служб с помощью брандмауэра без сценариев и самоанализа.

Чтобы было ясно, это НЕ РАБОТАЕТ:

$ sudo iptables -A DOCKER-USER -p tcp -m tcp -s 192.168.56.0/24 --dport 7777 -j RETURN
$ sudo iptables -A DOCKER-USER -p tcp -m tcp -s 10.0.24.0/24 --dport 8888 -j RETURN
$ sudo iptables -A DOCKER-USER -p tcp -m tcp --dport 7777 -j DROP
$ sudo iptables -A DOCKER-USER -p tcp -m tcp --dport 8888 -j DROP

Это действительно работает, но в результате обе службы доступны для обоих CIDR из белого списка:

$ sudo iptables -A DOCKER-USER -p tcp -m tcp -s 192.168.56.0/24 --dport 6379 -j RETURN
$ sudo iptables -A DOCKER-USER -p tcp -m tcp -s 10.0.24.0/24 --dport 6379 -j RETURN
$ sudo iptables -A DOCKER-USER -p tcp -m tcp --dport 6379 -j DROP

Таким образом, похоже, что DOCKER-USER полезен только для предоставления всех портов определенным IP-адресам, но не для раскрытия определенных портов для определенных IP-адресов, если вы не возражаете написать свои правила iptables для ссылки на внутренние номера портов и не имеете нескольких контейнеров, использующих одни и те же номера внутренних портов. Кажется, что все упускают эти моменты и работают с DOCKER-USER в качестве решения, и я думаю, что это заслуживает нового, лучшего решения.

@SeerUK
Решение, размещенное в https://gist.github.com/SeerUK/b583cc6f048270e0ddc0105e4b36e480 , у меня не сработало. Не могли бы вы помочь?

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

Кажется, что iptables ведет себя странно, если вы меняете iptables во время работы докера. Если вы остановите docker set iptables и перезапустите докер, все будет работать как предусмотрено. Меня беспокоит ... если мне придется изменить iptables в процессе производства?

Кажется, что iptables ведет себя странно, если вы меняете iptables во время работы докера.

iptables - это iptables, ему все равно, работает докер или нет ..

Лучше, если вы проведете много тестов раньше ... сказать, что iptables - это iptables, - это просто тавтология. Это мое предложение :)

Я думаю, что его точка зрения заключалась в том, что iptables не ведет себя иначе, когда Docker работает, как предложил ваш комментарий. Это явно проблема Docker в том, как они используют iptables, как будто это никому не нужно в той же системе.

Я не понимаю, почему DOCKER-USER не решает эту проблему.
Вы используете iptables для фильтрации, как вам нравится: исходный порт, целевой порт, исходный адрес, нелокальный трафик и т. Д.

Весь смысл DOCKER-USER том, что он запускает любые правила, которые пользователь хочет запустить, до того, как будут запущены какие-либо правила докеров, это позволяет вам делать с трафиком все, что вы хотите, до того, как он попадет в докер.

@ cpuguy83 основная проблема заключается в том, что докер открывает порты для общедоступной сети - вне системы - без уведомления, потому что он не привязывает открытые порты к сетевому интерфейсу (например, eth0 или lo) или конкретному IP (например, 127.0.0.1). 0.1, 172.16.1.1); правила, введенные Docker, также не отображаются в инструментах управления iptables, таких как UFW, поэтому пользователи могут не знать, что различные контейнеры докеров доступны из любой системы в сети, а не только из их локального хоста.

DOCKER-USER не может решить эту проблему, потому что он также не привязывает сети докеров к определенному сетевому интерфейсу или определенному IP (например, 127.0.0.1).

По моему первоначальному запросу:
1. Docker должен привязать себя к определенному IP-адресу - на основе конфигурации сети Docker - и затем разрешить пользователю добавлять правила для доступа к контейнеру вне системы (используя инструменты по своему выбору); по умолчанию контейнер не должен быть выставлен вне системы.

  1. Это невозможно сделать без переходного периода, поскольку, по крайней мере, некоторые люди полагаются на историческое поведение.

ИМХО, вся проблема в том, что Docker, как и буквально все другие программы, вообще не должен трогать брандмауэр (iptables или иначе). Когда я устанавливаю (например) apache и приказываю ему прослушивать 0.0.0.0:80, я принимаю решение открывать порт 80 на брандмауэре или нет, где я могу указать для него любые правила, которые я хочу.
Вместо того, чтобы заново изобретать правила брандмауэра для файлов конфигурации docker (и / или compose), вся функция PUBLISH должна быть исключена, а новая функция LISTEN должна работать, как и все другие программы. В лучшем случае докер может создавать отключенные по умолчанию службы firewalld для каждого порта / контейнера в системах, которые его используют.

Хорошо сказано @gcscaglia ! Еще больше сбивает с толку то, что все это почти или совсем не упоминается в https://docs.docker.com/engine/reference/run/#expose -incoming-ports, где, я думаю, большинство тех, кто действительно хочет искать немного документации, дополняющей обучение на примерах. Где-то должно быть ярко-красное поле, объясняющее риски того, что Docker переопределит ранее существовавшие правила iptables.

Если вы не хотите, чтобы докер управлял iptables, установите --iptables-=false
Если вы хотите, чтобы докер открывал порты только на определенном интерфейсе, вы также можете установить это в конфигурации демона.

@BenjamenMeyer
Вы можете заблокировать весь трафик в DOCKER-USER и пропустить только то, что хотите.

Вы можете заблокировать весь трафик в DOCKER-USER и пропускать только то, что хотите.

@ cpuguy83 Скажите, пожалуйста, если что-то из моих

$ docker run -d -p 7777:6379 --name data1 redis
$ docker run -d -p 8888:6379 --name data2 redis

Однако я не могу использовать DOCKER-USER (поправьте меня, если я ошибаюсь), чтобы влиять на трафик к data1, не влияя также на трафик data2, если я не использую какой-то самоанализ для обнаружения IP-адреса контейнера назначения, который является временным, что возвращает вас к решите, что нужно найти простой и надежный способ защиты опубликованных служб с помощью брандмауэра без сценариев и самоанализа.

Чтобы было ясно, это НЕ РАБОТАЕТ:

$ sudo iptables -A DOCKER-USER -p tcp -m tcp -s 192.168.56.0/24 --dport 7777 -j RETURN
$ sudo iptables -A DOCKER-USER -p tcp -m tcp -s 10.0.24.0/24 --dport 8888 -j RETURN
$ sudo iptables -A DOCKER-USER -p tcp -m tcp --dport 7777 -j DROP
$ sudo iptables -A DOCKER-USER -p tcp -m tcp --dport 8888 -j DROP

Это действительно работает, но в результате обе службы доступны для обоих CIDR из белого списка:

$ sudo iptables -A DOCKER-USER -p tcp -m tcp -s 192.168.56.0/24 --dport 6379 -j RETURN
$ sudo iptables -A DOCKER-USER -p tcp -m tcp -s 10.0.24.0/24 --dport 6379 -j RETURN
$ sudo iptables -A DOCKER-USER -p tcp -m tcp --dport 6379 -j DROP

Таким образом, нет способа контролировать трафик в / из контейнера data1 независимо от контейнера data2 с помощью DOCKER-USER . Для меня это делает DOCKER-USER не лучшим решением.

@colinmollenhour

Если у вас есть общее правило перетаскивания в DOCKER-USER , чем оно отличается от того, если докер добавлен или добавлен в начало, это основное правило перехода?

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

И даже в качестве отказа - чего не должно быть - он должен быть очень хорошо задокументирован, поскольку это совершенно неожиданно и довольно нелогично отлаживать. Особенно учитывая, сколько используют ufw . Я не знаю другого программного обеспечения, делающего что-то подобное без явных предупреждений, и я бы лично держался подальше от такого программного обеспечения.

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

Я надеюсь, что Docker перейдет к подходу, основанному только на СЛУШАНИИ , как показано на примере

Если у вас есть общее правило перетаскивания в DOCKER-USER, чем оно отличается от того, если докер добавлен, а не добавлен в начале, это основное правило перехода?

Я не думаю, что понимаю ваш вопрос ... Пожалуйста, не обращайте внимания на мой комментарий от 22 апреля 2017 г., так как он касается стойкости, которую действительно решает DOCKER-USER. Проблема, на которую я указываю, не в настойчивости.

@taladar Что именно ты

@jacoscaz

Во-первых, я думаю, что все сопровождающие согласны с тем, что существующее поведение не идеально. К сожалению, такое поведение существует всегда. Изменить значение по умолчанию для чего-то, что используется миллионами людей, - это не то, что мы можем здесь сделать. Это одна из причин, по которой мы добавили DOCKER-USER чтобы, по крайней мере, люди могли вводить любые правила, которые им нужны.

Однако мы не можем обойтись без необходимости использовать iptables (или ebpf, или какое-либо другое решение для nating) И предоставить функциональность, которую -p предоставляет ... обратную сторону, если вы хотите, чтобы люди не пробивали дыры в брандмауэре, запретить им использовать -p .

Напомним, что вы можете:

  1. Сообщите докеру (по умолчанию) привязку к определенному адресу (адрес по умолчанию, конечно, 0.0.0.0 или все интерфейсы) ... однако это не помешает кому-то вручную указать адрес в -p spec (например, -p 1.2.3.4:80:80 )
  2. Вставить пользовательские правила в DOCKER-USER , включая запретить все
  3. Отключить управление iptables с помощью --iptables=false

Вы можете предложить что-то еще, кроме нарушения работы существующих пользователей?

@ cpuguy83 Я понимаю это. Я не сторонник того, чтобы такое радикальное изменение от одной версии к другой происходило. Я действительно думаю, что было бы неплохо запланировать изменение в течение ряда версий. Я также не думаю, что Docker должен полностью отказаться от использования iptables. Forwarding отличается от разрешения , и, насколько я могу понять , Docker должна быть в состоянии направить по умолчанию без предоставления доступа к любому где -нибудь по умолчанию.

Что касается того, что можно сделать сейчас, в первую очередь должна быть дополнительная документация по этому поводу. Какой канал будет наиболее подходящим для обсуждения этого вопроса с сопровождающими docker.com?

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

Я согласен с призывом добавить дополнительную документацию для этой функции. Насколько я могу судить, он упоминается только на https://docs.docker.com/network/iptables/. Я узнал об этой проблеме только после того, как попытался выяснить, почему служба, доступность которой я ограничил для определенного IP-адреса через брандмауэр, стала общедоступной. Такое поведение было для меня совершенно неожиданным, поскольку оно противоречит всем другим сетевым службам, с которыми я когда-либо имел дело.

Я понимаю, что есть обходные пути, но я считаю, что это следует учитывать для долгосрочного изменения функционирования Docker. Мне нравится идея установить для него LISTEN на порту и разрешить пользовательским правилам строить поверх этого.

Как и было предложено, я добавил правило DROP в DOCKER-USER, чтобы предотвратить случайное попадание контейнеров извне (что, к сожалению, действительно произошло).

Однако теперь у меня есть одна услуга, которую я хочу раскрыть. Но, как объяснил @colinmollenhour , поскольку NAT происходит до фильтрации, я могу фильтровать только IP-адрес докера (который не фиксирован) и внутренний номер порта (который может быть одинаковым для нескольких контейнеров).

Итак, как я могу открыть эту единственную услугу?

@SystemParadox - это одна из многих причин, по которым DOCKER-USER не решает проблему.

@ cpuguy83
Мне действительно нравится предлагаемое решение LISTEN, и я никогда не выступал за критический переход от одного выпуска к другому; но скорее выступал за внесение изменений в серию версий с соответствующими уведомлениями. Я понимаю, что многие люди используют Docker, и критическое изменение одной версии на другую будет пагубным для всех.

Я также согласен с тем, что обновление документации Docker относительно -p EXPOSE и т. Д. Должно быть приоритетом, который выполняется немедленно, чтобы хотя бы привлечь внимание к проблеме. По моему опыту, большинство людей, использующих Docker, не являются экспертами по брандмауэрам, поэтому они доверяют Docker делать то, что они ожидают, чего нет в текущем дизайне.

Кроме того, решения по составлению резюме в https://github.com/moby/moby/issues/22054#issuecomment -425580301 тоже не работают. Почему? Я не запускаю Docker напрямую, я запускаю Docker Compose - на основе YAML; IP-адреса являются динамическими (контролируются Docker), и часто я развертываю несколько служб в одной сети Docker, которым необходимо взаимодействовать друг с другом. Таким образом, как использование -p использование привязки адреса (вариант 1 в резюме) не являются решениями. DOCKER-USER на самом деле ничего не решает, как указывали другие (вариант 2 в резюме), и полное отключение IP-таблиц (вариант 3 в резюме) также ничего не помогает, b / c теперь все сломано (IP-адреса динамические, поэтому трудно написать сценарий решения; межконтейнерная сеть нарушена, потому что Docker полагается на IPTables для перемещения между контейнерами и т. д.).

Опять же, в этом потоке нет вызова для критического изменения между двумя версиями; но призыв к плановому поэтапному подходу, который позволяет людям мигрировать надлежащим образом.

В качестве обходного пути вы можете получить доступ к исходному порту назначения с помощью -m conntrack --ctorigdstport .

Итак, в моем случае у меня есть следующее:

-A DOCKER-USER -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
# Allow docker out
-A DOCKER-USER -s 172.17.0.0/16 -j ACCEPT
# Allow access to docker service mapped to host 8702 (the service is actually listening on port 8088 in the container)
-A DOCKER-USER -p tcp -m conntrack --ctorigdstport 8702 -j ACCEPT
# Prevent access to docker from outside
-A DOCKER-USER -j DROP

@SystemParadox

Ура за правильное решение iptables! 🍻

Я никогда не слышал о --ctorigdstport но думаю, это потому, что я не читал и не пробовал все возможные расширения iptables, и, насколько мне известно, вы первый, кто упомянул об этом в этом контексте.

Я тестировал, и это действительно работает:

$ docker run -d -p 7777:6379 --name data1 redis
$ docker run -d -p 8888:6379 --name data2 redis
$ sudo iptables -N DOCKER-USER-redis1
$ sudo iptables -A DOCKER-USER-redis1 -s 192.168.56.0/24 -p tcp -m tcp -j RETURN
$ sudo iptables -A DOCKER-USER-redis1 -j REJECT --reject-with icmp-port-unreachable
$ sudo iptables -N DOCKER-USER-redis2
$ sudo iptables -A DOCKER-USER-redis2 -s 10.0.24.0/24 -p tcp -m tcp -j RETURN
$ sudo iptables -A DOCKER-USER-redis2 -j REJECT --reject-with icmp-port-unreachable
$ sudo iptables -A DOCKER-USER -i eth0 -p tcp -m conntrack --ctorigdstport 7777 -j DOCKER-USER-redis1
$ sudo iptables -A DOCKER-USER -i eth0 -p tcp -m conntrack --ctorigdstport 8888 -j DOCKER-USER-redis2

Я думаю, что подобный пример относится к документации, поскольку он, вероятно, охватывает то, что ищут 99% пользователей: возможность открывать порты с помощью -p но при этом иметь возможность контролировать трафик к ним, используя общие фильтры, такие как -s .

Я создал запрос на обновление документации Docker относительно iptables.

https://github.com/docker/docker.github.io/issues/8087

Решение, указанное там в https://unrouted.io/2017/08/15/docker-firewall/
похоже, что-то похожее, создавая дополнительную цепочку iptables под названием FILTERS
туда, куда переходят цепочки INPUT и DOCKER-USER.

@SystemParadox @colinmollenhour После тестирования --ctorigdstport я могу подтвердить, что он работает, но с небольшой оговоркой.

В моем случае у меня есть dockerized PHP-приложение на Apache, прослушивающем порт 80. Мои правила, разрешающие только 1.2.3.4, следующие:

-A DOCKER-USER -s 1.2.3.4/32 -i eth0 -p tcp -m conntrack --ctorigdstport 80 -j ACCEPT
-A DOCKER-USER -i eth0 -p tcp -m conntrack --ctorigdstport 80 -j DROP

Так что мое правило отбрасывания немного более конкретное, чем ваше, отбрасывая только пакеты, попадающие на мой веб-сервер - по крайней мере, я так думал. Фактически, он отбрасывал пакеты, направленные на мой веб-сервер, а также пакеты, возвращаемые с ответами на запросы, сделанные приложением PHP на сторонние серверы.

Это связано с тем, что --ctorigdstport соответствует не порту назначения в фильтруемом пакете, а в пакете, который инициировал соединение. Таким образом, ответы на запросы, исходящие из Docker на другие серверы, будут иметь SPT=80 а также будут соответствовать --ctorigdstport 80 .

Если кто-то хочет иметь более жесткий контроль в правилах DROP, также необходимо добавить --ctdir :

-A DOCKER-USER -i eth0 -p tcp -m conntrack --ctorigdstport 80 --ctdir ORIGINAL -j DROP

Фактически, все правила, разрешающие соединение, также должны иметь добавление --ctdir для точного выражения их значения:

-A DOCKER-USER -s 1.2.3.4/32 -i eth0 -p tcp -m conntrack --ctorigdstport 80 --ctdir ORIGINAL -j ACCEPT

@jest Вау, это действительно важно знать! Я не понимал, что он может сопоставлять пакеты в другом направлении. Это имеет смысл, если подумать, поскольку он соответствует состоянию всего соединения, но его легко упустить при чтении документации.

@SystemParadox да, у меня не было возможности проинформировать себя через документацию, и я был застигнут врасплох запросами от Docker, которые зависали в ожидании ответов. :)

Я все время езжу по кругу, пытаясь найти причины, по которым мне нужен --ctdir ORIGINAL . С одной стороны, объяснение @jest имеет смысл, а с другой стороны, мне обычно никогда не приходится иметь дело с ответными пакетами, так почему здесь должно быть иначе?

Я думаю, разница в том, что у меня -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT в качестве первого правила, поэтому остальные мои правила никогда не видят никаких ответных пакетов. В этом случае я думаю, что --ctdir ORIGINAL не является строго обязательным, хотя, вероятно, было бы безопаснее включить его в любом случае.

@jest , ты согласен с этим? Предположительно у вас нет раннего правила ESTABLISHED,RELATED -j ACCEPT , поэтому это имеет значение для вас?

@jest Ваш пост очень помог, спасибо.

Ваш подход требуется только для вещей, управляемых докером, или для всего? Например, мой порт ssh (22) не имеет ничего общего с докером. Нужно ли мне использовать -m tcp --dport 22 как обычно, или я должен использовать -m conntrack --ctorigdstport 22 --ctdir ORIGINAL ?

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

@ lonix1 Правила добавляются докером в цепочку DOCKER только в том случае, если вы открываете порт и, вероятно, только тогда, когда контейнер запущен. Если ни в одном из ваших контейнеров вы не открываете порт 22, ваши правила брандмауэра должны работать без изменений.

@SystemParadox Прошло некоторое время, и у меня нет доступа к этому серверу для проверки, но, если я правильно помню, были правила ESTABLISHED,RELATED (управляемые UFW в цепочке ufw-before-input ). Однако в моем случае они не будут соответствовать первому пакету (SYN) подключений, сделанных от докера к Интернет-узлам на порту 80. И будут отброшены правилом approriate в DOCKER-USER когда --ctdir .

@ Ionix1 , пакеты для служб на хосте проходят только через INPUT, тогда как пакеты для служб докеров проходят только через FORWARD и DOCKER-USER.

Например, учитывая внешний IP-адрес 10.0.0.1 и два контейнера с -p 4000:80 и -p 4001:80 , вы увидите пакеты со следующими свойствами:

INPUT:
dst 10.0.0.1 dport 80 ctorigdst 10.0.0.1 ctorigdstport 80
FORWARD/DOCKER-USER:
dst 172.17.0.5 dport 80 ctorigdst 10.0.0.1 ctorigdstport 4000
dst 172.17.0.6 dport 80 ctorigdst 10.0.0.1 ctorigdstport 4001

Таким образом, вы можете безопасно использовать --dport 80 для правил INPUT, поскольку они находятся в совершенно разных цепочках. Как видите, --ctorigdstport 80 прежнему будет соответствовать, но если вы также не искажаете ввод по какой-либо причине, я, вероятно, не стал бы этого делать.

Вы также можете заметить, что на самом деле вы можете использовать --dport 80 с --dst 172.17.0.5 для фильтрации пакетов для определенного контейнера докеров, но этот IP-адрес непредсказуем, поэтому мы используем --ctorigdstport .

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

@jest спасибо, похоже, это подтверждает мои мысли.

Итак, мне нужно небольшое руководство ... чтобы переключиться с UFW на iptables ...
-Я просто выключу UFW через "ufw disable"?
-создать ли я свой собственный файл .sh или есть уже существующий (я Ubuntu в DigitalOcean)?
-я просто нужно сообщить Docker "--iptables = false"? (это для Докера?)
-DOCKER-USER уже создан и привязан Docker?

@fredjohnston, вы можете продолжать использовать UFW, если хотите. Профили хранятся в /etc/ufw . Проблема здесь в том, что Docker не отображается, потому что в /etc/ufw/applications.d нет профиля приложения (то же самое для любого другого инструмента брандмауэра и его конфигурации).

Отключение IPTables в Docker означает, что в Docker у вас не будет много сетевых возможностей, только IP-адреса и контейнеры не смогут общаться друг с другом. DOCKER_USER - это хитрость, которая дает вам некоторый контроль, но на самом деле не решает проблему - которая на самом деле заключается в том, чтобы не делать контейнеры Docker общедоступными в сети по умолчанию, а блокировать их по IP-адресу контейнера.

На данный момент я рекомендую вам продолжать использовать любой инструмент брандмауэра, который вам удобнее всего (ufw и т. Д.), Но имейте в виду, что контейнеры Docker будут общедоступными в вашей сети.

Поскольку я здесь в любом случае - я фактически сталкиваюсь с связанной проблемой сейчас, когда это проблема и на платформах, отличных от Linux. Учтите следующее:

  • В проекте A есть два контейнера: один для базы данных и один для приложения.
  • В проекте B есть два контейнера: один для базы данных и один для приложения.
  • Оба проекта изолированы друг от друга (отдельные репозитории исходников, конфигурации и т. Д.)
  • Оба проекта управляются с помощью Docker Compose.
  • Оба проекта предоставляют свои порты баз данных для локальных целей разработки.
  • Оба проекта используют один и тот же сервер базы данных (postgres, mysql и т. Д.)

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

В соответствии с текущим дизайном взаимодействия брандмауэра - и что отчасти приводит к указанным выше проблемам, связанным с открытием контейнера для общедоступной сети без ведома пользователя - докеры базы данных обоих проектов не могут быть запущены одновременно, поскольку они будут конкурировать за открытый порт для базы данных. То же самое, если вы хотите предоставить доступ к обоим портам их приложений, и они оба использовали один и тот же порт для сервера приложений - обычное дело, поскольку API и приложения на основе HTTP сейчас чрезвычайно распространены, особенно в облачных приложениях.

Конечно, вы можете взломать свой путь к настройке обоих в одном контейнере БД; но вы не изолируете их в соответствии с дизайном вашего проекта, и вам нужно быть еще более осторожным с конфигурациями и т. д.

В правильном решении есть два аспекта:

  1. Контейнеры будут привязаны только к их IP-адресам, а их открытые порты будут привязаны только к их IP-адресам в соответствующих сетях Docker, а не в системе, соответствующей всем IP-адресам ( 0.0.0.0 , :: ).
  2. Docker также по умолчанию не раскрывает маршрут к сетям Docker вне системы. Docker Networking может использоваться для установления межсетевых соединений (сеть докеров - сеть докеров) в соответствии с текущим дизайном, а затем также разрешать соединения с локальным хостом по умолчанию.
  3. В этом случае пользователи будут вынуждены добавить соответствующие правила брандмауэра для предоставления доступа к контейнеру внешнему миру, когда и при желании - например, путем перенаправления порта 443 на порт 443 по своему выбору.

Опять же, это можно делать постепенно:

  • Выпуск 1: Выполните шаги 1 и 2; но добавьте также маршрутизацию, отличную от localhost (временно), с предупреждением. Текущее поведение «первым пришел - первым обслужен» для получения порта сохраняется; и выдается предупреждение об исчезновении этого поведения.
  • Выпуск 1 + N: Отбросьте предупреждение и откажитесь от маршрутизации, отличной от localhost. Требуйте шаг 3 для пользователей, которые хотят, чтобы Docker открывал порты вне системы, и убедитесь, что это хорошо задокументировано.

DOCKER_USER - это хитрость, которая дает вам некоторый контроль, но на самом деле не решает проблему, которая на самом деле заключается в том, чтобы не делать контейнеры Docker общедоступными в сети по умолчанию, а блокировать их по IP-адресу контейнера.

На данный момент я рекомендую вам продолжать использовать любой инструмент брандмауэра, который вам удобнее всего (ufw и т. Д.), Но имейте в виду, что контейнеры Docker будут общедоступными в вашей сети.

Здесь есть запрос на обновление документа https://github.com/docker/docker.github.io/pull/8357, чтобы создать статическую конфигурацию iptables, но я не уверен в ее статусе.

Изменить: я заметил, что вы неправильно поняли значение DOCKER-USER. Он используется не для «блокировки контейнеров по IP-адресу контейнера», а для фильтрации доступа к контейнеру с помощью правил iptables.

@ aki-k на DOCKER_USER Я знаю, что DOCKER_USER не блокирует контейнер по IP-адресу и никогда не утверждал, что это так. DOCKER_USER просто передает проблему безопасности пользователю для управления - это означает, что пользователь должен знать, как управлять своим брандмауэром, чтобы гарантировать, что у него действительно безопасная среда. Делать это проблемой пользователя также неприемлемо, потому что большинство пользователей не знают, как управлять своим брандмауэром - правила брандмауэра сложны, и даже те из нас, кто хорошо разбирается в написании правил брандмауэра, по-прежнему часто ошибаются.

Моя просьба - и вся суть - в том, что Docker должен быть защищен по умолчанию и не открывать контейнеры внешнему миру без ведома пользователя или явного вмешательства для этого. И, согласно моему последнему комментарию (https://github.com/moby/moby/issues/22054#issuecomment-552951146), это также будет иметь некоторые другие технические преимущества.

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

Самый ранний отчет о проблеме, который я нашел по этому поводу, был от 18 марта 2014 г .:

https://github.com/moby/moby/issues/4737

Вероятно, это дизайнерское решение, которое они не хотят менять и пытаются исправить с помощью цепочки iptables DOCKER-USER. Вы можете использовать параметр -p для запуска докера, чтобы опубликовать порт только на хосте докера (-p 127.0.0.1:port:port), но это тоже не решает проблему.

Давайте проясним, Docker безопасен по умолчанию. Вы должны указать Docker, чтобы он выполнял переадресацию портов.

Что касается сервисов, которым необходимо взаимодействовать друг с другом, вы должны использовать сети ( docker network ) для ограничения доступа, а не переадресацию портов.

Давайте проясним, Docker _Is_ безопасен по умолчанию. Вы должны указать Docker, чтобы он выполнял переадресацию портов.

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

Давайте проясним, Docker _Is_ безопасен по умолчанию. Вы должны указать Docker, чтобы он выполнял переадресацию портов.

Что касается сервисов, которым необходимо взаимодействовать друг с другом, вы должны использовать сети ( docker network ) для ограничения доступа, а не переадресацию портов.

Давайте проясним - Docker безопасен, пока вы не захотите получить к нему доступ за пределами сети докеров. После того, как вы хотите , чтобы получить доступ к нему даже с локального хоста (127.0.0.1) , то Docker не является безопасным по умолчанию , как он связывается с 0.0.0.0 и выставляет контейнеры выключения системы - в обход ничего пользователь делает , чтобы обеспечить свою систему, не отображая в инструментах, кроме прямого использования iptables . DOCKER_USER не является и никогда не будет подходящим решением, поскольку требует, чтобы пользователь слишком много знал о базовой системе брандмауэра (iptables в Linux, все, что использует Mac, и брандмауэр Windows в Windows). Это по-прежнему должно быть защищено по умолчанию. Есть очень простой способ сделать это, как я обрисовал ранее в https://github.com/moby/moby/issues/22054#issuecomment -552951146 и неоднократно обращался к ним по этим вопросам (хотя, возможно, не так четко, как в этом комментарий).

Также обратите внимание, что документация по раскрытию порта также не упоминает эту проблему безопасности, не делая никаких ссылок на то, как Docker взаимодействует с системным брандмауэром при раскрытии порта - тем самым снижая безопасность систем, которые были спроектированы так, чтобы быть безопасными до использования Docker. .

Давайте проясним, Docker _Is_ безопасен по умолчанию. Вы должны указать Docker, чтобы он выполнял переадресацию портов.
Что касается сервисов, которым необходимо взаимодействовать друг с другом, вы должны использовать сети ( docker network ) для ограничения доступа, а не переадресацию портов.

Давайте проясним - Docker безопасен, пока вы не захотите получить к нему доступ за пределами сети докеров. Если вы хотите получить к нему доступ даже с локального хоста (127.0.0.1), тогда Docker _не_ безопасен по умолчанию, поскольку он связывается с 0.0.0.0 [...]

Если быть точным, Docker не привязан к 0.0.0.0. Это (разумное) ожидание пользователя: указание --publish в CLI, следовательно, работающее в пользовательском пространстве, запускает своего рода прокси-демон, который прослушивает указанный порт, принимает входящие соединения и толкает туда и обратно. все пакеты между Докером и внешним миром.

Но вместо этого Docker вводит магические правила DNAT / маскарадинга в брандмауэр, чтобы переписать адреса в пакете, тем самым случайным образом нарушая любую предустановленную систему правил.

На мой взгляд, беспорядок в уровнях абстракции здесь - самый большой облом и сбивает пользователя с толку. Я не знаю, какие сценарии учитывала команда Docker при разработке механизма --publish мы видим здесь, я не вижу ни одного, что оправдывает это решение (возможно, помимо соображений производительности).

Давайте проясним, Docker по умолчанию безопасен. Вы должны указать Docker, чтобы он выполнял переадресацию портов.

... что является одной из наиболее часто используемых функций Docker. Фактически, вы только что заявили, что недокументированное переопределение ранее существовавших правил брандмауэра в одной из наиболее часто используемых функций Docker делает Docker _безопасным по умолчанию_.

Что ж, думаю, все, что сработает для вас. Поскольку у меня это не работает, я начну искать альтернативные контейнерные платформы. Проблемы с безопасностью можно исправить, но явное пренебрежение разумными ожиданиями безопасности - совсем другое дело.

Оценим ситуацию на сегодняшний день:

По умолчанию порты не отображаются. Вы должны указать Docker открыть порт.
Раньше Docker настраивал iptables таким образом, чтобы все, что знало, как маршрутизировать в сеть моста, могло получить доступ к IP-адресам контейнера (путем установки политики пересылки на «accept»), но теперь это неверно.

Я видел, как некоторые люди говорили, что они используют -p для предоставления друг другу услуг, что не должно быть обязательным.
В сети по умолчанию вы можете использовать --link для соединения служб вместе, контейнер доступен через DNS.
В нестандартных сетях (определяемых пользователем сетях) контейнеры также могут обращаться друг к другу через DNS, включая настройку псевдонимов через --link или даже в сеть с указанным псевдонимом.

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

-p специально разработан для входа, так как позволяет внешним объектам обращаться к этой службе.
По умолчанию для -p действительно разрешен трафик из любого места. Вы можете изменить это, вручную указав разрешенный адрес либо для -p либо как параметр для всего демона.
Поскольку -p действительно использует iptables, была создана цепочка DOCKER-USER чтобы пользователи могли добавлять свои собственные правила фильтрации до того, как она попадет в контейнер.

Поскольку -p предназначен для входящего трафика, я думаю, что для этого разумно раскрыть трафик, как он это делает. Мне очень жаль, что правила Docker вставлены в верхнюю часть таблицы фильтров, однако изменение этого было бы серьезным изменением для большой группы пользователей, которым ДЕЙСТВИТЕЛЬНО нужно такое поведение.

Есть несколько других альтернатив -p :

  1. Не используйте -p , подключитесь к IP-адресу контейнера напрямую. Это требует небольшой дополнительной работы, поскольку вам нужно искать IP-адрес, но эти данные доступны из API. Вам также необходимо убедиться, что политика пересылки на брандмауэре позволяет это (при условии, что вы подключаетесь с другого хоста, тот же хост должен быть в порядке)
  2. Используйте сеть macvlan или ipvlan для служб, которые должны быть доступны из сети хоста (т. Е. Входящие). Эти сетевые параметры предоставляют контейнеру IP-адрес непосредственно из сетевого интерфейса хоста (вы выбираете, к какому интерфейсу он привязан).
  3. Используйте --net=host , это запускает службу в пространстве имен сети хоста, предоставляя службе доступ к сетевой инфраструктуре, уже существующей на хосте.

Вы говорите «сделать это безопасным по умолчанию», но раскрытие порта по определению является потенциально небезопасным действием. Также, похоже, есть какая-то идея, что доступ только к localhost является безопасным, но это не так, как что-либо, работающее на хосте, может получить доступ к localhost (включая javascript в браузере, если это рабочий стол).

Какой вариант использования вы пытаетесь решить, используя -p ?
Есть ли у вас какие-то мысли о реальных изменениях, которые вы бы хотели увидеть?

Я могу что-то изменить, чтобы сделать это лучше для вашего рабочего процесса, но здесь есть много разных вариантов использования, и один размер никогда не подходит всем (см. Жалобы на -p ).

@ cpuguy83 все в порядке, но ничего не меняет в этом запросе.

Люди используют докер и хотят подключаться к приложениям, работающим под докером, из своей локальной системы - это чрезвычайно распространенный вариант использования для разработчиков, особенно при попытке что-то диагностировать или написании службы, которая не находится в докере, но должна подключаться к службам, размещенным в докере. (чтобы заполнить среду разработки). Разработка под Docker - не всегда хорошее, интересное или полезное занятие, вот что:

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

Это просто запрет. Не все инструменты совместимы с Docker, да и не должны. От пользователя не требуется, чтобы он полностью находился под Docker, чтобы использовать службы, работающие под Docker. Даже в этом случае, если Docker используется для управления серверами, администратор не может легко управлять ими с помощью конфигурации брандмауэра и функции Expose Port, однако она полностью нарушена (командная строка, Dockerfile, Docker Compose Config).

Кроме того, люди используют Docker Compose для управления большей частью среды и указывают через docker-compose.yml или Dockerfile что порт должен быть открыт, чтобы они могли получить к нему доступ локально. Поэтому указание параметра use the -p неверно, поскольку они никогда не взаимодействуют с командой docker напрямую таким образом, чтобы это работало.

Открытие порта не означает, что безопасность должна быть нарушена. Я обрисовал в общих чертах, как вы можете открыть порт для локальной системы, не нарушая безопасности (https://github.com/moby/moby/issues/22054#issuecomment-552951146), и это поставит управление внешним воздействием (отключено system) в руках пользователя таким образом, чтобы он мог легко управлять с помощью имеющихся инструментов.

Решение:

  • Использовать сеть Docker
  • Выставляйте порты только в сети Docker и на локальном хосте
  • Прекратить привязку порта к 0.0.0.0 - или эффективно сделать это
  • Требовать от пользователей использования собственных средств брандмауэра для открытия порта вне системы (ufw, firewalld и т. Д.)
  • Обеспечьте интеграцию с распространенными межсетевыми экранами, чтобы упростить эту задачу.

Другими словами, вместо доступа к службе в контейнере докеров через 127.0.0.1:<port> требуется <docker container ip>:<service port> даже с локального хоста. Если люди хотят выставить службу вне системы, они могут добавить правило брандмауэра через свои инструменты (ufw и т. Д.) Для перенаправления порта с заданного порта на <docker container ip>:<service port> .

В качестве альтернативы, следуйте подходу Kubernetes с их дизайном прокси, эффективно действуя так, как https://github.com/moby/moby/issues/22054#issuecomment -554665865. Опять же, это все равно должна быть локальная система только до тех пор, пока пользователь целенаправленно не предоставит ее внешней сети.

Самая большая проблема здесь в том, что Docker ставит под угрозу целостность брандмауэра, чтобы предоставлять свои услуги, делая это вообще без уведомления пользователя и без интеграции в инструменты пользователя, поэтому пользователь не знает об этом и не может его контролировать.

Я понимаю, что на рынке существует масса интерфейсов межсетевого экрана - даже с iptables есть firewalld, ufw и еще десяток других. Я действительно не ожидаю, что Docker будет интегрироваться с ними (хотя это было бы неплохо), но я действительно ожидаю, что Docker будет работать таким образом, чтобы не обходить их и не нарушать какую-либо безопасность, которую установил пользователь.

В качестве базового тестового примера:

  • Настройте сервер на базе Debian (Debian, Ubuntu)
  • Установите ufw, OpenSSH Server
  • запустить ufw allow OpenSSH
  • запустить ufw enable

На данный момент у вас есть довольно безопасный сервер; единственный разрешенный трафик либо (а) связан с исходящим трафиком, либо (б) трафиком SSH-сервера.

Теперь запустите док-контейнер с открытым портом.

Приемлемое решение отвечало бы следующим требованиям:

  • Порт не должен быть доступен с другого компьютера; брандмауэр должен продолжать блокировать его от внешних систем.
  • Порт должен быть доступен с локального компьютера (localhost).
  • Несколько контейнеров докеров должны иметь возможность открывать один и тот же порт, и все должно работать; все контейнеры с открытым портом должны быть доступны только из локальной системы.
  • Чтобы это произошло, пользователю не нужно знать подробности конфигурации своего брандмауэра.

Попробуйте то же самое для системы на базе CentOS / Fedora / RHEL с firewalld .

@ cpuguy83 Люди IMO ожидают, что -p будет работать на уровне приложения. То есть, если я -p 80:80 , я ожидаю поведения, как если бы приложение, привязанное к порту 80 в контейнере, работало на хосте.

Перенаправление портов модели VirtualBox или SSH таким образом, поэтому люди предполагают, что то же самое и в случае с Docker.

Если использовать более широкую параллель с ожиданиями: с точки зрения пользователя, он должен работать как тома, привязанные к хосту. Вы просто указываете на каталог хоста, и "как по волшебству" он виден внутри контейнера; любые изменения, внесенные в файловую систему с другой стороны, работают одинаково, включая разрешения, квоты и т. д.

Сводя проблемы -p к случаям межсетевого экрана: в обычном мире пользователь ожидает, что приложение, привязанное к 0.0.0.0:80, будет видимым для внешнего мира. Если пользователь хочет ограничить доступ, в многочисленных руководствах он получает инструкции по использованию брандмауэра и настройке правила в цепочке INPUT :

-P INPUT DROP
-A INPUT -s 1.2.3.4/32 -p tcp --dst-port 80 -j ACCEPT

или используйте такие инструменты, как UFW:

ufw enable
ufw allow http

Но с докером пользователь должен иметь возможность создавать такие правила на ровном месте:

-A DOCKER-USER -s 1.2.3.4/32 -i eth0 -p tcp -m conntrack --ctorigdstport 80 --ctdir ORIGINAL -j ACCEPT
-A DOCKER-USER -i eth0 -p tcp -m conntrack --ctorigdstport 80 --ctdir ORIGINAL -j DROP

Пожалуйста, покажите мне одно исчерпывающее руководство для обычного пользователя о том, как решать такие распространенные сценарии с помощью Docker.

И, например, для среднего разработчика, которому вы продали всю концепцию «контейнеризации приложений», последствия раскрытия порта просто непредсказуемы.

В наши дни я просто не открываю порты из-за отсутствия знаний, а вместо этого использую такие сервисы, как Traefik. Возможно, --expose также не следует предлагать в качестве универсального в документах CLI, потому что IMO он просто не приносит никакой пользы для обычного пользователя.

Или предоставьте любому, кто не имеет глубоких знаний об этих инструментах, ноутбук с Linux, например Dell или Lenovo, и посмотрите, как их добросовестные усилия по правильной настройке своих брандмауэров не имеют абсолютно никакого значения, когда кому-то удается получить доступ к их локальной базе данных, пока выпить кофе в Starbucks.

Именно эта проблема привела к уязвимости системы, которую я обнаружил на выходных. Per @jacoscaz , мне просто удалось подключиться к локальной базе данных другого разработчика, потому что у нее есть опубликованный порт. Было бы здорово, если бы вы все могли включить это во вводную документацию, чтобы другие не делали то же самое. Нам нужна неконтейнерная служба для подключения к контейнеру без доступа всех остальных в сети, поэтому о сетях Docker не может быть и речи. Похоже, что лучший вариант на данный момент - подключиться к локальному IP-адресу контейнера, если у кого-то нет лучшей идеи.

@dentonmwood Это похоже на то, что вам следует делать. Как я упоминал выше, вы даже можете настроить службу для запуска с использованием macvlan или ipvlan, которые предоставят ей IP-адрес непосредственно в вашей обычной сети.

@BenjamenMeyer @jest @jacoscaz

Спасибо за дополнительный отзыв.

Я согласен, что знания, которые мы просим пользователей позировать в этом отношении, невелики. В настоящее время мы возлагаем на пользователя (будь то системный администратор или разработчик) ответственность за понимание того, что делает -p , и за принятие соответствующих мер предосторожности, чтобы служба была доступна только тем, кому, по их мнению, она доступна. Мы делаем еще один шаг вперед, даже ожидая, что разработчики, у которых часто нет причин знать iptables, вмешаются и исправят его для своей собственной среды (с помощью DOCKER-USER ).
В основном мы попали в такую ​​ситуацию из-за опасений нарушить совместимость.

У меня есть некоторые идеи, на которые я еще не успел полностью обдумать, но в основном это будет изменение поведения -p зависимости от версии API клиента и отдельной обработки входящего трафика. Все еще критическое изменение, которое меня беспокоит, но, по крайней мере, старое поведение сохраняется в более старых версиях API.

У меня была мысль, что мы могли бы сделать локальный прокси (ala kubectl proxy ) для варианта использования локального доступа, однако это снова возлагает на разработчика обязанность знать и понимать больше, чем им действительно нужно. .

Мысли?

Я считаю, что внедрение прокси - шаг в правильном направлении.

Я признаю, что «переадресация портов» - это совместная ответственность контейнера и оркестратора, аналогичная по духу режиму Swarm. Но всегда требуется минимальная интеграция с хостом, и при предоставлении опции типа -p (неопытному) пользователю предлагаемое решение должно быть понятным для такого человека.

Поскольку последствия -p для окружающей среды не указаны (по крайней мере, на уровне CLI, я думаю), есть возможность ввести удобную для пользователя конфигурацию того, как это работает.

Например, в настоящий момент в /etc/docker/daemon.json есть iptables /etc/docker/daemon.json который определяет, манипулировать ли DOCKER или нет. Конфигурация может быть расширена другой записью, так что комбинация типа iptables==true && expose_with_network_proxy==true будет включать поведение, подобное прокси.

В целом это должно быть безопасно для новых установок, поскольку некоторое время назад (если я правильно помню) предпочитали overlay2 aufs . И легко развертывается как таковой, поскольку обновления не затрагивают существующие файлы конфигурации, но новые установки можно запускать бесплатно.

В основном мы попали в такую ​​ситуацию из-за опасений нарушить совместимость.

Я действительно хотел бы подчеркнуть тот факт, что серьезность этой проблемы, по крайней мере, в моих глазах, больше связана с отсутствием документации, чем с текущими техническими основами -p . Да, я не верю, что текущий механизм можно назвать _безопасным_, но недостатки безопасности можно исправить, и я не в состоянии критиковать причины, которые привели к текущему сценарию. В конце концов, ретроспективный взгляд всегда 20/20, и я восхищаюсь приверженностью к обратной совместимости и готовностью решать технические проблемы, которые влечет за собой такая приверженность.

Я не понимаю, почему в документации Docker до сих пор нет ярких больших красных рамок, явно предупреждающих о побочных эффектах использования -p . Если бы я столкнулся с таким предупреждением, я вообще не думаю, что вообще был бы здесь.

Мысли?

Вариант прокси звучит разумно. Кроме того, мне нравится возможность открывать и закрывать порты всякий раз, когда это необходимо, вместо того, чтобы быть обязанным делать это при запуске docker run .

@BenjamenMeyer @jest @jacoscaz

Спасибо за дополнительный отзыв.

Спасибо, что наконец-то посмотрели на это более внимательно.

Я согласен, что знания, которые мы просим пользователей позировать в этом отношении, невелики. В настоящее время мы возлагаем на пользователя (будь то системный администратор или разработчик) ответственность за понимание того, что делает -p , и за принятие соответствующих мер предосторожности, чтобы служба была доступна только тем, кому, по их мнению, она доступна. Мы делаем еще один шаг вперед, даже ожидая, что разработчики, у которых часто нет причин знать iptables, вмешаются и исправят его для своей собственной среды (с помощью DOCKER-USER ).
В основном мы попали в такую ​​ситуацию из-за опасений нарушить совместимость.

У меня есть некоторые идеи, на которые я еще не успел полностью обдумать, но в основном это будет изменение поведения -p зависимости от версии API клиента и отдельной обработки входящего трафика. Все еще критическое изменение, которое меня беспокоит, но, по крайней мере, старое поведение сохраняется в более старых версиях API.

Мысли?

  1. Начните с обновления документации, чтобы пользователи знали о влиянии EXPOSE из всех методов (-p, Dockerfile, docker-compose.yml). Это можно сделать быстро. Это также может помочь привлечь некоторое внимание к проблеме, чтобы привлечь больше людей, предлагающих советы / опыт в поиске хорошего решения, и помочь ответить, если сообщество желает также обеспечить обратную совместимость по этому поводу.
  2. Спланируйте способ добраться из того места, где сейчас находится Docker, туда, где должен быть Docker. Это может включать в себя критическое изменение в какой-то момент.

Я думаю, что серьезность ситуации, безусловно, позволяет внести критические изменения, чтобы исправить это; просто не делайте этого, не проинформировав людей. Установите график (6 месяцев? 12 месяцев?), В течение которого изменение будет активно обсуждаться и т. Д., И вы подготовите сообщество; затем в «основной» версии внесите изменения. Исходя из схемы вашей версии, похоже, что первый набор - это год (19); учитывая, что мы находимся в конце 2019 года, используйте оставшуюся часть 2019 года, а затем 2020 год, чтобы разработать решение и рекламировать его; представьте его как дополнительную функцию где-нибудь в 2020 году, а затем продвигайте его на первое место / использование по умолчанию в 2021 году.

Версия Dockerfile и версия схемы Docker Compose также могут быть хорошими инструментами в том, что касается установки поведения по умолчанию, но я бы не стал блокировать более старые версии, чтобы они могли воспользоваться этим, и сделал бы серьезное предупреждение о необходимости обновления. в таком случае тоже.

Как бы оно ни было развернуто, я думаю, вы обнаружите, что сообщество в целом будет гораздо больше поддерживать такое изменение, если оно понимает, почему и что происходит, и не чувствует, что оно им мешает.

У меня была мысль, что мы могли бы сделать локальный прокси (ala kubectl proxy ) для варианта использования локального доступа, однако это снова возлагает на разработчика обязанность знать и понимать больше, чем им действительно нужно. .

Мне очень нравится идея прокси, и предложение @jest о том, как его включить, вполне может быть отличным методом выполнения миграции.

Честно говоря, большинство людей, которые будут стремиться раскрыть его вне системы, будут или должны быть в какой-то степени знакомы с брандмауэрами, даже если это просто то, как настроить UFW или firewalld для этого. Так что, если решение делает его доступным только для локального хоста, я думаю, что для людей приемлемо научиться использовать свои инструменты брандмауэра для перенаправления порта через брандмауэр.

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

Мне очень нравится идея прокси, и предложение @jest о том, как его включить, вполне может быть отличным методом выполнения миграции.

Честно говоря, большинство людей, которые будут стремиться раскрыть его вне системы, будут или должны быть в какой-то степени знакомы с брандмауэрами, даже если это просто то, как настроить UFW или firewalld для этого. Так что, если решение делает его доступным только для локального хоста, я думаю, что для людей приемлемо научиться использовать свои инструменты брандмауэра для перенаправления порта через брандмауэр.

Я повторяю это. Я думаю, что с помощью прокси-решения было бы возможно придумать что-то, что позволило бы пользователям привязать прокси к любой комбинации интерфейса (ов) и порта (ов), которые они хотели. Однако, если вы вынуждены пойти на компромисс во имя обратной совместимости (что я поддерживаю!) Или по любой другой причине, приоритет должен быть отдан разумным ожиданиям в области безопасности, даже за счет делегирования пользователям внесистемной уязвимости.

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

Docker обходит брандмауэр macOS так же, как и в Linux.

На моей машине разработки (Docker Desktop для Mac) я добавил "ip": "127.0.0.1" в конфигурацию демона Docker. Таким образом, любые базы данных разработки и т. Д. По умолчанию будут доступны только с localhost. В тех редких случаях, когда мне нужно сделать приложение видимым для других, я могу опубликовать его явно с помощью -p 0.0.0.0:8080:8080 .

Изменить: похоже, что Docker Compose по-прежнему привязан к 0.0.0.0 по умолчанию, независимо от конфигурации демона Docker. Так что этот трюк работает только при запуске Docker из командной строки. И в любом случае, поскольку файлы Compose часто передаются товарищам по команде, у которых может быть другая конфигурация системы, лучше явно добавить 127.0.0.1 в файл Compose.

@luontola Какое элегантное решение.

Изменить: то, что я также думал, было, возможно, способом иметь список разрешенных / заблокированных адресов, который можно было бы определить, например, в Docker compose, который затем автоматически добавлялся бы в цепочку iptables DOCKER-USER.

Небольшая маргаритка, которая подчеркивает важность этой проблемы: https://github.com/docker/for-linux/issues/810 (сводка: DOCKER-USER был удален, поэтому даже если вы настроили iptables для блокировки внешнего доступа к докерам, он проигнорирует эту дополнительную конфигурацию и покажет все контейнеры)

Комментарии @danhallin, подобные

Хм интересное чтение!

Я работаю в Mac OS X и у меня есть локальный брандмауэр LittleSnitch. Просто появилось диалоговое окно с вопросом, нормально ли 185.156.177.252 подключиться к процессу com.docker.backend. Я нажал «отрицать».

Мне было интересно, как он может открыть порт в моем маршрутизаторе, но я только что понял, что это, конечно же, делается через UPnP! Все роутеры это поддерживают.

Теперь мне интересно, почему. Почему что-то из 185.156.177.252 пытается подключиться извне? Если моему локальному процессу Docker что-то нужно, он должен звонить домой изнутри, а не открывать порт снаружи.

@ cpuguy83 все в порядке, но ничего не меняет в этом запросе.

Люди используют докер и хотят подключаться к приложениям, работающим под докером, из своей локальной системы - это чрезвычайно распространенный вариант использования для разработчиков, особенно при попытке что-то диагностировать или написании службы, которая не находится в докере, но должна подключаться к службам, размещенным в докере. (чтобы заполнить среду разработки). Разработка под Docker - не всегда хорошее, интересное или полезное занятие, вот что:

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

Это просто запрет. Не все инструменты совместимы с Docker, да и не должны. От пользователя не требуется, чтобы он полностью находился под Docker, чтобы использовать службы, работающие под Docker. Даже в этом случае, если Docker используется для управления серверами, администратор не может легко управлять ими с помощью конфигурации брандмауэра и функции Expose Port, однако она полностью нарушена (командная строка, Dockerfile, Docker Compose Config).

Кроме того, люди используют Docker Compose для управления большей частью среды и указывают через docker-compose.yml или Dockerfile что порт должен быть открыт, чтобы они могли получить к нему доступ локально. Поэтому указание параметра use the -p неверно, поскольку они никогда не взаимодействуют с командой docker напрямую таким образом, чтобы это работало.

Открытие порта не означает, что безопасность должна быть нарушена. Я обрисовал в общих чертах, как вы можете открыть порт для локальной системы, не нарушая безопасности ( # 22054 (комментарий) ), и это

Решение:

* Use Docker Network

* Expose the Ports on the Docker Network alone and local host

* Stop binding the port on 0.0.0.0 - or effectively doing so

* Require users to use their own firewall tooling to expose the port off system (ufw, firewalld, etc)

* Provide integrations for common firewalls to make this easy

Другими словами, вместо доступа к службе в контейнере докеров через 127.0.0.1:<port> требуется <docker container ip>:<service port> даже с локального хоста. Если люди хотят выставить службу вне системы, они могут добавить правило брандмауэра с помощью своих инструментов (ufw и т. Д.) Для перенаправления порта с заданного порта на <docker container ip>:<service port> .

В качестве альтернативы, следуйте подходу Kubernetes с их дизайном прокси, эффективно действуя так, как # 22054 (комментарий) . Опять же, это все равно должна быть локальная система только до тех пор, пока пользователь целенаправленно не предоставит ее внешней сети.

Самая большая проблема здесь в том, что Docker ставит под угрозу целостность брандмауэра, чтобы предоставлять свои услуги, делая это вообще без уведомления пользователя и без интеграции в инструменты пользователя, поэтому пользователь не знает об этом и не может его контролировать.

Я понимаю, что на рынке существует масса интерфейсов межсетевого экрана - даже с iptables есть firewalld, ufw и еще десяток других. Я действительно не ожидаю, что Docker будет интегрироваться с ними (хотя это было бы неплохо), но я действительно ожидаю, что Docker будет работать таким образом, чтобы не обходить их и не нарушать какую-либо безопасность, которую установил пользователь.

В качестве базового тестового примера:

* Setup a Debian-based server (Debian, Ubuntu)

* Install ufw, OpenSSH Server

* run `ufw allow OpenSSH`

* run `ufw enable`

На данный момент у вас есть довольно безопасный сервер; единственный разрешенный трафик либо (а) связан с исходящим трафиком, либо (б) трафиком SSH-сервера.

Теперь запустите док-контейнер с открытым портом.

Приемлемое решение отвечало бы следующим требованиям:

* The port should not be accessible from another computer; the firewall should continue to block it from outside systems.

* The port should be accessible from the local computer (localhost).

* Multiple docker containers should be able to expose the same port and have everything work; all containers with an exposed port should be accessible from the local system only.

* The user should not have to know about their firewall configuration details to make this happen.

Попробуйте то же самое для системы на базе CentOS / Fedora / RHEL с firewalld .

Я столкнулся с этой проблемой, когда пытался понять, есть ли способ настроить докер, чтобы он не обходил мои правила ввода брандмауэра каждый раз, когда порт публикуется. Приведенный выше комментарий хорошо объясняет все это, а также более предпочтительный, ИМО, подход.

Вчера случайно наткнулся на этот вопрос. Я потратил недели на тщательную настройку правил брандмауэра iptables для своего VPS. Я старался быть осторожным и ограничительным. Фильтр ввода и вывода по умолчанию отключен. Я явно разрешаю определенный трафик и блокирую все остальное. Трафик регистрируется, я тестировал и отслеживал.

Я знал, что Docker внес свои собственные изменения в цепочку пересылки и таблицу NAT ... Поэтому я был чрезвычайно осторожен, чтобы уважать эти изменения на протяжении всего процесса. Кроме того, все мои контейнеры подключены к пользовательским сетям. Хост-сеть по умолчанию никогда не используется. Официальная документация говорит нам не делать этого.

Моим первым сюрпризом было то, что мой контейнерный прокси-сервер Nginx был доступен для всего Интернета. В моих правилах брандмауэра есть опция, разрешающая входящий веб-трафик из мира. Но я еще не включил эту функцию. Нигде в моих iptables не очевидно, что HTTP 80/443 разрешен.

Некоторые из моих веб-приложений полагаются на такие базы данных, как MySQL. Изначально я _не_ использовал параметр -p в Docker Compose. Я знал, что в этом нет необходимости, поскольку мое веб-приложение и сервер db использовали одну и ту же определяемую пользователем сеть Docker. Но как бывший администратор базы данных я всегда думаю о резервных копиях. Поэтому я активировал параметр -p, чтобы разрешить заданиям cron моего хоста и средствам резервного копирования своевременно создавать резервные копии. Я написал еще один брандмауэр _option_, чтобы разрешить внешний доступ SQL. Но, опять же, еще не позволил этого.

После того, как я был удивлен Nginx, я мудро решил перепроверить, что MySQL тоже не открыт. Я попытался подключиться (со своего ноутбука) к базе данных MySQL на моем удаленном VPS. И снова был в шоке, когда сразу успешно подключился.

Все, что я говорю, не обсуждалось подробно в предыдущих постах. Большое спасибо @BenjamenMeyer @jest @jacoscaz за их полезные исследования и предложения. Но для тех, кому нужны современные варианты использования и опыт? Вот оно. Спустя более 4 лет после начала этой беседы люди вроде меня все еще сталкиваются с таким поведением. И все еще уходит в шоке.

Да, есть много обходных путей. Я сразу займусь несколькими. Но для их реализации вы должны знать, что эта проблема существует. Вот что меня больше всего разочаровывает. Не то чтобы разработчики Docker принимали определенные дизайнерские решения, хорошие или плохие.

Но это «поведение обхода брандмауэра» явно не объявляется, не предупреждается и не документируется более широко. Когда дело доходит до сетевой безопасности, сюрпризы никогда не приносят пользы.

Хотел поделиться своим решением этой проблемы, если другие сочтут это полезным. Использование iptables и ipset. Протестировано на Ubuntu, CentOS 7 и RHEL 7. Используется ipset, поскольку «доверенные» IP-адреса не всегда находятся в одном и том же диапазоне IP-адресов (исправьте ограничение iptables).
Он работает с обычным Docker и Docker Swarm (он же SwarmKit).
Это обеспечивает безопасность по умолчанию, позволяя только указанным вами IP-адресам подключаться к портам ОС и портам Docker (с использованием iptables и ipset), которые должны быть открыты. Также есть возможность сделать порт ОС или порт Docker "общедоступным" (открытым для всех IP-адресов).

Ansible не требуется, но упрощает работу .. Роль Ansible: https://github.com/ryandaniels/ansible-role-iptables-docker
Ручные шаги для CentOS / RHEL:
https://github.com/ryandaniels/ansible-role-iptables-docker#manual -commands-centosrhel
И Ubuntu 18.04 / 20.04:
https://github.com/ryandaniels/ansible-role-iptables-docker#manual -commands-ubuntu-2004

Вам нужно будет установить / настроить ipset в дополнение к iptables (см. Ссылки выше, если возникнут проблемы).
Пример конфигурации iptables (с открытым для всех ssh-портом 22):

*filter
:DOCKER-USER - [0:0]
:FILTERS - [0:0]
#Can't flush INPUT. wipes out docker swarm encrypted overlay rules
#-F INPUT
#Use ansible or run manually once instead to add -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
#Below Docker ports open to everyone if uncommented
#-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
#Because Docker Swarm encrypted overlay network just appends rules to 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
#Below OS ports open to everyone if uncommented
-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
Была ли эта страница полезной?
0 / 5 - 0 рейтинги