Одним из основных нововведений, появившихся в Podman, стала возможность запуска контейнеров без прав root.
С точки зрения безопасности это было существенным улучшением, поскольку потенциально скомпрометированный контейнер, запущенный от имени root, представляет собой угрозу безопасности для хост-системы.
Чтобы добиться аналогичного поведения, последние версии Docker поддерживают запуск демона docker в пользовательском контексте.
🐳 Почему процессы в контейнерах Docker не должны запускаться от имени Root
Запуск непривилегированных контейнеров, хотя и является более безопасным, имеет и свои недостатки, например, невозможность привязки к привилегированным портам хоста.
В этом руководстве мы узнаем, как разрешить контейнеру Docker/Podman без рута привязываться к привилегированному порту хоста на Linux.
- Введение
- Перенаправление привилегированного порта на непривилегированный с помощью правила брандмауэра
- Создание правила с помощью firewalld
- Создание правила с помощью утилит iptables или nft
- Перенаправление порта с помощью redir
- Сопоставление непривилегированного порта с контейнером
- Установка возможности CAP_NET_BIND_SERVICE
- Изменение диапазона непривилегированного порта
- Заключение
Введение
Поскольку мы запустили команду от имени непривилегированного пользователя, то получили следующую ошибку (для удобства вывод урезан):
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, для этого нужно выполнить команду:
Обратите внимание на два момента: во-первых, приведенная выше команда добавляет правило в зону firewalld “default”, поскольку мы не указали ее явно (зоны – это абстракции, связанные с firewalld. Если вы хотите узнать о них больше, обратитесь к нашему учебнику по firewalld).
Во-вторых, правило не является постоянным и не переживет перезагрузки или перезапуска службы firewalld.
Для того чтобы сделать его постоянным, необходимо использовать опцию –permanent и перезапустить брандмауэр, выполнив команду:
Создание правила с помощью утилит iptables или nft
Синтаксис, используемый для задания прямых правил firewalld, как вы видите выше, похож на синтаксис, используемый в iptables.
Если вам (как и мне) это больше нравится, то можно задать правило, используя непосредственно утилиты iptables/ip6tables.
Поскольку в современных дистрибутивах Linux на смену фреймворку iptables пришел nftables, эти утилиты теперь являются просто ссылками на iptables-nft – обертку, которая позволяет нам продолжать использовать старый синтаксис:
Наконец, чтобы задать такое же перенаправление с помощью собственного синтаксиса nftables, мы можем воспользоваться утилитой nft и выполнить следующую команду:
Перенаправление порта с помощью redir
Альтернативой созданию правила брандмауэра является использование специализированного инструмента, такого как redir, который имеет открытый исходный код и доступен в стандартных репозиториях всех основных дистрибутивов Linux.
На Fedora мы можем установить его следующей командой:
В дистрибутивах Debian и Debian-based можно использовать:
Redir способен перенаправлять TCP-соединения, приходящие на локальный порт, на определенную комбинацию <адрес>:порт. Часть <адрес> необязательна: если она опущена, то используется адрес 0.0.0.0 (мета-адрес) (он представляет все IPv4-адреса на машине).
Чтобы перенаправить порт 80 на порт 8080 с помощью redir, выполним команду:
Перенаправления, заданные с помощью 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.
Чтобы запустить и включить службу при загрузке, выполним команду:
Сопоставление непривилегированного порта с контейнером
После того как мы перенаправили трафик порта, мы можем запустить контейнер и привязать его к непривилегированному порту:
Установка возможности CAP_NET_BIND_SERVICE
Альтернативой перенаправления трафика портов только для docker является установка возможности CAP_NET_BIND_SERVICE для бинарного файла /usr/bin/rootlesskit.
Возможности – это специальные разрешения, которые могут быть назначены процессам в Linux.
В данном случае нас интересует именно CAP_NET_BIND_SERVICE, поскольку она позволяет привязать сокет к привилегированному порту.
Для назначения возможности CAP_NET_BIND_SERVICE двоичному файлу rootlesskit выполним следующую команду:
Чтобы удалить эту возможность:
Изменение диапазона непривилегированного порта
Последняя стратегия, которую мы здесь рассмотрим, заключается в изменении диапазона привилегированных портов.
Чтобы проверить, какие порты система считает привилегированными, достаточно прочитать файл /proc/sys/net/ipv4/ip_unprivileged_port_start: он содержит первый непривилегированный порт в системе (по умолчанию 1024).
Продолжая предыдущий пример, для того чтобы система считала порт 80 непривилегированным, запишем в этот файл соответствующее значение:
Чтобы сделать это изменение постоянным, необходимо добавить следующую строку в конфигурационный файл в каталоге /etc/sysctl.d/:
Обратите внимание, что при использовании этой стратегии система будет рассматривать все порты >= 80 как непривилегированные!
Заключение
В этом руководсте мы рассмотрели некоторые методы, которые можно использовать для того, чтобы разрешить контейнеру без корня привязываться к привилегированному порту.
Мы рассмотрели, как перенаправить трафик с привилегированного порта на непривилегированный, как установить характеристику CAP_NET_BIND_SERVICE, и, наконец, как изменить диапазон привилегированных портов.