From d7289115f57d014b2e33a2c1e5a862eb26e7f5f6 Mon Sep 17 00:00:00 2001 From: nnz1024 <0comffdiz@inbox.ru> Date: Sat, 28 Oct 2017 00:14:16 +0300 Subject: [PATCH] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB=D0=B5?= =?UTF-8?q?=D0=BD=20=D0=BF=D0=B5=D1=80=D0=B5=D0=B2=D0=BE=D0=B4=20=D1=81?= =?UTF-8?q?=D1=82=D0=B0=D1=82=D1=8C=D0=B8=20"IP=20Accounting=20and=20Acces?= =?UTF-8?q?s=20Lists=20with=20systemd"?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- s4a.tex | 601 +++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 599 insertions(+), 2 deletions(-) diff --git a/s4a.tex b/s4a.tex index 410bc5c..8577025 100644 --- a/s4a.tex +++ b/s4a.tex @@ -2766,7 +2766,7 @@ PrivateNetwork=yes котором настраивается только интерфейс обратной петли. \subsection{Предоставление службам независимых каталогов \texttt{/tmp}} -\label{sec:privatetmp} +\label{ssec:privatetmp} Еще одна простая, но мощная опция настройки служб~--- +PrivateTmp=+: \begin{Verbatim} @@ -3781,6 +3781,7 @@ $ journalctl /usr/sbin/vpnc /usr/sbin/dhclient Отлично, мы нашли причину проблемы! \subsection{Продвинутые методы выборки} +\label{ssec:metadata} Да, это все, конечно, здорово, но попробуем подняться еще на ступеньку выше. Чтобы понять описанные ниже приемы, нужно знать, что systemd добавляет к @@ -5402,7 +5403,7 @@ systemd реализует одновременно обе стратегии, Для самой службы создается пространство имен монтирования (mount namespace), в котором эти подкаталоги bind-монтируются в +/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, не~входящий в наш белый список), и видим ошибку <>. Наконец, мы пингуем адрес 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 \section{FAQ (часто задаваемые вопросы)\sfnote{Перевод статьи