Iptables: сопоставление исходящего трафика с conntrack и владельцем. Работает со странными каплями

11

В моем скрипте iptables я экспериментировал с написанием как можно более детальных правил. Я ограничиваю пользователей, которым разрешено использовать какие сервисы, частично для безопасности и частично для обучения.

Использование iptables v1.4.16.2 на Debian 6.0.6 с ядром 3.6.2.

Однако я столкнулся с проблемой, которую пока не совсем понимаю ...

исходящие порты для всех пользователей

Это прекрасно работает. У меня нет общих правил отслеживания состояния.

## Исходящий порт 81
$ IPTABLES -A OUTPUT -p tcp --dport 81 -m conntrack --ctstate НОВЫЙ, УСТАНОВЛЕННЫЙ -j ПРИНЯТЬ
$ IPTABLES -A INPUT -p tcp --sport 81 -s $ MYIP -m conntrack --ctstate УСТАНОВЛЕНО -j ПРИНЯТЬ

исходящие порты с соответствием пользователя

## исходящий порт 80 для учетной записи пользователя
$ IPTABLES -A OUTPUT --match владелец --uid-владелец useraccount -p tcp --dport 80 -m conntrack --ctstate NEW, УСТАНОВЛЕНО --sport 1024: 65535 -j ПРИНЯТЬ
$ IPTABLES -A INPUT -p tcp --sport 80 --dport 1024: 65535 -d $ MYIP -m conntrack --ctstate УСТАНОВЛЕНО -j ПРИНЯТЬ

Это позволяет использовать порт 80 только для учетной записи «useraccount», но такие правила для трафика TCP имеют проблемы.

## Исходящий журнал по умолчанию + правила блокировки
$ IPTABLES -A OUTPUT -j LOG - log-префикс "BAD OUTGOING" --log-ip-options --log-tcp-options --log-uid
$ IPTABLES -A OUTPUT -j DROP

Проблема

Выше работает, пользователь «useraccount» может получить файлы отлично. Другие пользователи системы не могут устанавливать исходящие соединения на порт 80.

useraccount @ host: $ wget http://cachefly.cachefly.net/10mb.test

Но wget выше оставляет x7 пропущенных записей в моем системном журнале:

18 октября 02:00:35 xxxx ядро: BAD OUTGOING IN = OUT = eth0 SRC = xx.xx.xx.xx DST = 205.234.175.175 LEN = 40 TOS = 0x00 PREC = 0x00 TTL = 64 ID = 12170 DF PROTO = TCP SPT = 37792 DPT = 80 SEQ = 164520678 ACK = 3997126942 ОКНО = 979 RES = 0x00 ACK URGP = 0  

Я не получаю эти отбрасывания для аналогичных правил с трафиком UDP. У меня уже есть правила, которые ограничивают, какие пользователи могут делать запросы DNS.

Кажется, что отброшенные исходящие ACK-пакеты поступают от учетной записи root (URGP = 0), чего я не понимаю. Даже когда я меняю useraccount на root.

Я полагаю, что пакеты ACK относятся к категории новых, поскольку conntrack начинает отслеживать соединения после 3-го шага трехстороннего рукопожатия, но почему их отбрасывают?

Можно ли безопасно игнорировать эти капли?

редактировать

Поэтому я часто вижу такие правила, которые хорошо работают для меня:

$ IPTABLES -A OUTPUT -s $ MYIP -p tcp -m tcp --dport 80 -m состояние --state NEW, УСТАНОВЛЕНО -j ПРИНЯТЬ
$ IPTABLES -A INPUT -p tcp -m tcp --sport 80 -d $ MYIP -m состояние - состояние УСТАНОВЛЕНО -j ПРИНЯТЬ

Я поменял местами «-m состояние --state» на «-m conntrack --ctstate», так как совпадение состояний явно устарело.

Лучше ли иметь общие правила отслеживания состояния? Разве приведенные выше правила не считаются правильными?

Для жесткого контроля над исходящими соединениями пользователей было бы что-то подобное лучше?

$ IPTABLES -A INPUT -m conntrack --ctstate УСТАНОВЛЕНО -j ПРИНЯТЬ
$ IPTABLES -A OUTPUT -m conntrack --ctstate УСТАНОВЛЕНО -j ПРИНЯТЬ

$ IPTABLES -A ВЫХОД -p tcp --dport 80 -s $ SERVER_IP_TUNNEL -m conntrack --ctstate NEW -m владелец --uid-владелец useraccount -j ПРИНЯТЬ

$ IPTABLES -A ВЫХОД -p tcp --dport 80 -s $ SERVER_IP_TUNNEL -m conntrack --ctstate NEW -m владелец --uid-владелец otheraccount -j ПРИНЯТЬ
рАРО
источник
Не могли бы вы опубликовать все правила, включая цепочку FORWARD и таблицу nat, если этот хост также маршрутизирует трафик из локальной сети.
Серж
Этот хост не выполняет маршрутизацию, трафик исходит от машины с этими правилами. Я разместил только соответствующие правила для конкретного исходящего трафика.
arcX

Ответы:

16

Короче говоря, этот ACK был отправлен, когда сокет никому не принадлежал. Вместо того, чтобы разрешать пакеты, которые относятся к сокету, который принадлежит пользователю x, разрешите пакеты, которые относятся к соединению, которое было инициировано сокетом от пользователя x.

Чем дольше история.

Чтобы понять проблему, полезно понять, как wgetи HTTP-запросы работают в целом.

В

wget http://cachefly.cachefly.net/10mb.test

wgetустанавливает TCP-соединение с cachefly.cachefly.net, и после установления отправляет запрос в протокол HTTP, который говорит: «Пожалуйста, пришлите мне содержимое /10mb.test( GET /10mb.test HTTP/1.1) и, кстати, не могли бы вы не закрыть соединение после того, как вы сделали ( Connection: Keep-alive). Причина это происходит потому, что если сервер отвечает перенаправлением URL-адреса на тот же IP-адрес, он может повторно использовать соединение.

Теперь сервер может ответить либо: «здесь идут запрошенные вами данные, остерегайтесь их размера 10 МБ ( Content-Length: 10485760), и да, ОК, я оставлю соединение открытым». Или, если он не знает размер данных: «Вот данные, извините, я не могу оставить соединение открытым, но я сообщу, когда вы можете прекратить загрузку данных, закрыв мой конец соединения».

В приведенном выше URL мы в первом случае.

Таким образом, как только wgetон получил заголовки для ответа, он знает, что его работа выполнена после загрузки 10 МБ данных.

По сути, wgetэто чтение данных до получения 10 МБ и выход. Но в этот момент еще многое предстоит сделать. Как насчет сервера? Было сказано оставить соединение открытым.

Перед выходом wgetзакрывает ( closeсистемный вызов) дескриптор файла для сокета. После этого closeсистема заканчивает подтверждать данные, отправленные сервером, и отправляет FINсообщение «Я больше не буду отправлять данные». В этот момент closeвозвращается и wgetвыходит. Больше нет сокета, связанного с TCP-соединением (по крайней мере, ни один из них не принадлежит ни одному пользователю). Однако это еще не закончено. Получив это FIN, HTTP-сервер видит конец файла при чтении следующего запроса от клиента. В HTTP это означает «больше нет запросов, я закрою свой конец». Поэтому он также отправляет свой FIN, чтобы сказать: «Я тоже ничего не буду отправлять, это соединение исчезнет».

После получения этого FIN клиент отправляет «ACK». Но на тот момент wgetуже давно нет, так что ACK не от какого-либо пользователя. Именно поэтому он заблокирован вашим брандмауэром. Поскольку сервер не получает ACK, он будет отправлять FIN снова и снова, пока он не сдастся, и вы увидите больше пропущенных ACK. Это также означает, что, отбрасывая эти ACK, вы без необходимости используете ресурсы сервера (который должен поддерживать сокет в состоянии LAST-ACK) в течение некоторого времени.

Поведение было бы другим, если бы клиент не запросил «Keep-alive» или сервер не ответил «Keep-alive».

Как уже упоминалось, если вы используете трекер соединений, вам нужно пропустить каждый пакет в состоянии ESTABLISHED и RELATED и беспокоиться только о NEWпакетах.

Если вы разрешаете NEWпакеты от пользователя, xно не пакеты от пользователя y, то другие пакеты для установленных соединений пользователем xбудут проходить, и поскольку пользователь не может установить соединения y(так как мы блокируем NEWпакеты, которые установят соединение), не будет никакого пакета для пользовательских yсоединений, проходящих.

Стефан Шазелас
источник
3

Это позволяет использовать порт 80 только для учетной записи «useraccount»

- ну, по крайней мере, правила, которые вы показали, не подразумевают этого, на самом деле.

Есть также место для совета - не делайте пользовательскую проверку на УСТАНОВЛЕННЫХ потоках, просто сделайте эту проверку на НОВОМ. Я также не вижу смысла в проверке исходного порта при проверке Incoming ESTABLISHED, в чем разница, каким портом он был, он уже находится в состоянии ESTABLISHED из PoV conntrack. Брандмауэр должен быть настолько простым, насколько это возможно, но все же эффективным, так что бритвенный подход Оккама - лучший вариант.

poige
источник
1
Спасибо за совет. Обычно я за простоту, но это не главное в этом упражнении.
arcX
1
@arcX, это упражнение может потерпеть неудачу из-за того, что оно слишком сложное и противоречивое - IOW, поведение, которое вы видите, может быть связано не с внутренними сетевыми фильтрами (ошибками, причудами), а с тем, как вы его используете. Попробуйте сначала упростить набор правил ...
poige