Файрволи і VPN
Навіщо це
Section titled “Навіщо це”Ви піднімаєте новий сервер, і за годину в журналі автентифікації з’являються сотні невдалих спроб зайти по SSH із незнайомих адрес. Ви нічого не оголошували: адресу знайшов сканер, що перебирає весь IPv4-простір за лічені години. Або обернена ситуація: у колеги правило «відкрити порт 443» є, а сайт не відкривається, і треба зрозуміти, на якому кроці шляху пакет зник.
Файрвол (firewall, мережевий екран) вирішує, кого пустити і чого не пускати, навіть
якщо кажуть, що можна. Практично це код, який для кожного пакета вирішує:
пропустити, відкинути чи змінити. У Linux цей код живе всередині ядра й називається
netfilter; правила для нього пишуть інструментом nft.
Друга половина модуля про VPN: як з’єднати дві мережі через ворожий інтернет, щоб вони поводилися як одна.
Передумови. Шлях пакета крізь ядро
(модуль 3), маршрутизація (модуль 7),
NAT і conntrack (модуль 9), TCP (модуль 11),
TLS (модуль 15).
Хуки netfilter
Section titled “Хуки netfilter”Netfilter складається з точок у мережевому стеку ядра, які називаються хуками
(hooks). Модуль ядра може зареєструватися на хук і отримувати кожен пакет, що
через нього проходить. Побачити, коли саме пакет потрапляє до якого хука,
допомагає рисунок (сам шлях через драйвер і sk_buff описано в
модулі 3).
- prerouting. Перший хук для кожного пакета, що прийшов із мережі, ще до вибору маршруту. Тут змінюють адресу призначення (DNAT, модуль 9), бо від неї залежить, куди піде пакет. Тут же зручно відкидати явне сміття.
- input. Пакети, призначені самому цьому хосту. Правила для захисту самої машини: хто може зайти по SSH.
- forward. Транзитні пакети, коли Linux працює маршрутизатором. На звичайному
сервері
net.ipv4.ip_forward=0, і цей хук порожній. - output. Пакети, створені локальними процесами.
- postrouting. Останній хук перед виходом у мережу. Тут підміняють адресу
відправника (SNAT,
masquerade), бо інтерфейс виходу вже відомий.
Розгалуження — ключ до більшості помилок: пакет із адресою призначення самого хоста
не потрапляє в forward, транзитний не потрапляє в input. Правило «закрити порт 22» у forward
не захистить сам маршрутизатор, а правило в input не зупинить трафік
через нього.
Кілька ланцюжків на одному хуці
Section titled “Кілька ланцюжків на одному хуці”На один хук можуть зареєструватися багато ланцюжків із різними пріоритетами
(число; менше означає раніше). Зарезервовані імена в nftables: raw (-300),
mangle (-150), dstnat (-100), filter (0), srcnat (100). Спостереження conntrack
теж стоїть на prerouting, з пріоритетом -200, тому правило в raw виконується
до нього (на цьому тримається notrack, спосіб не стежити за пакетом).
Ланцюжки одного хука пакет проходить по черзі за пріоритетом, і будь-який із них
може його відкинути: accept закінчує розгляд лише в цьому ланцюжку, а наступний
на тому самому хуці ще може сказати drop. Тобто «дозволив у своїй таблиці»
не означає «пакет пройде», якщо Docker, firewalld чи інша таблиця поставили
власні правила.
nftables
Section titled “nftables”nftables замінив iptables як основний інструмент (старий синтаксис iptables тепер
часто лише оболонка над тим самим ядровим механізмом). Модель складається з кількох
рівнів:
- таблиця (table) має сімейство адрес:
ip,ip6,inet(обидва одночасно),arp,bridge,netdev; - ланцюжок (chain) містить правила. Базовий ланцюжок зареєстрований на хук
(тип, хук, пріоритет, політика); звичайний викликається командою
jump; - правило (rule) — умови плюс вердикт:
accept,drop,reject,jump,returnта інші. Правила виконуються згори вниз, перший вердикт зупиняє розгляд; - набір (set) — іменований список адрес, портів чи пар, у якому пошук швидкий незалежно від розміру.
Ось повний робочий приклад файрвола для простої топології: клієнт
10.16.1.0/24 — Linux-файрвол — сервер 10.16.2.2.
table inet filter { set web_ok { type ipv4_addr elements = { 10.16.1.2 } }
chain input { type filter hook input priority filter; policy drop; ct state established,related accept ct state invalid drop iif "lo" accept icmp type echo-request limit rate 5/second accept ip saddr 10.16.1.0/24 tcp dport 22 accept }
chain forward { type filter hook forward priority filter; policy drop; ct state established,related accept ct state invalid drop ip saddr @web_ok ip daddr 10.16.2.2 tcp dport 80 counter accept icmp type echo-request counter accept }
chain output { type filter hook output priority filter; policy accept; }}Завантажується командою nft -f файл, перегляд — nft list ruleset.
Політика «за замовчуванням заборонено»
Section titled “Політика «за замовчуванням заборонено»”Можна дозволяти все й перелічувати заборонене
(policy accept плюс правила drop) — так ви ніколи не закриєте всіх
небезпечних шляхів, бо їх не всі знаєте. Або заборонити все й перелічувати дозволене:
policy drop на базовому ланцюжку, а далі лише те, що потрібно. Помилка в
такому файрволі робить щось недоступним, що швидко помічають і виправляють,
а не відкриває тихо. У прикладі вище input і forward закриті політикою,
output відкритий, бо хост довіряє власним процесам (на суворіших
вузлах закривають і його).
Файрвол зі станом
Section titled “Файрвол зі станом”Перше правило в обох ланцюжках — ct state established,related accept. Без нього
файрвол довелося б писати у двох напрямках: дозволити запит клієнта на порт 80
і окремо відповіді сервера з порту 80 на довільний порт клієнта. Так працювали
ранні фільтри пакетів без пам’яті (stateless), і вони були або дірявими, або
виснажливими. Відстеження з’єднань (connection tracking, conntrack) дає ядру
таблицю з’єднань. Для кожного пакета відомо, чи належить він до вже відомого
з’єднання, а правила вказують лише напрямок ініціювання.
Стани ct state:
new: перший пакет, що відкриває з’єднання (для TCP це SYN);established: з’єднання бачили в обидва боки;related: новий потік, пов’язаний зі старим (ICMP-помилка про наше з’єднання, вторинне з’єднання протоколу на кшталт FTP);invalid: пакет, який не вкладається в жоден відомий стан (запізнілий сегмент, порушення послідовності).
Ось як виглядає таблиця після одного запиту клієнта:
curl http://10.16.2.2/ # з клієнтаconntrack -L # на файрволіtcp 6 114 TIME_WAIT src=10.16.1.2 dst=10.16.2.2 sport=59938 dport=80 src=10.16.2.2 dst=10.16.1.2 sport=80 dport=59938 [ASSURED] mark=0 use=1icmp 1 29 src=10.16.1.2 dst=10.16.2.2 type=8 code=0 id=13055 src=10.16.2.2 dst=10.16.1.2 type=0 code=0 id=13055 mark=0 use=1Кожен запис має два набори адрес: «туди» і «назад». Він зберігає, як мала
б виглядати відповідь, і тому відповідь пропускають без окремого правила. Число після 6
(114) — секунди до забування запису. Для UDP, де з’єднання немає, conntrack
теж веде записи за таймером, типово 30 секунд без відповіді, а для встановленого
TCP-з’єднання за замовчуванням аж п’ять діб (nf_conntrack_tcp_timeout_established),
щоб тихе з’єднання не розірвалося між пакетами.
Якщо це правило прибрати, запит проходить, а відповідь сервера
в forward не відповідає жодному дозволу й відкидається політикою. Клієнт
бачить лише таймаут, а лічильник правила для порту 80 зростає: проходить
лише SYN, що щоразу повторюється.
nft delete rule inet filter forward handle 10 # прибрали established (handle видно в nft -a list ruleset)curl -m3 http://10.16.2.2/ # rc=28: таймаутТаблиця conntrack — скінченний ресурс (nf_conntrack_max, типово
десятки чи сотні тисяч записів залежно від пам’яті), і це слабке місце: надмір потоків, зокрема підроблених,
її заповнює, а нові з’єднання відкидаються. Про це далі й у модулі 9.
Діагностика: де зник пакет
Section titled “Діагностика: де зник пакет”Коли «правило є, а не працює», найкорисніше запитати в самого ядра. Почніть
із лічильників: додайте counter до правила й подивіться, чи зростає він.
Якщо цього мало, вмикайте трасування. Правило з meta nftrace set 1 на ранньому хуці вмикає
запис кожного кроку пакета, який nft monitor trace показує в реальному часі:
nft add table inet tracenft "add chain inet trace pre { type filter hook prerouting priority -301; }"nft add rule inet trace pre ip saddr 10.16.1.2 icmp type echo-request meta nftrace set 1nft monitor trace # в іншому терміналіtrace id 12772c1f inet trace pre packet: iif "m16fw-c" ... ip saddr 10.16.1.2 ip daddr 10.16.2.2 ip ttl 64 ...trace id 12772c1f inet trace pre rule ip saddr 10.16.1.2 icmp type echo-request meta nftrace set 1 (verdict continue)trace id 12772c1f inet trace pre policy accepttrace id 12772c1f inet filter forward packet: iif "m16fw-c" oif "m16fw-s" ... ip saddr 10.16.1.2 ip daddr 10.16.2.2 ...trace id 12772c1f inet filter forward rule icmp type echo-request counter packets 1 bytes 84 accept (verdict accept)Тут видно весь шлях: пакет пройшов prerouting, потім став транзитним,
і в forward його впіймало конкретне правило з вердиктом accept. Пріоритет
-301 потрібен, щоб трасувальне правило стояло перед усіма іншими на цьому хуці.
Після налагодження таблицю trace видаліть.
drop чи reject
Section titled “drop чи reject”Обидва відкидають пакет. drop мовчить: клієнт нічого не чує й чекає таймаут.
reject повертає відповідь (TCP RST або ICMP «port unreachable»), і клієнт
дізнається одразу.
nc -zv -w3 10.16.2.2 8080 # політика drop# nc: connect to 10.16.2.2 port 8080 (tcp) timed outnft add rule inet filter forward tcp dport 8080 reject with tcp resetnc -zv -w3 10.16.2.2 8080 # те саме, але з reject# nc: connect to 10.16.2.2 port 8080 (tcp) failed: Connection refused (за 5 мс)Для внутрішньої мережі reject зручніший: застосунок одразу бачить помилку й не
висить на таймауті. Для зовнішньої зазвичай обирають drop: сканер не отримує
підтвердження, що хост живий, і витрачає час на кожен порт. Проте
не переоцінюйте цю «невидимість»: вона лише ускладнює розвідку, а захищає
вас закритий порт, а не мовчання.
Типові атаки
Section titled “Типові атаки”Сканування портів
Section titled “Сканування портів”Сканер шле SYN на кожен порт цілі. Відповідь SYN+ACK означає «відкрито»,
RST означає «закрито», а мовчання означає «відфільтровано» (у файрволі політика drop).
Саме по собі сканування нічого не ламає: воно лише збирає карту
того, що доступне. Захищає від нього мінімум відкритих портів:
усе, що ніхто не просив, закрите політикою. Обмеження швидкості нових з’єднань
(ct state new limit rate) лише уповільнює перебір, а повністю його не зупиняє.
SYN flood і SYN cookies
Section titled “SYN flood і SYN cookies”Звичайний connect() починається з SYN (модуль 11). Отримавши його,
сервер для кожного з’єднання в процесі встановлення резервує місце в черзі
напіввідкритих з’єднань і чекає на третій пакет рукостискання. Якщо слати
тисячі SYN із підроблених адрес, де нікого не чекає відповіді SYN+ACK, черга
заповнюється, і справжні клієнти отримують відмову.
Захист із ядра — SYN cookies. Коли черга переповнюється, сервер перестає зберігати стан: у SYN+ACK він кодує параметри з’єднання в початковому номері послідовності (це і є cookie), а сам нічого не пам’ятає. Справжній клієнт відповість ACK із числом, яке містить це значення, а відповідь підробленого джерела не прийде ніколи й нічого не коштуватиме. Ціна в тому, що під cookie не можна повністю погодити деякі параметри TCP (частина опцій втрачається), тому механізм вмикається лише при переповненні.
Перевіримо. Слухач із мізерною чергою (listen(1)) у просторі імен сервера, а з файрвола
надсилаємо 500 SYN з випадкових підроблених адрес. Для цього потрібен сирий сокет
(IPPROTO_RAW) і вручну пораховані контрольні суми; ось скрипт syn.py:
import socket, struct, random, sysdef csum(b): if len(b) % 2: b += b'\0' s = sum(struct.unpack('!%dH' % (len(b) // 2), b)); s = (s >> 16) + (s & 0xffff); s += s >> 16 return ~s & 0xffffdst, port, n = sys.argv[1], int(sys.argv[2]), int(sys.argv[3])s = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket.IPPROTO_RAW)for i in range(n): src = '10.99.%d.%d' % (random.randint(0, 255), random.randint(1, 254)) tcp = struct.pack('!HHIIBBHHH', random.randint(1024, 65535), port, random.getrandbits(32), 0, 5 << 4, 0x02, 64240, 0, 0) ph = socket.inet_aton(src) + socket.inet_aton(dst) + struct.pack('!BBH', 0, 6, len(tcp)) tcp = tcp[:16] + struct.pack('!H', csum(ph + tcp)) + tcp[18:] ip = struct.pack('!BBHHHBBH4s4s', 0x45, 0, 20 + len(tcp), random.getrandbits(16), 0, 64, 6, 0, socket.inet_aton(src), socket.inet_aton(dst)) ip = ip[:10] + struct.pack('!H', csum(ip)) + ip[12:] s.sendto(ip + tcp, (dst, 0))Слухач, що нічого не приймає, запускають у просторі імен сервера (Python: s.bind(('10.16.2.2', 8081)); s.listen(1) і далі time.sleep(60)), а потім:
ip netns exec srv sysctl net.ipv4.tcp_syncookiesip netns exec fw python3 syn.py 10.16.2.2 8081 500 # 500 SYN зі спуфінгомip netns exec srv nstat -az TcpExtSyncookiesSent TcpExtTCPReqQFullDoCookies TcpExtTCPReqQFullDropip netns exec fw nc -zv -w3 10.16.2.2 8081 # справжній клієнтnet.ipv4.tcp_syncookies = 1TcpExtSyncookiesSent 496TcpExtTCPReqQFullDoCookies 496TcpExtTCPReqQFullDrop 0Connection to 10.16.2.2 8081 port [tcp/tproxy] succeeded!З tcp_syncookies=1 ядро відправило 496 cookie, жоден SYN не відкинуто,
і справжній клієнт підключився. Тепер вимкнемо механізм (sysctl -w net.ipv4.tcp_syncookies=0)
і повторимо з новим слухачем:
TcpExtTCPReqQFullDrop 496nc: connect to 10.16.2.2 port 8082 (tcp) timed out: Operation now in progressТепер 496 SYN відкинуті як переповнення черги, і клієнт не підключився: це і є
відмова в обслуговуванні. Зверніть увагу й на conntrack на файрволі: кожен підроблений SYN
лишив у ньому запис SYN_SENT ... [UNREPLIED]. Якщо файрвол стоїть перед сервером, він
сам стає вузьким місцем, поки ці записи не протухнуть, тому для великих потоків
використовують правила, що не створюють стану (notrack у raw), або окремі
пристрої.
Справжні DDoS-атаки вимірюються гігабітами, і на вашому ядрі їх не зупинити:
канал забито раніше, ніж сервер встигне щось відкинути. Там допомагають провайдерська
фільтрація й спеціалізовані сервіси, а не nftables.
Підміна адреси відправника (спуфінг)
Section titled “Підміна адреси відправника (спуфінг)”IP-адреса відправника в заголовку нічим не захищена: її просто пишуть. Пряма відповідь піде на підроблену адресу, тому спуфінг корисний, коли відповідь не потрібна (SYN flood) або, навпаки, має прийти жертві (ампліфікація нижче).
Захищаються від цього на вході в мережу, перевіряючи, чи могла адреса відправника звідти
прийти. У ядрі для цього є зворотна перевірка маршруту (reverse path filter):
якщо відповідь на цю адресу пішла б через інший інтерфейс, пакет підозрілий.
У nftables це виражає fib:
nft add rule inet raw pre fib saddr . iif oif missing counter dropПравило відкидає пакет, для адреси відправника якого немає маршруту назад через
вхідний інтерфейс. На тесті 50 SYN із випадкових адрес 10.99.x.x, яких
файрвол не знає, уся партія потрапила під нього: counter packets 50 bytes 2000.
Справжній трафік із 10.16.1.2 проходить. У промислових мережах те саме називають
BCP 38, або фільтрацією на вході, і чим частіше провайдери її вмикають, тим менше
спуфінгу в інтернеті.
Ампліфікація
Section titled “Ампліфікація”Тут спуфінг поєднують із протоколом, у якому відповідь сервера набагато більша за запит. Зловмисник шле дрібний запит до відкритого DNS- чи NTP-сервера (або memcached) з підробленою адресою жертви як відправника. Сервер чесно відповідає, але жертві й у набагато більшому обсязі: в окремих протоколів відповідь більша за запит у десятки разів, а в найгірших випадках і значно більше. Так зловмисник множить власну смугу, а жертва отримує відповіді, яких не просила й які нема за чим відфільтрувати.
Проти цього працює лише фільтрація спуфінгу на вході (вона взагалі не дасть таких запитів) і налаштування власних серверів так, щоб вони не відповідали кому завгодно: рекурсивний DNS лише для власних клієнтів, обмеження швидкості, відповіді не більші за запит. «Відкритий рекурсивний резолвер» (модуль 13) через це й вважається помилкою налаштування.
VPN (virtual private network) пересилає пакети IP між двома мережами поверх третьої, яку ви не контролюєте, упаковуючи їх у шифрований тунель: внутрішній пакет шифрується і стає тілом зовнішнього, що йде через інтернет. Зв’язок виглядає як звичайний мережевий інтерфейс із власною адресою, і через нього ходять звичайні маршрути. Застосунки про тунель нічого не знають.
Типові задачі: приєднати віддалену людину до офісної мережі, з’єднати два офіси, захистити трафік у ненадійній мережі.
WireGuard
Section titled “WireGuard”WireGuard з’явився в ядрі Linux з версії 5.6, і простота в ньому навмисна: замість довгого переліку опцій і переговорів про алгоритми він має один фіксований набір криптографії (Curve25519 для обміну ключами, ChaCha20-Poly1305 для шифрування, BLAKE2s для хешів, протокол рукостискання Noise). Якщо виявиться вразливість у примітиві, замінюють версію протоколу, а не домовляються наново.
Ключі замість паролів. Кожна сторона має пару ключів Curve25519. Приватний ключ зберігається на вузлі, а публічний роздається іншій стороні так, як ви роздаєте ключі SSH: заздалегідь, іншим каналом. Сертифікатів і центрів сертифікації немає.
wg genkey | tee private.key | wg pubkey > public.keyІнтерфейс як інтерфейс. wg0 — звичайний мережевий пристрій ядра: він має адресу,
до нього ведуть маршрути, його видно в ip a. Тунель не постійне з’єднання,
а перелік «довірених співрозмовників» (peer). Кожен записаний у конфігурації
публічним ключем, необов’язковою адресою endpoint (IP:порт, куди слати) і
списком AllowedIPs.
AllowedIPs — це і маршрут, і фільтр. WireGuard визначає, кому слати пакет, за цим полем, а не за
конфігурацією маршрутизації ОС.
Це називають cryptokey routing. Пакет, який ви відправляєте на
10.8.0.3, шукають серед усіх AllowedIPs; знайшли peer C, то шифрують ключем C
і шлють на його endpoint. У зворотний бік пакет приходить по UDP, розшифровується
і перевіряється автентичність відправника (тільки володар приватного ключа peer
міг його створити), а потім внутрішня адреса відправника має потрапити в AllowedIPs
саме цього peer. Інакше пакет відкидається. Тому скомпрометований peer B не може
надіслати пакет з адресою peer C, бо його AllowedIPs цього не
охоплюють. Окремих правил файрвола для цього писати не треба: фільтр адрес уже вбудований.
Ось мінімальний тунель між двома «офісами» (прикладна адресація: 10.8.0.1 і 10.8.0.2 на тунелі; 192.168.10.0/24 і 192.168.20.0/24 за ним). Лабораторна B4 збирає саме таку схему в просторах імен.
# вузол Aip link add wg0 type wireguardwg set wg0 private-key ./a.key listen-port 51820 \ peer <ПУБЛІЧНИЙ_КЛЮЧ_B> allowed-ips 10.8.0.2/32,192.168.20.0/24 endpoint 198.51.100.2:51820ip addr add 10.8.0.1/24 dev wg0ip link set wg0 upip route add 192.168.20.0/24 dev wg0 # не обов'язково: wg-quick робить це за AllowedIPswg showinterface: wg0 public key: <ключ A> private key: (hidden) listening port: 51820
peer: <ключ B> endpoint: 198.51.100.2:51820 allowed ips: 10.8.0.2/32, 192.168.20.0/24 latest handshake: 14 seconds ago transfer: 1.21 KiB received, 1.05 KiB sentДеякі особливості, які відрізняють WireGuard від решти:
- Мовчить без запрошення. На пакет від невідомого ключа він не відповідає нічим, тож сканер бачить порт закритим.
- Без стану сеансу.
latest handshake— не «з’єднання встановлено», а час останнього обміну ключами. Ключі оновлюються автоматично, а нової сесії не потрібно. Поки немає трафіку, не летить нічого. - Роумінг. Якщо клієнт змінив IP (телефон перейшов на іншу мережу), сервер
оновлює
endpointза останнім автентифікованим пакетом. - За NAT. Щоб пакети від сервера знаходили шлях назад через NAT клієнта,
клієнт може слати порожні пакети раз на кілька секунд (
PersistentKeepalive). - MTU. Інкапсуляція збільшує пакет, тож MTU тунелю менше за MTU каналу
(у
wg-quickза замовчуванням 1420). Path MTU розбирали в модулі 6.
IPsec — старіша й ширша родина протоколів, яка вбудована в переважну більшість
мережевого обладнання, тому її й досі часто вимагають для з’єднання з чужими
маршрутизаторами. Пакети в ній шифрує й автентифікує ESP (RFC 4303):
це окремий протокол IP із номером 50, тож у tcpdump такий пакет видно як
ESP(spi=…), а не як TCP чи ICMP. Перед тим сторони мають домовитися про
алгоритми й ключі та переконатися, що говорять саме одна з одною
(сертифікатами чи заздалегідь узгодженим спільним ключем, PSK).
Є два режими: транспортний (шифрується лише вміст пакета) і тунельний
(пакет інкапсулюється цілком, це і є «VPN»).
Порівняно з WireGuard, IPsec гнучкіший, але величезний за кількістю комбінацій
параметрів, і саме неузгодженість налаштувань двох кінців найчастіше ламає з’єднання.
У Linux пакети шифрує ядровий механізм XFRM, його стан показують
ip xfrm state і ip xfrm policy. Для цього курсу досить знати, що він є
і що під ним лежать протокол ESP та домовленість про параметри.
VPN і проксі
Section titled “VPN і проксі”Проксі й VPN часто плутають, хоч працюють на різних рівнях.
| Проксі (HTTP, SOCKS) | VPN | |
|---|---|---|
| Рівень | Прикладний, для конкретного застосунку | Мережевий, для всього трафіку через інтерфейс |
| Кому треба знати | Застосунок налаштований на проксі | Застосунок нічого не знає |
| Що переноситься | З’єднання (TCP-потоки, запити) | IP-пакети, зокрема UDP та ICMP |
| Шифрування | Залежить від протоколу | Є за визначенням |
Обидва переносять довіру з мережі на вузол, що стоїть посередині. Комерційний VPN не робить вас анонімним: провайдер замінюється на VPN-сервіс, який бачить те саме, що бачив провайдер. Він захищає від локальної мережі й провайдера, але не від сайту, на який ви заходите, і не від власного браузера.
Як це насправді в Linux
Section titled “Як це насправді в Linux”Усі приклади з файрвола відтворюються в трьох просторах імен: клієнт, файрвол
із ip_forward=1, сервер. Складіть їх руками, як у модулі 3
чи B2, і працюйте з файрвола:
ip netns exec fw nft -f fw.nftip netns exec fw nft list rulesetip netns exec fw conntrack -Lip netns exec fw nft list chain inet filter forward # лічильникиЩо дивитися:
nft list rulesetразом із лічильниками правил показує, яке правило ловить трафік.conntrack -Lіconntrack -E(потік подій) показують записи з’єднань у реальному часі. Пакетconntrackставиться окремо.nft monitor traceіз правиломmeta nftrace set 1відповідає, який саме ланцюжок і правило вирішили долю пакета.ss -ltnпоказує, на яких адресах слухають процеси (перша лінія захисту: те, що не слухає, атакувати нічим), аRecv-Qслухача — довжину черги прийнятих з’єднань.nstat -az | grep -i -E 'syn|listen'показує лічильники ядра:TcpExtSyncookiesSent,TcpExtListenOverflows,TcpExtListenDrops(модуль 3).sysctl net.netfilter.nf_conntrack_maxіnf_conntrack_countдля стеження за заповненням таблиці.
Побачити, що проходить крізь правила, корисно й tcpdump на обох боках файрвола:
пакет, що є на вході й нема на виході, відкинуто всередині.
Типові помилки розуміння
Section titled “Типові помилки розуміння”«Закрив порт у файрволі, значить, сервіс недоступний». Ні: ви закрили його
на одному хуці одного ланцюжка. Сервіс, який слухає 0.0.0.0, доступний через усі
інтерфейси, і шлях, що оминає ваш ланцюжок (інша таблиця, IPv6, інший інтерфейс),
лишається відкритим. Надійніше не слухати там, де не треба.
«accept у моєму ланцюжку гарантує, що пакет пройде». accept закінчує лише
цей ланцюжок. Інший ланцюжок на тому самому хуці (з іншого пріоритету чи з
іншої таблиці) ще може відкинути пакет.
«Правила в input захищають мережу за цим файрволом». Трафік через файрвол іде
в forward, а не в input. input стосується лише самої машини.
«NAT — це файрвол». NAT змінює адреси й створює записи conntrack. Те, що
ззовні не можна відкрити нове з’єднання до приватної адреси, — лише побічний
ефект, і ненадійний: усе залежить від маршрутизації й конфігурації. Захищають
правила forward.
«SYN cookies роблять сервер невразливим до SYN flood». Вони знімають
проблему переповнення черги напіввідкритих з’єднань. Канал, процесор і
conntrack усе ще можуть вичерпатися.
«AllowedIPs у WireGuard — це список адрес, з яких можна підключатися».
Це адреси всередині тунелю, які належать цьому peer: маршрут на відправленні
й фільтр на прийнятті. Звідки він фізично приходить, визначає endpoint.
«VPN робить мене анонімним». VPN переносить довіру з одного посередника на іншого. Сайт бачить адресу VPN-сервера й усе, що ви самі йому скажете (cookie, вхід в обліковий запис).
Перевір себе
Лабораторна
Section titled “Лабораторна”B4. Файрвол і WireGuard: політика nftables «за замовчуванням заборонено» для двох офісів і тунель WireGuard між ними. Тут усе, що розібрано вище, збирається в одну топологію просторів імен.
Матеріал цього модуля знадобиться й у налагодженні зламаної мережі (B7), де одна з несправностей — неправильне правило файрвола.
Джерела
Section titled “Джерела”- Документація nftables (wiki.nftables.org): «Quick reference», «Netfilter hooks», «Connection tracking system»
man nft,man conntrack,man 8 wg,man 8 wg-quick- Donenfeld J., WireGuard: Next Generation Kernel Network Tunnel (NDSS 2017), а також документація на wireguard.com
- RFC 4303 (ESP)
- RFC 4987 (TCP SYN flooding attacks and common mitigations)
- BCP 38 (RFC 2827, Network Ingress Filtering)
- Документація ядра:
Documentation/networking/у вихідному коді Linux