Добавлен перевод статьи "IP Accounting and Access Lists with systemd"

This commit is contained in:
nnz1024
2017-10-28 00:14:16 +03:00
parent fb2a8ab3cd
commit d7289115f5

601
s4a.tex
View File

@@ -2766,7 +2766,7 @@ PrivateNetwork=yes
котором настраивается только интерфейс обратной петли. котором настраивается только интерфейс обратной петли.
\subsection{Предоставление службам независимых каталогов \texttt{/tmp}} \subsection{Предоставление службам независимых каталогов \texttt{/tmp}}
\label{sec:privatetmp} \label{ssec:privatetmp}
Еще одна простая, но мощная опция настройки служб~--- +PrivateTmp=+: Еще одна простая, но мощная опция настройки служб~--- +PrivateTmp=+:
\begin{Verbatim} \begin{Verbatim}
@@ -3781,6 +3781,7 @@ $ journalctl /usr/sbin/vpnc /usr/sbin/dhclient
Отлично, мы нашли причину проблемы! Отлично, мы нашли причину проблемы!
\subsection{Продвинутые методы выборки} \subsection{Продвинутые методы выборки}
\label{ssec:metadata}
Да, это все, конечно, здорово, но попробуем подняться еще на ступеньку выше. Да, это все, конечно, здорово, но попробуем подняться еще на ступеньку выше.
Чтобы понять описанные ниже приемы, нужно знать, что systemd добавляет к Чтобы понять описанные ниже приемы, нужно знать, что systemd добавляет к
@@ -5402,7 +5403,7 @@ systemd реализует одновременно обе стратегии,
Для самой службы создается пространство имен монтирования (mount Для самой службы создается пространство имен монтирования (mount
namespace), в котором эти подкаталоги bind-монтируются в +/tmp+ namespace), в котором эти подкаталоги bind-монтируются в +/tmp+
и +/var/tmp+ соответственно. См. также раздел и +/var/tmp+ соответственно. См. также раздел
\ref{sec:privatetmp}.}, причем их жизненный цикл привязан к \ref{ssec:privatetmp}.}, причем их жизненный цикл привязан к
жизненному циклу службы: при остановке службы удаляется жизненному циклу службы: при остановке службы удаляется
не~только ее пользователь, но и ее временные каталоги. (Опять же не~только ее пользователь, но и ее временные каталоги. (Опять же
замечу, что эту директиву имеет смысл применять и без замечу, что эту директиву имеет смысл применять и без
@@ -5945,6 +5946,602 @@ DynamicUser=yes
На сегодня все! На сегодня все!
\sectiona{Учет и фильтрация IP-трафика служб}
\emph{Коротко о главном: теперь systemd может подсчитывать и фильтровать
по подсетям IP-трафик любой службы.}
В недавно вышедшем выпуске
\href{https://lists.freedesktop.org/archives/systemd-devel/2017-October/039589.html}%
{systemd 235}, помимо расширения поддержки динамических пользователей
(рассмотренной в предыдущей статье), добавлена еще одна важная возможность:
учет и фильтрация IP-трафика.
systemd и раньше поддерживал механизмы управления ресурсами, доступными юнитам:
процессорным временем, дисковым вводом-выводом, потреблением памяти, количеством
запускаемых процессов. А в версии 235 был добавлен контроль над еще одним видом
ресурсов~--- IP-трафиком.
Для этого были введены три новых директивы конфигурации юнитов:
\begin{enumerate}
\item \hreftt{https://www.freedesktop.org/software/systemd/man/systemd.resource-control.html\#IPAccounting=}%
{IPAccounting=}~--- булева переменная, позволяющая включить
подсчет IP-трафика, принятого и полученного службой (как
количества пакетов, так и их суммарного объема в байтах).
\item \hreftt{https://www.freedesktop.org/software/systemd/man/systemd.resource-control.html\#IPAddressAllow=ADDDRESS[/PREFIXLENGTH]\%E2\%80\%A6}%
{IPAddressDeny=} указывает <<черный>> список адресов подсетей
или хостов для службы. Весь трафик, отправляемый на эти адреса
процессами службы, а также полученный ими с этих адресов,
будет заблокирован.
\item \hreftt{https://www.freedesktop.org/software/systemd/man/systemd.resource-control.html\#IPAddressAllow=ADDDRESS[/PREFIXLENGTH]\%E2\%80\%A6}%
{IPAddressAllow=} имеет противоположный смысл~--- процессы
службы могут обмениваться трафиком с перечисленными в этой
директиве адресами, даже если они пересекаются с тем, что задано
в +IPAddressDeny=+ (т.е. белый список имеет приоритет над
черным).
\end{enumerate}
Эти три опции предоставляют интерфейс к новой функциональности ядра Linux,
добавленной в выпуске 4.11, а именно, eBPF-хукам контрольных групп. Основную
работу берет на себя ядро, а systemd лишь обеспечивает возможность настройки
этих механизмов. Обратите внимание, что cgroup/eBPF никак не~относятся к
традиционному брандмауэру Linux~--- NetFilter/+iptables+. Вы можете использовать
любую из этих технологий, или обе сразу, или вообще не~пользоваться ни~одной из
них.
\subsectiona{Учет IP-трафика}
Давайте посмотрим, как работает учет трафика. Создадим тестовую службу
+/etc/systemd/system/ip-accounting-test.service+:
\begin{Verbatim}
[Service]
ExecStart=/usr/bin/ping 8.8.8.8
IPAccounting=yes
\end{Verbatim}
Этот простой юнит вызывает команду
\hreftt{http://man7.org/linux/man-pages/man8/ping.8.html}{ping(8)}, которая
отправляет серию ICMP эхо-запросов на IP-адрес 8.8.8.8 (это адрес DNS-сервера
Google; он используется в нашем примере потому, что его легко запомнить, он
отовсюду доступен и отвечает на эхо-запросы; вы можете использовать любой другой
адрес, отвечающий на пинги). Опция +IPAccounting=yes+ включает учет IP-трафика
для нашей службы.
Запустим нашу службу и посмотрим ее состояние:
\begin{Verbatim}
# systemctl daemon-reload
# systemctl start ip-accounting-test
# systemctl status ip-accounting-test
ip-accounting-test.service
Loaded: loaded (/etc/systemd/system/ip-accounting-test.service; static; vendor preset: disabled)
Active: active (running) since Mon 2017-10-09 18:05:47 CEST; 1s ago
Main PID: 32152 (ping)
IP: 168B in, 168B out
Tasks: 1 (limit: 4915)
CGroup: /system.slice/ip-accounting-test.service
└─32152 /usr/bin/ping 8.8.8.8
Okt 09 18:05:47 sigma systemd[1]: Started ip-accounting-test.service.
Okt 09 18:05:47 sigma ping[32152]: PING 8.8.8.8 (8.8.8.8) 56(84) bytes of data.
Okt 09 18:05:47 sigma ping[32152]: 64 bytes from 8.8.8.8: icmp_seq=1 ttl=59 time=29.2 ms
Okt 09 18:05:48 sigma ping[32152]: 64 bytes from 8.8.8.8: icmp_seq=2 ttl=59 time=28.0 ms
\end{Verbatim}
Как видим, программа +ping+ сейчас работает. Судя по логу в конце вывода
+systemctl status+, она как раз закончила второй цикл пинга. Однако, для нас
сейчас более интересна строка выше, начинающаяся с +IP:+ и содержащая счетчики
принятых и полученных байт IP-трафика. Сайчас она показывает, что было
отправлено 168 байт, и ровно столько же принято. Это вполне предсказуемо: ICMP
эхо-запросы и эхо-ответы должны иметь одинаковый размер\footnote{Прим. перев.:
Если не~считать заголовков IP и ICMP, имеющих фиксированную длину, размер пакета
определяется только телом сообщения (payload), которое должно полностью
копироваться из запроса в ответ.}. Обратите внимание, что эта строка появляется
только для юнитов с включенной опцией +IPAccounting=+. Если она выключена,
трафик юнита не~подсчитывается\footnote{Прим. перев.: Можно заметить, что для
приведенного в примере юнита подсчитывается не~только трафик, но и количество
процессов/потоков (строка +Tasks:+). Это связано с введенным лимитом на
их количество
\hreftt{https://www.freedesktop.org/software/systemd/man/systemd.resource-control.html\#TasksMax=N}%
{TasksMax=4915} (+TasksMax=+ автоматически включает
\hreftt{https://www.freedesktop.org/software/systemd/man/systemd.resource-control.html\#TasksAccounting=}%
{TasksAccounting=}).}.
Подождем немного, и вызовем +systemctl status+ снова:
\begin{Verbatim}
# systemctl status ip-accounting-test
ip-accounting-test.service
Loaded: loaded (/etc/systemd/system/ip-accounting-test.service; static; vendor preset: disabled)
Active: active (running) since Mon 2017-10-09 18:05:47 CEST; 4min 28s ago
Main PID: 32152 (ping)
IP: 22.2K in, 22.2K out
Tasks: 1 (limit: 4915)
CGroup: /system.slice/ip-accounting-test.service
└─32152 /usr/bin/ping 8.8.8.8
Okt 09 18:10:07 sigma ping[32152]: 64 bytes from 8.8.8.8: icmp_seq=260 ttl=59 time=27.7 ms
Okt 09 18:10:08 sigma ping[32152]: 64 bytes from 8.8.8.8: icmp_seq=261 ttl=59 time=28.0 ms
Okt 09 18:10:09 sigma ping[32152]: 64 bytes from 8.8.8.8: icmp_seq=262 ttl=59 time=33.8 ms
Okt 09 18:10:10 sigma ping[32152]: 64 bytes from 8.8.8.8: icmp_seq=263 ttl=59 time=48.9 ms
Okt 09 18:10:11 sigma ping[32152]: 64 bytes from 8.8.8.8: icmp_seq=264 ttl=59 time=27.2 ms
Okt 09 18:10:12 sigma ping[32152]: 64 bytes from 8.8.8.8: icmp_seq=265 ttl=59 time=27.0 ms
Okt 09 18:10:13 sigma ping[32152]: 64 bytes from 8.8.8.8: icmp_seq=266 ttl=59 time=26.8 ms
Okt 09 18:10:14 sigma ping[32152]: 64 bytes from 8.8.8.8: icmp_seq=267 ttl=59 time=27.4 ms
Okt 09 18:10:15 sigma ping[32152]: 64 bytes from 8.8.8.8: icmp_seq=268 ttl=59 time=29.7 ms
Okt 09 18:10:16 sigma ping[32152]: 64 bytes from 8.8.8.8: icmp_seq=269 ttl=59 time=27.6 ms
\end{Verbatim}
Как видно из вывода, после 269 пингов счетчики достигли значения 22 килобайт.
Несмотря на то, что +systemctl status+ показывает только счетчики байт,
ведется также учет и количества пакетов. Для просмотра этих значений можно
использовать низкоуровневую команду +systemctl show+:
\begin{Verbatim}
# systemctl show ip-accounting-test -p IPIngressBytes -p IPIngressPackets \
-p IPEgressBytes -p IPEgressPackets
IPIngressBytes=37776
IPIngressPackets=449
IPEgressBytes=37776
IPEgressPackets=449
\end{Verbatim}
Разумеется, эта информация доступна также и через API D-Bus. Если вы хотите
автоматизировать обработку таких данных, использование вызовов D-Bus будет
гораздо удобнее, чем разбор вывода +systemctl show+.
Теперь остановим нашу службу:
\begin{Verbatim}
# systemctl stop ip-accounting-test
\end{Verbatim}
После остановки службы, для которой включен учет потребления ресурсов, в
системный журнал добавляется запись с итоговой суммой потребленных службой
ресурсов. Просмотреть ее можно командой +journalctl+:
\begin{Verbatim}[fontsize=\small]
# journalctl -u ip-accounting-test -n 5
-- Logs begin at Thu 2016-08-18 23:09:37 CEST, end at Mon 2017-10-09 18:17:02 CEST. --
Okt 09 18:15:50 sigma ping[32152]: 64 bytes from 8.8.8.8: icmp_seq=603 ttl=59 time=26.9 ms
Okt 09 18:15:51 sigma ping[32152]: 64 bytes from 8.8.8.8: icmp_seq=604 ttl=59 time=27.2 ms
Okt 09 18:15:52 sigma systemd[1]: Stopping ip-accounting-test.service...
Okt 09 18:15:52 sigma systemd[1]: Stopped ip-accounting-test.service.
Okt 09 18:15:52 sigma systemd[1]: ip-accounting-test.service: Received 49.5K IP traffic, sent 49.5K IP traffic
\end{Verbatim}
Последняя строка~--- и есть та самая запись о потребленных ресурсах. На самом
деле, эта запись является структурной\footnote{Прим. перев.: Подробнее о
структуре записей Journal можно почитать в разделе~\ref{ssec:metadata}.}, и
содержит поля метаданных, в которых приводится более полная информация:
\begin{Verbatim}[fontsize=\small]
# journalctl -u ip-accounting-test -n 1 -o verbose
-- Logs begin at Thu 2016-08-18 23:09:37 CEST, end at Mon 2017-10-09 18:18:50 CEST. --
Mon 2017-10-09 18:15:52.649028 CEST [s=89a2cc877fdf4dafb2269a7631afedad;i=14d7;b=4c7e7adcba0c45b69d612857270716d3;m=137592e75e;t=55b1f81298605;x=c3c9b57b28c9490e]
PRIORITY=6
_BOOT_ID=4c7e7adcba0c45b69d612857270716d3
_MACHINE_ID=e87bfd866aea4ae4b761aff06c9c3cb3
_HOSTNAME=sigma
SYSLOG_FACILITY=3
SYSLOG_IDENTIFIER=systemd
_UID=0
_GID=0
_TRANSPORT=journal
_PID=1
_COMM=systemd
_EXE=/usr/lib/systemd/systemd
_CAP_EFFECTIVE=3fffffffff
_SYSTEMD_CGROUP=/init.scope
_SYSTEMD_UNIT=init.scope
_SYSTEMD_SLICE=-.slice
CODE_FILE=../src/core/unit.c
_CMDLINE=/usr/lib/systemd/systemd --switched-root --system --deserialize 25
_SELINUX_CONTEXT=system_u:system_r:init_t:s0
UNIT=ip-accounting-test.service
CODE_LINE=2115
CODE_FUNC=unit_log_resources
MESSAGE_ID=ae8f7b866b0347b9af31fe1c80b127c0
INVOCATION_ID=98a6e756fa9d421d8dfc82b6df06a9c3
IP_METRIC_INGRESS_BYTES=50880
IP_METRIC_INGRESS_PACKETS=605
IP_METRIC_EGRESS_BYTES=50880
IP_METRIC_EGRESS_PACKETS=605
MESSAGE=ip-accounting-test.service: Received 49.6K IP traffic, sent 49.6K IP traffic
_SOURCE_REALTIME_TIMESTAMP=1507565752649028
\end{Verbatim}
Нас интересуют поля +IP_METRIC_INGRESS_BYTES=+, +IP_METRIC_INGRESS_PACKETS=+,
+IP_METRIC_EGRESS_BYTES=+, +IP_METRIC_EGRESS_PACKETS=+, отображающие значения
соответствующих счетчиков.
Все подобные записи имеют один и тот же идентификатор типа сообщения
(\href{https://www.freedesktop.org/software/systemd/man/systemd.journal-fields.html\#MESSAGE_ID=}%
{message ID}), при помощи которого их можно легко найти в журнале
(+ae8f7b866b0347b9af31fe1c80b127c0+). Вызовем +journalctl+ с указанием этого
идентификатора, добавив также ключ +-u+, который ограничит выборку только
записями нашей службы:
\begin{Verbatim}[fontsize=\small]
# journalctl -u ip-accounting-test MESSAGE_ID=ae8f7b866b0347b9af31fe1c80b127c0
-- Logs begin at Thu 2016-08-18 23:09:37 CEST, end at Mon 2017-10-09 18:25:27 CEST. --
Okt 09 18:15:52 sigma systemd[1]: ip-accounting-test.service: Received 49.6K IP traffic, sent 49.6K IP traffic
\end{Verbatim}
Приведенный вывод содержит пока только одну запись, так как мы запускали службу
всего один раз. Каждый последующий запуск будет добавлять новую запись.
Учет IP-трафика доступен также и для одноразовых служб, запускаемых через
\hreftt{https://www.freedesktop.org/software/systemd/man/systemd-run.html}%
{systemd-run}, что позволяет прозрачно выполнять различные команды как службы
systemd, и подсчитывать их трафик:
\begin{Verbatim}
# systemd-run -p IPAccounting=yes --wait \
wget https://cfp.all-systems-go.io/en/ASG2017/public/schedule/2.pdf
Running as unit: run-u2761.service
Finished with result: success
Main processes terminated with: code=exited/status=0
Service runtime: 878ms
IP traffic received: 231.0K
IP traffic sent: 3.7K
\end{Verbatim}
Мы использовали \hreftt{https://linux.die.net/man/1/wget}{wget}, чтобы загрузить
\href{https://cfp.all-systems-go.io/en/ASG2017/public/schedule/2.pdf}{PDF с
расписанием второго дня} нашей любимой конференции
\href{https://all-systems-go.io/}{All Systems Go! 2017}. Эта операция
потребовала 231 килобайт входящего трафика и 4 килобайта исходящего.
Особого внимания заслуживают параметры командной строки, с которыми мы вызвали
+systemd-run+. Первый, +-p IPAccounting=yes+, включает режим учета IP-трафика
для нашей одноразовой службы (аналогично тому, как та же строка в юнит-файле
делает это для обычной службы). Второй параметр, +--wait+, приказывает
+systemd-run+ дождаться завершения работы созданной службы, после чего вывести
информацию о результатах ее работы, включая интересующую нас статистику по
IP-трафику (разумеется, только при условии, что мы включили его подсчет).
Еще интереснее получается, если применять учет IP-трафика для
\emph{интерактивных} одноразовых служб:
\begin{Verbatim}
# systemd-run -p IPAccounting=1 -t /bin/sh
Running as unit: run-u2779.service
Press ^] three times within 1s to disconnect TTY.
sh-4.4# dnf update
...
sh-4.4# dnf install firefox
...
sh-4.4# exit
Finished with result: success
Main processes terminated with: code=exited/status=0
Service runtime: 5.297s
IP traffic received: ...B
IP traffic sent: ...B
\end{Verbatim}
Мы использовали ключ +systemd-run+ +--pty+ (в краткой форме +-t+), который
открывает интерактивное псевдо-терминальное подключение к запускаемой службе~---
в нашем случае это оболочка +/bin/sh+. Таким образом, мы получаем полноценную
оболочку, с управлением фоновыми заданиями и прочими возможностями, запущенную
в рамках службы systemd. Так как для этой службы включен учет IP-трафика,
при ее завершении мы получаем суммарную статистику по входящим и исходящим
данным. (Для краткости я не~стал показывать вывод целиком, а оставил только
ключевые моменты. Если вам хочется посмотреть на полный вывод~--- попробуйте
запустить что-нибудь сами.)
Иногда бывает нужно включить учет IP-трафика для юнита, который уже запущен.
Это можно сделать командой
\begin{Verbatim}
systemctl set-property foobar.service IPAccounting=yes
\end{Verbatim}
Обратите внимание, что она не~имеет обратной силы: трафик учитывается только с
момента выполнения этой команды. Аналогичной командой (+IPAccounting=no+) учет
трафика можно отключить.
Если вы хотите подсчитывать IP-трафик сразу для всех служб, вам вовсе
не~обязательно добавлять +IPAccounting=yes+ во все юнит-файлы. Достаточно
задействовать глобальную опцию
\hreftt{https://www.freedesktop.org/software/systemd/man/systemd-system.conf.html\#DefaultCPUAccounting=}%
{DefaultIPAccounting=} в файле +/etc/systemd/system.conf+.
\subsectiona{Фильтрация IP-трафика}
От учета трафика переходим к его фильтрации. Рассмотрим поподробнее механизм
контрольных списков IP-адресов (IP ACL), добавленный в systemd 235. Как уже
упоминалось выше, за него отвечают две директивы: +IPAddressAllow=+ и
+IPAddressDeny=+. Работают они следующим образом:
\begin{enumerate}
\item Если адрес источника входящего пакета, либо адрес назначения
исходящего пакета соответствует какому-либо из адресов хостов
или попадает в одну из подсетей, указанных в +IPAddressAllow=+,
пакет проходит свободно.
\item Если пакет не~соответствует +IPAddressAllow=+, но подпадает
под +IPAddressDeny=+ (аналогично, проверяются исходные адреса
входящих пакетов и адреса назначения исходящих), он блокируется.
\item Если пакет не~подпадает ни под одну из этих директив, он тоже
проходит свободно.
\end{enumerate}
Иными словами, +IPAddressDeny=+ является черным списком, но белый список
+IPAddressAllow=+ имеет над ним приоритет.
Посмотрим, как это работает. Поменяем предыдущий пример, чтобы получить
интерактивную оболочку, работающую в рамках одноразовой службы с настроенными
контрольными списками IP-адресов:
\begin{Verbatim}
# systemd-run -p IPAddressDeny=any -p IPAddressAllow=8.8.8.8 \
-p IPAddressAllow=127.0.0.0/8 -t /bin/sh
Running as unit: run-u2850.service
Press ^] three times within 1s to disconnect TTY.
sh-4.4# ping 8.8.8.8 -c1
PING 8.8.8.8 (8.8.8.8) 56(84) bytes of data.
64 bytes from 8.8.8.8: icmp_seq=1 ttl=59 time=27.9 ms
--- 8.8.8.8 ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 27.957/27.957/27.957/0.000 ms
sh-4.4# ping 8.8.4.4 -c1
PING 8.8.4.4 (8.8.4.4) 56(84) bytes of data.
ping: sendmsg: Operation not permitted
^C
--- 8.8.4.4 ping statistics ---
1 packets transmitted, 0 received, 100% packet loss, time 0ms
sh-4.4# ping 127.0.0.2 -c1
PING 127.0.0.1 (127.0.0.2) 56(84) bytes of data.
64 bytes from 127.0.0.2: icmp_seq=1 ttl=64 time=0.116 ms
--- 127.0.0.2 ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 0.116/0.116/0.116/0.000 ms
sh-4.4# exit
\end{Verbatim}
Мы задали +IPAddressDeny=any+, чтобы работать по схеме белого списка: для нашей
службы разрешен обмен трафика только с теми адресами, которые перечислены в
+IPAddressAllow=+. А это, в данном примере, уже знакомый нам адрес 8.8.8.8
(указанный без маски подсети, что соответствует адресу хоста, т.е. маске +/32+),
и подсеть 127.0.0.0/8. Таким образом, служба может взаимодействовать только с
одним из DNS-серверов Google и адресами обратной петли, и больше ни с кем.
Команды, запущенные в оболочке, иллюстрируют это. Сначала мы пытаемся пинговать
8.8.8.8~--- успешно. Затем, мы пробуем пропинговать 8.8.4.4 (это другой
DNS-сервер Google, не~входящий в наш белый список), и видим ошибку <<Operation
not permitted>>. Наконец, мы пингуем адрес 127.0.0.2 (принадлежащий подсети
обратной петли), и снова успешно.
Обратите внимание на специальное значение +any+, которое мы использовали в
примере выше. Оно является сокращением для <<+0.0.0.0/0 ::/0+>>, то есть
\emph{всех} возможных адресов IPv4 и IPv6. Есть и другие сокращения. Например,
вместо +127.0.0.0/8+ мы могли бы указать более понятное обозначение +localhost+,
соответствующее <<+127.0.0.0/8 ::1/128+>>, то есть IPv4 и IPv6 подсетям обратной
петли.
Возможность настраивать контрольные списки IP-адресов независимо для каждого
юнита~--- это уже неплохо. Однако, для большего удобства, существует
возможность задавать их для целых групп юнитов, или даже для всей системы. Это
можно сделать при помощи юнитов
\hreftt{https://www.freedesktop.org/software/systemd/man/systemd.slice.html}%
{.slice} (для тех, кто не~очень хорошо разбирается в systemd: slice-юниты
предоставляют возможность организовать юниты в группы для управления системными
ресурсами\footnote{Прим. перев.: Чуть более развернутое определение:
slice-юниты соответствуют <<промежуточным>> уровням иерархии контрольных групп,
которые не~содержат непосредственно процессов, а объединяют другие +.slice+,
+.service+ и +.scope+-юниты. Последние два типа юнитов уже соответствуют
контрольным группам процессов, причем группы +.service+ создаются автоматически
при запуске служб systemd, а +.scope+~--- это специально сформированные
контрольные группы процессов, запущенных другими программами, что позволяет
<<перекидывать>> отдельные процессы в другие точки иерархии контрольных
групп. В частности, процессы, созданные в рамках пользовательских сессий, а
также деревья процессов контейнеров, автоматически перемещаются из контрольных
групп породивших их служб (например, +kdm.service+) в специальные scope-юниты
(например, +session-1.scope+).}): контрольные списки адресов юнита формируются
путем объединения списков самого юнита, а также всех slice-юнитов, в которые он
вложен.
По умолчанию, все системные службы помещаются в группу
\hreftt{https://www.freedesktop.org/software/systemd/man/systemd.special.html\#system.slice}%
{system.slice}, которая, в свою очередь, входит в корневую группу
\hreftt{https://www.freedesktop.org/software/systemd/man/systemd.special.html\#-.slice}%
{-.slice}. Соответственно, при помощи любой из этих групп можно заблокировать
\emph{все} системные службы разом. Разница состоит в том, что ограничения
+system.slice+ будут применяться только к системным службам, а +-.slice+
действует сразу на все процессы системы, включая не~только службы, но и процессы
пользовательских сеансов (объединенные в группу +user.slice+, которая находится
в корневой группе).
Воспользуемся этим:
\begin{Verbatim}
# systemctl set-property system.slice IPAddressDeny=any IPAddressAllow=localhost
# systemctl set-property apache.service IPAddressAllow=10.0.0.0/8
\end{Verbatim}
Две приведенные команды имеют очень мощный эффект: сначала мы отключаем
взаимодействие через IP (кроме обратной петли) для всех служб системы, а затем
добавляем белый список 10.0.0.0/8 (скажем, это локальная сеть вашей компании)
только для службы веб-сервера Apache.
\subsectiona{Практическое применение}
Вот несколько идей по практическому применению рассмотренных возможностей:
\begin{enumerate}
\item Механизм контрольных списков IP адресов можно рассматривать как
современную альтернативу классической технологии
\href{https://en.wikipedia.org/wiki/TCP_Wrapper}{TCP Wrapper}.
Однако, в отличие от нее, контрольные списки применяются сразу
ко всем IP-сокетам службы, и не~требуют никакой поддержки со
стороны приложения. С другой стороны, TCP Wrapper предоставляет
ряд опций, которые остутствуют в нашей схеме, в частности,
возможность указания DNS-имен вместо IP-адресов (лично я считаю
это весьма сомнительной опцией~--- выполнять сетевые операции
(разрешение имен), причем незащищенные, для того, чтобы
ограничить работу сети).
\item В некоторых аспектах наши механизмы могут заменить или хотя бы
дополнить классический брандмауэр Linux~---
NetFilter/+iptables+. На текущий момент, контрольные списки
IP-адресов systemd предоставляют гораздо меньше возможностей,
чем NetFilter, однако имеют перед ним серьезное преимущество:
они работают на уровне \emph{приложений}, а не~абстрактных
TCP/UDP-портов. Классические брандмауэры, в частности,
NetFilter, вынуждены делать предположение о принадлежности
пакетов тем или иным службам, основываясь только на номерах
портов\footnote{Прим. перев.: В ядрах Linux 2.4-2.6
существовала теоретическая возможность выборки пакетов по
идентификатору процесса (+-m owner --pid-owner PID+), однако она
была удалена в выпуске 2.6.14. Много лет спустя, в ядре 3.14 был
добавлен модуль netfilter +xt_cgroup+, позволяющий выбирать
пакеты по индексу класса, присвоенного cgroup-контроллером
+net_class+. Кстати, поддержка соответствующей опции
+NetClass=+ существовала в systemd с 227 по 229 версию, но была
удалена при миграции на
\href{https://www.kernel.org/doc/Documentation/cgroup-v2.txt}%
{cgroup v2}, где такого контроллера уже нет, зато есть
вышеописанный механизм контрольных списков.}, однако на практике
порты часто выбираются динамически. Например, клиенты
BitTorrent для передачи данных могут использовать любые удобные
им порты, что крайне затрудняет корректную выборку таких
пакетов. В случае с контрольными списками все просто: достаточно
настроить разрешения для юнита службы BitTorrent, и дело в
шляпе.
Замечу что, по большей части, сравнение NetFilter и контрольных
списков systemd~--- это сравнение теплого с мягким. Контрольные
списки ориентированы исключительно на конечные машины (клиенты и
серверы), так как работают только с локальными службами. В то
время как NetFilter отлично работает на промежуточных
маршрутизаторах, передающих чистый IP-трафик, никак
не~привязанный к их локальным процессам.
\item Контрольные списки предоставляют простой способ обеспечить
безопасность служб <<из коробки>>. Например, если вы
сопровождаете пакет службы, которая не~должна требовать доступа
к сети, добавьте в ее юнит-файл +IPAddressDeny=any+ (и, при
необходимости, +IPAddressAllow=localhost+), и она будет помещена
в песочницу, из которой не~сможет сбежать. В systemd подобные
ограничения уже введены по умолчанию для целого ряда служб,
например, для системного журнала
+systemd-journald.service+\footnote{Прим. перев.: Знаменитый
HTTP-сервер вынесен в отдельную службу
\href{https://www.freedesktop.org/software/systemd/man/systemd-journal-gatewayd.html}%
{systemd-journal-gatewayd(8)}, которая
\hreftt{https://github.com/systemd/systemd/blob/master/system-preset/90-systemd.preset}%
{отключена} по умолчанию, а в ряде дистрибутивов вообще
поставляется в отдельном опциональном пакете (в частности,
RHEL/CentOS, Debian/Ubuntu).}, менеджера логинов
+systemd-logind+ и обработчика дампов памяти
+systemd-coredump@.service+, так как мы знаем, что этим службам
ни~при каких условиях не~нужна сеть.
\item Механизм контрольных списков IP-адресов можно сочетать с
одноразовыми службами, что позволяет быстро и эффективно
изолировать произвольные команды, и даже включать их в конвееры.
Предположим, что мы не~доверяем нашему бинарнику
\hreftt{https://linux.die.net/man/1/curl}{curl} (может быть, он
модифицирован хакером, и обращается на подконтрольные ему
серверы?), но все равно хотим использовать его для загрузки
\href{http://0pointer.de/public/casync-kinvolk2017.pdf}{презентации
с моего последнего доклада по casync}, чтобы распечатать их, и
при этом быть уверенными, что он не~будет связываться ни~с кем,
кроме нужного нам сервера (а чтобы было еще интереснее и
безопаснее, включим описанный в предыдущей главе механизм
динамических пользователей):
\begin{Verbatim}
# systemd-resolve 0pointer.de
0pointer.de: 85.214.157.71
2a01:238:43ed:c300:10c3:bcf3:3266:da74
-- Information acquired via protocol DNS in 2.8ms.
-- Data is authenticated: no
# systemd-run --pipe -p IPAddressDeny=any \
-p IPAddressAllow=85.214.157.71 \
-p IPAddressAllow=2a01:238:43ed:c300:10c3:bcf3:3266:da74 \
-p DynamicUser=yes \
curl http://0pointer.de/public/casync-kinvolk2017.pdf | lp
\end{Verbatim}
\end{enumerate}
Как и в прошлой главе, в силу масштабности обсуждаемой концепции, приведенный
список применений не~претендует на полноту, а лишь является затравкой для вашего
воображения.
\subsectiona{Рекомендации сопровождающим пакетов}
Механизмы учета и контроля IP-трафика ориентированы прежде всего на системных
администраторов, а не~разработчиков. Тем не~менее, как я уже заметил выше, для
служб, которые никак и никогда не~требуют сети, целесообразно добавлять
настройку +IPAddressDeny=any+ (и опционально +IPAddressAllow=localhost+), чтобы
повысить безопасность системы сразу <<из коробки>>.
Для специализированных дистрибутивов, ориентированных на максимальную
безопасность, можно предложить более радикальный подход: добавить
+IPAddressDeny=any+ сразу в +-.slice+ или +system.slice+, чтобы администратор,
когда ему нужно выпустить в сеть какую-либо службу, вручную командовал
+systemctl set-property ... IPAddressAllow=...+. Разумеется, это вариант для
тех дистрибутивов, которые не~боятся ломать обратную совместимость.
\subsectiona{Дополнительные замечания}
И еще несколько замечаний:
\begin{enumerate}
\item Описанные механизмы учета и фильтрации IP-трафика можно совмещать
с сокет-активацией. При этом, целесообразно настраивать их
и для сокета, и для активируемой им службы, так как это
разные юниты с независимыми настройками. Обратите внимание, что
учет и фильтрация трафика, настроенные для сокет-юнита,
применяются для всех сокетов, созданных в рамках этого юнита,
включая переданные активированным службам. Как следствие, трафик
учитывается именно для юнита сокета, а не~службы. Тот факт, что
для сокетов, созданных под эгидой сокет-юнита (т.е. при
сокет-активации), и для сокетов, созданных из кода службы,
используются \emph{разные} контрольные списки, открывает весьма
интересные возможности. Например, можно настроить относительно
свободный доступ для сокета, при этом полностью запретив
IP-трафик для активируемых им служб~--- в результате эти службы
смогут взаимодейстовать с внешним миром только через переданный
им при активации сокет.
\item Учет и фильтрация IP-трафика работают только с IP-сокетами. В
частности, сокеты типа +AF_PACKET+ (так называемые <<сырые
сокеты>>) под эти ограничения не~подпадают\footnote{Прим.
перев.: Они также <<обходят>> и netfilter~--- именно поэтому
бесполезно пытаться фильтровать трафик DHCP-клиента с той же
машины.}. Поэтому целесообразно дополнять контрольные списки
опцией
\hreftt{https://www.freedesktop.org/software/systemd/man/systemd.exec.html\#RestrictAddressFamilies=}%
{RestrictAddressFamilies=AF\_UNIX AF\_INET AF\_INET6}.
\item Вы также можете поинтересоваться, может ли журнальная запись о
потребленных ресурсах, а также +systemd-run+ с ключом +--wait+
показывать статистику по другим видам ресурсов?
Ответ утвердительный: например, если вы зададите для юнита
\hreftt{https://www.freedesktop.org/software/systemd/man/systemd.resource-control.html\#CPUAccounting=}%
{CPUAccounting=yes}, то журнальная запись и +systemd-run+ будут
показывать статистику по потребленному процессорному времени. В
ближайшее время мы планируем добавить аналогичную поддержку для
\hreftt{https://www.freedesktop.org/software/systemd/man/systemd.resource-control.html\#IOAccounting=}%
{IOAccounting=}.
\item Обратите внимание, что учет и фильтрация IP-трафика сами могут
потреблять определенные ресурсы. Чтобы эти функции работали,
systemd вставляет в путь прохождения IP-трафика специальную
eBPF-программу. Однако, в последних версиях ядра выполнение eBPF
очень неплохо оптимизировано, и работа по оптимизации
продолжается, так что серьезных проблем с производительностью
ожидать не~стоит.
\item Учет трафика не~является иерархическим, то есть, счетчики
slice-юнита не~суммируют результаты вложенных в него юнитов. Это
одна из задач, которую мы намерены решить, однако она требует
доработки кода ядра.
\item У вас может возникнуть вопрос~--- как соотносятся механизмы
\hreftt{https://www.freedesktop.org/software/systemd/man/systemd.exec.html\#PrivateNetwork=}%
{PrivateNetwork=yes} и +IPAccessDeny=any+? На первый взгляд, они
работают одинаково: блокируют доступ к сети для службы. Однако,
при более пристальном рассмотрении обнаруживается ряд отличий.
Механизм +PrivateNetwork=+ реализован на основе сетевых
пространств имен ядра Linux. Он полностью изолирует от хоста
все сетевые операции службы~--- не~только обычные IP-сокеты, но
и другие виды сетевого взаимодействия (в частности, сырые
сокеты). Для этого он создает службе отдельное сетевое
пространство имен, в котором она может общаться только сама
с собой (через свой личный интерфейс обратной петли). Однако,
при использовании опции
\hreftt{https://www.freedesktop.org/software/systemd/man/systemd.unit.html\#JoinsNamespaceOf=}%
{JoinsNamespaceOf=} в ту же песочницу могут быть помещены другие
службы~--- в результате, службы из одной песочницы смогут
взаимодействовать между собой, но при этом будут изолированы от
остального мира. С другой стороны, +IPAddressAllow=+ и
+IPAccessDeny=+ действуют не~столь жестко. Прежде всего, они
работают только с IP-сокетами и IP-адресами. Кроме того, служба
с выключенным режимом +PrivateNetwork=+, но включенным
+IPAccessDeny=any+, все равно способна прочитать список сетевых
интерфейсов хоста и узнать присвоенные им адреса, хотя и
не~сможет пересылать данные по протоколу IP. В то же время, с
включенным режимом +PrivateNetwork=+ служба может видеть только
свой личный интерфейс +lo+. Короче: в зависимости от конкретной
ситуации, для изоляции службы можно использовать тот или иной
вариант, или оба сразу, или вообще ни~одного. Когда это
возможно, для максимальной безопасности лучше использовать обе
эти защиты, как это делаем мы для всех основных служб из состава
systemd.
\end{enumerate}
На этом пока все.
\newpage
\appendix \appendix
\section{FAQ (часто задаваемые вопросы)\sfnote{Перевод статьи \section{FAQ (часто задаваемые вопросы)\sfnote{Перевод статьи