Кроки для обмеження зовнішніх з'єднань до контейнера докера з iptables?


20

Моя мета - обмежити доступ до контейнерів докера лише кількома публічними IP-адресами. Чи є простий, повторюваний процес для досягнення моєї мети? Розуміючи лише основи iptables під час використання параметрів за замовчуванням Docker, мені це дуже важко.

Я хотів би запустити контейнер, зробити його видимим для загальнодоступного Інтернету, але дозволити з'єднання лише від вибраних хостів. Я очікував би встановити політику INPUT за замовчуванням REJECT, а потім дозволити лише з'єднання від своїх хостів. Але правила NAT і ланцюги Docker заважають, і мої правила INPUT ігноруються.

Чи може хтось навести приклад того, як досягти своєї мети, враховуючи наступні припущення?

  • Приймаюча громадськість IP 80.80.80.80 на eth0
  • Приймає приватний IP 192.168.1.10 на eth1
  • docker run -d -p 3306:3306 mysql
  • Блокуйте всі з'єднання з хостом / контейнером 3306, крім хостів 4.4.4.4 та 8.8.8.8

Я радий прив’язати контейнер лише до локальної ip-адреси, але мені знадобляться інструкції, як правильно встановити правила пересилання iptables, які переживуть процес докера та перезапуск хоста.

Спасибі!

Відповіді:


15

Дві речі, які слід пам’ятати, працюючи з правилами брандмауера Docker:

  1. Щоб уникнути того, щоб ваші правила були докеріровані докер, використовуйте DOCKER-USERланцюжок
  2. Докер робить порт-карти у PREROUTINGланцюжку natтаблиці. Це відбувається перед filterправилами, тому --destі --dportпобачать внутрішній IP та порт контейнера. Для доступу до оригінального пункту призначення можна скористатися -m conntrack --ctorigdstport.

Наприклад:

iptables -A DOCKER-USER -i eth0 -s 8.8.8.8 -p tcp -m conntrack --ctorigdstport 3306 --ctdir ORIGINAL -j ACCEPT
iptables -A DOCKER-USER -i eth0 -s 4.4.4.4 -p tcp -m conntrack --ctorigdstport 3306 --ctdir ORIGINAL -j ACCEPT
iptables -A DOCKER-USER -i eth0 -p tcp -m conntrack --ctorigdstport 3306 --ctdir ORIGINAL -j DROP

ПРИМІТКА. Без --ctdir ORIGINALцього це також відповідатиме пакетам відповідей, що повертаються для з'єднання з контейнера до порту 3306 на якомусь іншому сервері, що майже точно не є тим, що вам потрібно! Це вам зовсім не потрібно, якщо, як я, ваше перше правило -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT, оскільки це стосуватиметься всіх пакетів відповідей, але було б безпечніше все-таки використовувати --ctdir ORIGINAL.


Чи слід це редагувати, щоб включити --ctdir ? Я використовую-m conntrack --ctstate NEW --ctorigdstport 3306 --ctdir ORIGINAL
lonix

@Ionix, так, хоча, хоча я тільки що розробив, чому це мене бентежить. Я додав трохи пояснень.
SystemParadox

1
Зверніть увагу , що за замовчуванням DOCKER-USERтаблиця містить запис: -A DOCKER-USER -j RETURNяка буде працювати до вище , якщо ви використовуєте -A. Рішення - вставити правила на чолі у зворотному порядку з -I.
BMitch

@BMitch Або ще краще , додайте всі правила в новий FILTERSланцюжок і -Iвставте нові правила (як ви сказали), щоб перейти до нього: -I INPUT -j FILTERSі-I DOCKER-USER -i eth0 -j FILTERS
lonix

@BMitch Однак я щойно перевірив свій сервер, і правила повернення немає, можливо, остання версія докера вже не вставляє його? Хороша ідея використовувати, -Iхоча, щоб бути безпечною.
lonix

8

З Docker v.17.06 з'явився новий ланцюжок iptables під назвою DOCKER-USER. Це для ваших спеціальних правил: https://docs.docker.com/network/iptables/

На відміну від ланцюга DOCKER, він не скидається при складанні / запуску контейнерів. Таким чином, ви можете додати ці рядки до iptables config / script для забезпечення сервера ще до встановлення докера та запуску контейнерів:

-N DOCKER
-N DOCKER-ISOLATION
-N DOCKER-USER
-A DOCKER-ISOLATION -j RETURN
-A DOCKER-USER -i eth0 -p tcp -m tcp --dport 3306 -j DROP
-A DOCKER-USER -j RETURN

Тепер порт для MySQL заблокований від зовнішнього доступу (eth0), навіть думка докер відкриває порт для світу. (Ці правила передбачають, що ваш зовнішній інтерфейс - eth0.)

Врешті-решт, вам доведеться спочатку очистити iptables, щоб перезапустити службу докера, якщо ви заблукали її, намагаючись заблокувати порт, як я.


Я сумую, чому ця таблиця DOCKER-USER відрізняється від будь-якої іншої таблиці, доданої користувачем. У ній не застосовується попередній фільтр, тому вам все одно доведеться самостійно вказувати імена інтерфейсу. Якщо ви створите "МОЙ-СХЕЙ" і вставите його в ланцюг ВПЕРЕД, це матиме такий же результат, ні?
ColinM

Так, це має значення, адже Docker вставляє ланцюг DOCKER-USER у ланцюг FORWARD: -A FORWARD -j DOCKER-USER -A FORWARD -j DOCKER-ISOLATION Ось чому, власні інструкції виконуються перед ланцюгом DOCKER.
ck1

Зауважте, що якщо ви використовуєте --dportвсередині DOCKER-USER, це повинно відповідати внутрішньому IP-службі контейнерної служби, а не відкритому порту. Вони часто відповідають, але не завжди, і це може легко конфліктувати з іншими службами, тому я все ще стверджую, що це рішення DOCKER-USER наполовину випечене.
ColinM

4

ОНОВЛЕННЯ : Хоча це дійсно в 2015 році, це рішення вже не є правильним способом.

Здається, відповідь міститься в документації Докера за адресою https://docs.docker.com/articles/networking/#the-world

Правила переадресації Докера дозволяють використовувати всі IP-адреси зовнішніх джерел за замовчуванням. Щоб дозволити доступ до контейнерів лише певному IP або мережі, вставте заперечне правило у верхній частині ланцюжка фільтрів DOCKER. Наприклад, щоб обмежити зовнішній доступ таким чином, що тільки джерело IP 8.8.8.8 може отримати доступ до контейнерів, можна додати таке правило:iptables -I DOCKER -i ext_if ! -s 8.8.8.8 -j DROP

Що я в кінцевому підсумку робив:

iptables -I DOCKER -i eth0 -s 8.8.8.8 -p tcp --dport 3306 -j ACCEPT
iptables -I DOCKER -i eth0 -s 4.4.4.4 -p tcp --dport 3306 -j ACCEPT
iptables -I DOCKER 3 -i eth0 -p tcp --dport 3306 -j DROP

Я не торкався --iptablesабо --iccпараметрів.


1
Якщо це так iptables -vnL DOCKER, порти призначення - це всі порти в контейнері. Якщо я маю на це право, це означає, що правила, наведені вище, впливали б лише на порт 3306усередині контейнера - тобто, якби ви були до -p 12345:3306свого контейнера, ваше правило все одно буде тим, яке потрібно блокувати доступ (тобто --dport 12345не працює) , оскільки правила ACCEPT ланцюга DOCKER мають пост-NAT.
сонця

Правильно, правила повинні стосуватися портів у контейнерах.
GGGforce

1
Хам, це щось некрасиво, якщо вам трапляється запускати кілька контейнерів, які використовують, скажімо, внутрішній NGINX для здійснення зворотного проксі (наприклад, Zabbix, користувацький балансир завантаження тощо), оскільки це вимагатиме від вас заздалегідь знати IP-адресу контейнера. Я все ще шукаю рішення тієї проблеми, яка не потребує --iptables=false, бо це, здається, найгірший вибір із них.
вранці

Дякую! Ви вирішили мою проблему після багатьох годин пошуку. Тепер я, нарешті, зможу заарештувати MySQL лише на свою домашню IP-адресу, не піддаючи м'якого підшерстя на весь світ.
Метт Кавана

1
Ланцюжок DOCKER не повинен безпосередньо маніпулювати користувачем! Використовуйте для цього ланцюжок DOCKER-USER. Перевірте прийняту відповідь.
Пол-Себастьян Маноле

3

ОНОВЛЕННЯ: Хоча ця відповідь все ще є дійсною, відповідь від @SystemParadox, яка використовується DOCKER-USERв поєднанні з --ctorigdstport, краще.

Ось рішення, яке добре зберігається між перезавантаженнями і дозволяє впливати на відкритий порт, а не на внутрішній порт.

iptables -t mangle -N DOCKER-mysql iptables -t mangle -A DOCKER-mysql -s 22.33.44.144/32 -j RETURN iptables -t mangle -A DOCKER-mysql -s 22.33.44.233/32 -j RETURN iptables -t mangle -A DOCKER-mysql -j DROP iptables -t mangle -A PREROUTING -i eth0 -p tcp -m tcp --dport 3306 -j DOCKER-mysql

Я створив зображення Docker, який використовує цей метод для автоматичного керування iptables для вас, використовуючи змінні середовища або динамічно за допомогою etcd (або обох):

https://hub.docker.com/r/colinmollenhour/confd-firewall/

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.