🐳 Как привязать контейнер без пользователя root к привилегированному порту на Linux |

🐳 Как привязать контейнер без пользователя root к привилегированному порту на Linux

Мануал

Одним из основных нововведений, появившихся в Podman, стала возможность запуска контейнеров без прав root.

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

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

🐳 Почему процессы в контейнерах Docker не должны запускаться от имени Root

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

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

🐳 Что такое Docker без root (rootless)?

Введение

Запуск контейнеров без рута хорош с точки зрения безопасности, но накладывает свои ограничения.
Предположим, мы используем Docker (или Podman) в режиме rootless и хотим запустить контейнер, содержащий веб-сервер Apache.
Для того чтобы сделать сервис “прозрачным” для пользователя, мы хотим прописать порт 80 на хосте на тот же порт, открываемый контейнером, и запустить контейнер таким образом:
docker run -p 80:80 httpd:latest

Поскольку мы запустили команду от имени непривилегированного пользователя, то получили следующую ошибку (для удобства вывод урезан):

error while calling PortManager.AddPort(): cannot expose privileged port 80

Команда не выполнилась, так как не удалось привязаться к порту 80 на хосте, поскольку он является привилегированным.

Как решить эту проблему?

Существует множество подходов, рассмотрим некоторые из них.

Перенаправление привилегированного порта на непривилегированный с помощью правила брандмауэра

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

Чтобы перенаправить один порт на другой, можно создать правило брандмауэра.

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

Компонент ядра Linux, обеспечивающий функциональность работы с пакетами, – это “netfilter“.

В современных дистрибутивах Linux для “общения” с этим компонентом из пользовательского пространства используется фреймворк “nftables“.

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

Одним из примеров такой утилиты является firewalld.

Создание правила с помощью firewalld

Если вы читали нашу вводную статью о firewalld, то уже знаете, что управление им осуществляется с помощью утилиты firewall-cmd, которая не имеет собственного способа задать правило перенаправления портов.

Для этого нам приходится “возвращаться” к “прямому” правилу, которое позволяет взаимодействовать с брандмауэром на более низком уровне.

Чтобы задать “прямое” правило, мы запускаем утилиту с опцией –direct.

Предположим, что мы хотим перенаправить трафик с порта 80 на порт 8080 на localhost, для этого нужно выполнить команду:

sudo firewall-cmd --direct --add-rule ipv4 nat OUTPUT 0 -p tcp --dport=80 -o lo -j REDIRECT --to-port=8080

Обратите внимание на два момента: во-первых, приведенная выше команда добавляет правило в зону firewalld “default”, поскольку мы не указали ее явно (зоны – это абстракции, связанные с firewalld. Если вы хотите узнать о них больше, обратитесь к нашему учебнику по firewalld).

Во-вторых, правило не является постоянным и не переживет перезагрузки или перезапуска службы firewalld.

Для того чтобы сделать его постоянным, необходимо использовать опцию –permanent и перезапустить брандмауэр, выполнив команду:

sudo firewall-cmd --reload

Создание правила с помощью утилит iptables или nft

Синтаксис, используемый для задания прямых правил firewalld, как вы видите выше, похож на синтаксис, используемый в iptables.

Если вам (как и мне) это больше нравится, то можно задать правило, используя непосредственно утилиты iptables/ip6tables.

Поскольку в современных дистрибутивах Linux на смену фреймворку iptables пришел nftables, эти утилиты теперь являются просто ссылками на iptables-nft – обертку, которая позволяет нам продолжать использовать старый синтаксис:

sudo iptables -t nat -A OUTPUT -o lo -p tcp --dport=80 -j REDIRECT --to-port=8080

Наконец, чтобы задать такое же перенаправление с помощью собственного синтаксиса nftables, мы можем воспользоваться утилитой nft и выполнить следующую команду:

sudo nft 'add rule ip nat OUTPUT oifname "lo" tcp dport 80 counter redirect to :8080'

Перенаправление порта с помощью redir

Альтернативой созданию правила брандмауэра является использование специализированного инструмента, такого как redir, который имеет открытый исходный код и доступен в стандартных репозиториях всех основных дистрибутивов Linux.

На Fedora мы можем установить его следующей командой:

sudo dnf install redir

В дистрибутивах Debian и Debian-based можно использовать:

sudo apt install redir

Redir способен перенаправлять TCP-соединения, приходящие на локальный порт, на определенную комбинацию <адрес>:порт. Часть <адрес> необязательна: если она опущена, то используется адрес 0.0.0.0 (мета-адрес) (он представляет все IPv4-адреса на машине).

Чтобы перенаправить порт 80 на порт 8080 с помощью redir, выполним команду:

sudo redir :80 127.0.0.1:8080

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

Первая используется для запуска приложения в фоновом режиме, вторая – для перенаправления журналов в syslog.

Практически все дистрибутивы Linux в настоящее время используют Systemd в качестве системы инициализации и менеджера сервисов.

Приведем минимальный пример того, как может выглядеть сервисная единица systemd, запускающая redir при загрузке:

[Unit]
Description=Redirect tcp port 80 to 8080 with redir

[Service]
ExecStart=/bin/redir -sn :80 127.0.0.1:8080

[Install]
WantedBy=multi-user.target

Мы можем сохранить блок под именем /etc/systemd/system/redir.service.

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

sudo systemctl enable --now redir.service

Сопоставление непривилегированного порта с контейнером

После того как мы перенаправили трафик порта, мы можем запустить контейнер и привязать его к непривилегированному порту:

docker run --name test_httpd -p 8080:80 httpd:latest

Установка возможности CAP_NET_BIND_SERVICE

Альтернативой перенаправления трафика портов только для docker является установка возможности CAP_NET_BIND_SERVICE для бинарного файла /usr/bin/rootlesskit.

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

В данном случае нас интересует именно CAP_NET_BIND_SERVICE, поскольку она позволяет привязать сокет к привилегированному порту.

Для назначения возможности CAP_NET_BIND_SERVICE двоичному файлу rootlesskit выполним следующую команду:

sudo setcap cap_net_bind_service=ep /usr/bin/rootlesskit

Чтобы удалить эту возможность:

sudo setcap -r /usr/bin/rootlesskit

Изменение диапазона непривилегированного порта

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

Чтобы проверить, какие порты система считает привилегированными, достаточно прочитать файл /proc/sys/net/ipv4/ip_unprivileged_port_start: он содержит первый непривилегированный порт в системе (по умолчанию 1024).

Продолжая предыдущий пример, для того чтобы система считала порт 80 непривилегированным, запишем в этот файл соответствующее значение:

echo 80 | sudo tee /proc/sys/net/ipv4/ip_unprivileged_port_start

Чтобы сделать это изменение постоянным, необходимо добавить следующую строку в конфигурационный файл в каталоге /etc/sysctl.d/:

echo net.ipv4.ip_unprivileged_port_start = 80 | sudo tee /etc/sysctl.d/90-unprivileged_port_start.conf

Обратите внимание, что при использовании этой стратегии система будет рассматривать все порты >= 80 как непривилегированные!

Заключение

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

Мы рассмотрели, как перенаправить трафик с привилегированного порта на непривилегированный, как установить характеристику CAP_NET_BIND_SERVICE, и, наконец, как изменить диапазон привилегированных портов.

 

Пожалуйста, не спамьте и никого не оскорбляйте. Это поле для комментариев, а не спамбокс. Рекламные ссылки не индексируются!
Добавить комментарий