В моем скрипте 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 ПРИНЯТЬ
Ответы:
Короче говоря, этот ACK был отправлен, когда сокет никому не принадлежал. Вместо того, чтобы разрешать пакеты, которые относятся к сокету, который принадлежит пользователю
x
, разрешите пакеты, которые относятся к соединению, которое было инициировано сокетом от пользователяx
.Чем дольше история.
Чтобы понять проблему, полезно понять, как
wget
и HTTP-запросы работают в целом.В
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
соединений, проходящих.источник
- ну, по крайней мере, правила, которые вы показали, не подразумевают этого, на самом деле.
Есть также место для совета - не делайте пользовательскую проверку на УСТАНОВЛЕННЫХ потоках, просто сделайте эту проверку на НОВОМ. Я также не вижу смысла в проверке исходного порта при проверке Incoming ESTABLISHED, в чем разница, каким портом он был, он уже находится в состоянии ESTABLISHED из PoV conntrack. Брандмауэр должен быть настолько простым, насколько это возможно, но все же эффективным, так что бритвенный подход Оккама - лучший вариант.
источник