Добавлен перевод статьи "IP Accounting and Access Lists with systemd"
This commit is contained in:
601
s4a.tex
601
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, не~входящий в наш белый список), и видим ошибку <<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
|
||||
|
||||
\section{FAQ (часто задаваемые вопросы)\sfnote{Перевод статьи
|
||||
|
||||
Reference in New Issue
Block a user