LINUX.ORG.RU

Фильтрация трафика приложения или группы приложений.

 , ,


2

1

Владельцам ядер ниже 4.4 можно не читать.

Собственно есть cgroup и iptables -m cgroup. Почему бы не создать отдельный cgroup и фильтровать локальный трафик для этой группы?

1. Не забываем включить классную фичу

sysctl -w net.ipv4.ip_early_demux=1
# на ядрах 4.10+
sysctl -w net.ipv4.tcp_early_demux=1
sysctl -w net.ipv4.udp_early_demux=1

2. Создаем группу

mkdir /sys/fs/cgroup/net_cls/testgroup2
echo 1234 >/sys/fs/cgroup/net_cls/testgroup2/net_cls.classid

3. создаем тестовые правила

iptables -A INPUT -m cgroup --cgroup 1234 -p icmp
iptables -A INPUT -m cgroup --cgroup 1234 -p udp
iptables -A INPUT -m cgroup --cgroup 1234 -p tcp
iptables -A OUTPUT -m cgroup --cgroup 1234 -p icmp
iptables -A OUTPUT -m cgroup --cgroup 1234 -p udp
iptables -A OUTPUT -m cgroup --cgroup 1234 -p tcp

4. Переносим себя в эту группу

echo $$ >/sys/fs/cgroup/net_cls/testgroup2/tasks

5. Тестируем UDP

dig google.com
iptables -nvxL INPUT; iptables -nvxL OUTPUT

Chain INPUT (policy ACCEPT 2 packets, 255 bytes)
    pkts      bytes target     prot opt in     out     source               destination
       0        0            tcp  --  *      *       0.0.0.0/0            0.0.0.0/0            cgroup 1234
       0        0            udp  --  *      *       0.0.0.0/0            0.0.0.0/0            cgroup 1234
       0        0            icmp --  *      *       0.0.0.0/0            0.0.0.0/0            cgroup 1234
Chain OUTPUT (policy ACCEPT 2 packets, 91 bytes)
    pkts      bytes target     prot opt in     out     source               destination
       0        0            icmp --  *      *       0.0.0.0/0            0.0.0.0/0            cgroup 1234
       2       91            udp  --  *      *       0.0.0.0/0            0.0.0.0/0            cgroup 1234
       0        0            tcp  --  *      *       0.0.0.0/0            0.0.0.0/0            cgroup 1234

6. Тестируем TCP

iptables -Z
wget -4 -O /dev/zero https://google.com
iptables -nvxL INPUT; iptables -nvxL OUTPUT

Chain INPUT (policy ACCEPT 4 packets, 538 bytes)
    pkts      bytes target     prot opt in     out     source               destination
       4      538            tcp  --  *      *       0.0.0.0/0            0.0.0.0/0            cgroup 1234
       0        0            udp  --  *      *       0.0.0.0/0            0.0.0.0/0            cgroup 1234
       0        0            icmp --  *      *       0.0.0.0/0            0.0.0.0/0            cgroup 1234
Chain OUTPUT (policy ACCEPT 6 packets, 451 bytes)
Chain OUTPUT (policy ACCEPT 6 packets, 451 bytes)
    pkts      bytes target     prot opt in     out     source               destination
       0        0            icmp --  *      *       0.0.0.0/0            0.0.0.0/0            cgroup 1234
       0        0            udp  --  *      *       0.0.0.0/0            0.0.0.0/0            cgroup 1234
       6      451            tcp  --  *      *       0.0.0.0/0            0.0.0.0/0            cgroup 1234

7. Тестировать остальные протоколы бесполезно, т.к. они не поддерживаются в early_demux

Что имеем в результате? TCP можно контролировать для группы, а с UDP пролный 3.14ц!

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

Проблема в том, что в UDP можно сделать bind() только на порт, что делает поиск сокета на которые пришли данные в два раза сложнее. __udp4_lib_demux_lookup() делает поиск только по номеру порта и адресу, игнорируя сокеты привязанные только к номеру порта.

Заменив __udp4_lib_demux_lookup() на полноценный поиск udp сокета через __udp4_lib_lookup()

для ядра 4.14.165

diff --git a/net/ipv4/udp.c b/net/ipv4/udp.c
index 731ea78..6143ca5 100644
--- a/net/ipv4/udp.c
+++ b/net/ipv4/udp.c
@@ -2295,8 +2295,8 @@ int udp_v4_early_demux(struct sk_buff *skb)
                                                   uh->source, iph->saddr,
                                                   dif, sdif);
        } else if (skb->pkt_type == PACKET_HOST) {
-               sk = __udp4_lib_demux_lookup(net, uh->dest, iph->daddr,
-                                            uh->source, iph->saddr, dif, sdif);
+               sk = __udp4_lib_lookup(net, iph->saddr, uh->source, iph->daddr, uh->dest,
+                                       dif, 0, &udp_table, NULL);
        }

        if (!sk || !refcount_inc_not_zero(&sk->sk_refcnt))

Да, это дополнительные накладные расходы, но мы дважды это подтвердили через net.ipv4.ip_early_demux=1 и net.ipv4.udp_early_demux=1

Но теперь мы получаем в п.5

Chain INPUT (policy ACCEPT 2 packets, 255 bytes)
    pkts      bytes target     prot opt in     out     source               destination
       0        0            tcp  --  *      *       0.0.0.0/0            0.0.0.0/0            cgroup 1234
       1      226            udp  --  *      *       0.0.0.0/0            0.0.0.0/0            cgroup 1234
       0        0            icmp --  *      *       0.0.0.0/0            0.0.0.0/0            cgroup 1234
Chain OUTPUT (policy ACCEPT 2 packets, 91 bytes)
    pkts      bytes target     prot opt in     out     source               destination
       0        0            icmp --  *      *       0.0.0.0/0            0.0.0.0/0            cgroup 1234
       2       91            udp  --  *      *       0.0.0.0/0            0.0.0.0/0            cgroup 1234
       0        0            tcp  --  *      *       0.0.0.0/0            0.0.0.0/0            cgroup 1234
Т.е. оно стало работать!

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

Есть возможность получить аналогичный результат при помощи модуля ядра. ( https://github.com/vel21ripn/ipt_sk_helper )

Чтоб оно заработало нужно сделать sysctl -w net.ipv4.ip_early_demux=2

Работа проверялать на 4.4.209 и 4.14.165. Собирается даже на 5.4.х

Если кто может попробовать собрать его и протестировать - напишите что получилось.

PS Если починить эту фичу в официальном ядре, то станет доступной фильтраци входящего трафика по UID/GID.

★★★★★

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

aidaho ★★★★★
()
Ответ на: комментарий от aidaho

почему бы пакеты группы не пометить fwmark

А как определить, что пакет адресован процессу группы?

Делать отдельный netns слишком дорого.

Так что "-m cgroup" - замечательный инструмент, если бы не баги в реализации.

vel ★★★★★
() автор топика

Если кто может попробовать собрать его и протестировать - напишите что получилось.

Да то же самое примерно и получилось.

imul ★★★★★
()
Ответ на: комментарий от imul

Да если будет время, то попробую.

vel ★★★★★
() автор топика
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.