B4. Файрвол і WireGuard
Модуль 16 розрізняє файрволи, що пропускають усе
крім явно забороненого, і ті, що забороняють усе крім явно дозволеного.
Перші зручні, другі безпечні. Тут ви зробите другий: маршрутизатор fw між LAN,
DMZ і «інтернетом», де новому з’єднанню потрібен окремий дозвіл, відповіді
пропускає таблиця відстеження з’єднань, а все відкинуте лишає слід у журналі.
Потім з’єднаєте два офіси тунелем WireGuard і переконаєтеся, що назовні
видно лише зашифровані UDP-пакети.
Після цієї роботи ви зможете:
- написати набір правил
nftablesіз політикоюdropуinputіforward; - застосувати таблицю відстеження з’єднань (
ct state) так, щоб відповіді проходили, а нові з’єднання в забороненому напрямку ні; - перевірити правила не «мені ніби працює», а з обох боків: що дозволене проходить і що заборонене не проходить;
- налаштувати тунель WireGuard між двома маршрутизаторами: ключі,
allowed-ips, маршрути; - довести через
tcpdump, що між офісами ходить лише шифрований UDP.
Так виглядає перший робочий тиждень адміністратора малої мережі: закрити те, що не мало бути відкритим, і з’єднати філії, не виставляючи внутрішні адреси в інтернет.
Завдання
Section titled “Завдання”Робота має дві незалежні частини.
Частина 1. Файрвол
Section titled “Частина 1. Файрвол” lan 10.0.1.2 dmz 10.0.2.2 │ │ │ .1 │ .1 ┌────┴─────────────────────┴───┐ │ fw │ └──────────────┬───────────────┘ │ 203.0.113.2 │ out 203.0.113.1| Простір імен | Адреса | Роль |
|---|---|---|
fw |
10.0.1.1/24 (LAN), 10.0.2.1/24 (DMZ), 203.0.113.2/24 (зовні) |
маршрутизатор-файрвол |
lan |
10.0.1.2/24, шлюз 10.0.1.1 |
внутрішня мережа |
dmz |
10.0.2.2/24, шлюз 10.0.2.1 |
публічні служби |
out |
203.0.113.1/24, маршрут у 10.0.0.0/16 через 203.0.113.2 |
«інтернет» |
Політика, яку має реалізувати fw:
| Звідки → куди | Дозволено (нові з’єднання) |
|---|---|
lan → dmz |
TCP 80, 443 |
lan → out |
TCP 80, 443; ping |
out → dmz |
TCP 80, 443 |
lan → сам fw |
TCP 22; ping |
out → сам fw |
лише ping |
| усе інше | заборонено, відкинуте пишеться в журнал |
Відповіді на дозволені з’єднання проходять автоматично (stateful).
Зокрема, out не може сам відкрити з’єднання до lan, а dmz не відкриває
нічого нікуди.
Частина 2. Тунель WireGuard
Section titled “Частина 2. Тунель WireGuard” pc1 ── o1 ═══ інтернет (198.51.100.0/24) ═══ o2 ── pc2 192.168.1.10 tun 10.200.0.1 10.200.0.2 192.168.2.10| Простір імен | Адреси |
|---|---|
o1 |
192.168.1.1/24 (офіс 1), 198.51.100.1/24 («інтернет»), 10.200.0.1/24 (тунель) |
o2 |
192.168.2.1/24 (офіс 2), 198.51.100.2/24 («інтернет»), 10.200.0.2/24 (тунель) |
pc1 |
192.168.1.10/24, шлюз 192.168.1.1 |
pc2 |
192.168.2.10/24, шлюз 192.168.2.1 |
Тунель працює на UDP-порту 51820. Офісні маршрутизатори зовні мають відповідати лише на нього.
Назви просторів імен і адреси зафіксовані, їх перевіряє check.sh.
Назви інтерфейсів, таблиць і ланцюжків оберіть самі.
Що зробити:
- Побудувати топологію частини 1 (чотири простори імен, три пари
veth), маршрути,ip_forwardуfw. - Написати в
fwтаблицюnftablesз двома базовими ланцюжками (inputіforward), політикамиdropі правилами за таблицею вище. - Додати відстеження з’єднань і журнал відкинутого.
- Перевірити кожен рядок таблиці двічі: що дозволене проходить і що недозволене ні.
- Побудувати топологію частини 2 і підняти тунель WireGuard.
- Закрити офісні маршрутизатори: зовні лише UDP 51820.
- Показати в
tcpdumpна «інтернет»-інтерфейсі, що між офісами ходить лише UDP.
Чого робити не треба. NAT у цій роботі не потрібен (out має маршрут
до внутрішніх мереж, див. B2). IPv6, iptables,
ротація ключів, динамічні списки адрес (set) і автоматизація через
wg-quick не потрібні.
Обмеження. Тільки nft (без iptables). Політика drop має бути саме
політикою ланцюжка, а не правилом у кінці: тоді додана без уваги служба
лишається закритою.
Готово, коли sudo ./check.sh не показує жодного «НІ». Перевірка
ганяє і ті з’єднання, що мають проходити, і ті, що ні.
Перед початком
Section titled “Перед початком”- Прочитайте модуль 9 (відстеження з’єднань) і модуль 16: розділи про фільтрацію, stateful і WireGuard.
- Встановіть інструменти:
sudo bash setup/provision.sh(потрібніnftables,wireguard-tools,tcpdump,python3). - Потрібні права root. Частину 1 можна робити будь-де, де працюють простори
імен і
nftables. Частина 2 потребує модуля ядраwireguard: у багатьох контейнерах його немає, тоді потрібна віртуальна машина. Перевіртеsudo bash setup/probe-net-env.sh, рядок про інтерфейс WireGuard.sudo ./check.sh fwперевіряє лише частину 1. - Працюйте в каталозі
labs/b4-firewall-vpn.
-
Топологія частини 1.
Побудуйте простори й дроти (див. приклад у B2), призначте адреси, додайте маршрути:
Terminal window sudo ip netns exec lan ip route add default via 10.0.1.1sudo ip netns exec dmz ip route add default via 10.0.2.1sudo ip netns exec out ip route add 10.0.0.0/16 via 203.0.113.2sudo ip netns exec fw sysctl -w net.ipv4.ip_forward=1Поки правил немає,
fwпросто маршрутизатор, і все з усім бачиться. Перевірте:lanпінгуєout,outпінгуєdmz. Це ваша контрольна точка «до». -
Політика «за замовчуванням заборонено».
Скелет таблиці: два базові ланцюжки з політикою
drop.table inet filter {chain input {type filter hook input priority 0; policy drop;}chain forward {type filter hook forward priority 0; policy drop;}}Збережіть у файл і завантажте:
sudo ip netns exec fw nft -f fw.nft. Тепер не працює нічого, навітьpingна самfw: так і має бути. Політикаdropвідкидає все, що не дозволило жодне правило. -
Stateful: відповіді.
Перше правило у двох ланцюжках:
ct state established,related acceptct state invalid dropinputще потребуєiif lo accept. Без цих правил навіть дозволене з’єднання рвалося б: запит пройде, а відповідь ні, бо нового дозволу для зворотного напрямку ви не писали.Запишіть: що означають стани
new,established,relatedіinvalid? Чому відповіді не потребують власних правил? -
Дозволені сервіси.
Додайте правила за таблицею політики. Приклад одного:
ip saddr 10.0.1.0/24 ip daddr 10.0.2.0/24 tcp dport { 80, 443 } acceptПорядок правил має значення: перше, що збіглося, визначає долю пакета. Перевіряйте кожен крок. Запустіть слухача й під’єднайтеся до нього:
Terminal window sudo ip netns exec dmz python3 -m http.server 80 &sudo ip netns exec lan curl -m 3 http://10.0.2.2/і так само для порту, який мав бути закритий (
python3 -m http.server 8080уdmzіcurlна нього зlan). -
Журнал відкинутого.
Останнім правилом кожного ланцюжка поставте журнал і лічильник:
log prefix "fw-forward-drop: " counterПакет, що дійшов до цього правила, лишає запис у журналі ядра й збільшує лічильник, а потім відкидається політикою. Зробіть заборонену спробу й подивіться:
Terminal window sudo ip netns exec lan curl -m 2 http://10.0.2.2:8080/sudo ip netns exec fw nft list ruleset # лічильник вирісsudo dmesg | grep fw-forward-dropУ журналі буде рядок такого вигляду:
fw-forward-drop: IN=to-lan OUT=to-dmz SRC=10.0.1.2 DST=10.0.2.2 ... PROTO=TCP SPT=51360 DPT=8080 ... SYN -
Перевірка з двох боків.
Складіть таблицю «дозволено / заборонено»: для кожного рядка політики один пакет, що має пройти, і один, що ні. Не забудьте про негативні випадки з «відповіддю, якої ніхто не чекав»:
outнамагається з’єднатися з високим портом уlan.Запишіть: чим небезпечне правило
tcp dport 1024-65535 accept, яке колись називали «для відповідей»? -
Топологія частини 2 й ключі.
Побудуйте
o1,o2,pc1,pc2за таблицею (тунель поки не чіпайте),ip_forwardв обох офісах, маршрути наpc. Згенеруйте ключі:Terminal window umask 077wg genkey | tee o1.key | wg pubkey > o1.pubwg genkey | tee o2.key | wg pubkey > o2.pubПриватні ключі нікому не показуйте й не комітьте в репозиторій.
-
Тунель.
Інтерфейс створюйте всередині простору імен:
Terminal window sudo ip netns exec o1 ip link add wg0 type wireguardsudo ip netns exec o1 wg set wg0 private-key o1.key listen-port 51820 \peer "$(cat o2.pub)" allowed-ips 10.200.0.2/32,192.168.2.0/24 \endpoint 198.51.100.2:51820 persistent-keepalive 5sudo ip netns exec o1 ip addr add 10.200.0.1/24 dev wg0sudo ip netns exec o1 ip link set wg0 upsudo ip netns exec o1 ip route add 192.168.2.0/24 dev wg0Дзеркально для
o2: його ключ,allowed-ips 10.200.0.1/32,192.168.1.0/24,endpoint 198.51.100.1:51820, адреса10.200.0.2/24, маршрут до192.168.1.0/24. Перевірте:pc1пінгуєpc2, аsudo ip netns exec o1 wg showпоказуєlatest handshakeі ненульові лічильникиtransfer.Запишіть: що означає
allowed-ipsу WireGuard: і «кого пускати з цього вузла», і «куди пакети слати»? Навіщо поруч іщеip route? -
Офіс закрито, тунель зашифровано.
Дайте
o1іo2файрволinputіз політикоюdrop: дозволеноlo, відповіді (ct state), UDP 51820 і все, що прийшло з інтерфейсу тунелю. Потім на «інтернет»-інтерфейсіo1запустіть захоплення:Terminal window sudo ip netns exec o1 tcpdump -ni <інтерфейс> 'icmp or udp port 51820'і пропінгуйте
pc2зpc1. Ви побачите лишеUDP, жодногоICMP. Порівняйте із захопленням наwg0, де пінг видно відкритим: усередині тунелю пакети ті самі, зовні вони вже шифровані.
Перевірка
Section titled “Перевірка”sudo ./check.sh # обидві частиниsudo ./check.sh fw # лише файрволsudo ./check.sh vpn # лише тунельСкрипт нічого не будує й від назв не залежить. Щоб не вимагати від вас
запущених служб, він на кілька секунд сам запускає тимчасові TCP-слухачі
на тестових портах (80, 443, 22, 8080, 25 та 50000) у просторах dmz, out,
lan і fw і потім зупиняє їх. Перевіряється (частина 1):
- структура: у
inputіforwardполітикаdrop, уforwardє правилоct state ... established, в обох ланцюжках є правилоlog; - що має проходити: усі рядки таблиці політики;
- що не має проходити (це важливіше): порти, яких немає в таблиці
(22 і 8080 у DMZ, 25 назовні), нові з’єднання з
outуlan, усі з’єднання зdmz,sshдоfwіз зовнішньої сторони й DMZ, і «відповіді, якої ніхто не чекав» на високий портlan.
Частина 2: адреси, інтерфейс WireGuard і рукостискання в обох офісах,
pc1 ↔ pc2, а в каналі між офісами є UDP і немає ICMP, а також
те, що офісні маршрутизатори не приймають TCP-з’єднань на своїй
«інтернет»-адресі.
Якщо правило надто широке, скрипт покаже, яке саме «заборонене» з’єднання
вдалося. Якщо забули ct state, не працюватимуть відповіді, а якщо зробили
«відповіді» правилом за номером порту, впаде перевірка високого порту.
Часті помилки
Section titled “Часті помилки”Політика drop без ct state. Запит проходить, відповідь ні:
curl висить. Перше правило в кожному ланцюжку — ct state established,related accept.
Забули iif lo accept у input. Локальні сервіси на самому fw
перестають бачити одне одного.
drop у кінці замість політики. Працює, але порожній ланцюжок
або зайва порожня таблиця нічого не закривають. Політика ланцюжка
надійніша.
Порядок правил. Якщо правило accept стоїть раніше за те, що мало
заборонити, заборона не спрацює. nft list ruleset показує порядок і лічильники.
Журнал порожній. Див. застереження в етапі 5: у просторі імен
треба ввімкнути nf_log_all_netns на господарі.
Тунель не піднімається. wg show без latest handshake: перевірте,
що публічний ключ сусіда (не свій) стоїть у peer, що endpoint
правильний і що файрвол пропускає UDP 51820. Завжди запускайте
tcpdump на «інтернет»-інтерфейсі: бачите пакети, що йдуть, але не
повертаються, значить, закрито на тому боці.
Пакети в тунель не потрапляють. Без ip route ... dev wg0 ядро
не знає, що 192.168.2.0/24 лежить за тунелем: allowed-ips лише
каже WireGuard, кого пускати й куди слати, коли пакет уже потрапив
в інтерфейс.
Далі, якщо цікаво
Section titled “Далі, якщо цікаво”Додайте NAT на fw і приберіть маршрут у out. Замініть перелік
портів на іменований набір (set) і подивіться, як правила скорочуються.
Обмежте швидкість нових з’єднань (limit rate) і подивіться, як log
реагує на сканування портів (nmap із out). У тунелі спробуйте
змінити MTU й подивіться на фрагментацію: WireGuard додає до кожного
пакета власні заголовки.