Netfilter forma part del nucli de Linux i permet manipular els diferents paquets de xarxa i el seu flux de processament.
Introducció
Netfilter proporciona diferents “hooks” (punt d’anclatge) en el flux de processament que et permet registrar funcions per aplicar als paquets de xarxa en aquell punt concret de processament.
Netfilter permet filtrar en múltiples nivells de xarxa:
| 4 | Transport | Port | TCP | UDP | ||
|---|---|---|---|---|---|---|
| 3 | Network | Address | IPv4 | IPv6 | ARP | ICMP |
| 2 | Data Link | VLAN | Ethernet | PPP | ||
nftables forma part del projecte netfilter i és la utilitat que et permet interactuar amb nefilter mitjançant l’eina nft.
Entorn de treball
Crea *dues màquines server a {% link “/cloud/isard/” %}.
Ha de tenir les interficies “Default”, “Wireguard VPN” i “provençana1”.
Configura la interficie “provençana1” amb la IP 10.0.0.X/24 on X és el teu número de PC, amb increments de +50.
Per exemple 10.0.0.10 i 10.0.0.60.
Modifica el nom del host:
Surt i torna a entrar a la màquina.
Tables
Una taula a nftables és un espai de noms que conté una col·lecció de chains, sets, maps, flowtables, i stateful objects.
Family
Cada taula ha de tenir assignat una única family d’adreces que defineix els tipus de paquets que processa aquesta taula.
Quan crees una taula pots escollir entre aquestes famílies d’adreces:
ip | La taula filtra paquets IPv4. |
ip6 | La taula filtra paquets IPv6. |
inet | La taula filtra paquets IPv4 i IPv6, i pots utilitzar una única regla pels dos tipus de paquets en el que tenen en comú, per exemple el port. |
arp | La taula filtra paquets ARP a nivell 2, abans que el kernel faci qualsevol gestió a nivell 3. |
bridge | Un pont conecta dos segments Ethernet i els paquets es reenvien en funció de l’adreça Ethernet. Com que el reenviament es fa a la capa 2, aquest encaminament és transparent als protocols de capa superior com pot ser el protocol IP. |
netdev | La taula s’enllaça directament a una única interficie de xarxa enlloc de un protocol. |
ingress | Fa el mateix que netdev, però més fàcil de configurar (des del kernel 5.10). |
No hi ha taules predeterminades per a nftables.
Per tant, l’ordre list tables no torna cap resultat si no has definit cap taula.
UFW
A {% link “/linux/ufw/” %} vas veure que per defecte UFW està deshabilitat.
Crea un firewall amb UFW:
Pots verificar que s’han creat dues taules amb el nom filter:
Encara que les dos taules tenen el mateix nom pertanyen a famílies diferents: ip i ip6.
En la taula filter està la regla que permet connexions entrants (dport) al port 22:
|
Deshabilita el firewall
Eliminar les taules filter:
Com que per defecte la família és ip si no indiques altre cosa, només s’ha borrat la taula ip filter,
Borra la taula ip6 filter:
filter
Quan crees una taula li pots posar qualsevol nom, però per defecte és costum que el nom sigui filter com fa UFW.
Crea el fitxer test.nft:
#!/usr/sbin/nft -f
Executa el fitxer:
Tal com hem vist abans, un cop s’ha afegit la taula l’ordre list tables retorna el nom de la taula:
Pots mostrar la informació d’una taula concreta:
Chains
Com has vist a {% link “/linux/ufw/” %}, un firewall és un conjunt de regles que es processen per decidir que és fa amb un paquet.
Però les regles no s’afegeixen directament a les taules sinó a les cadenes.
Una cadena s’enllaça a un dels hooks que proporciona netfilter per tal de poder filtrar els paquets de xarxes en punts diferents del seu processament.
hooks
Els “hooks” que pots utilitzar al configurar una cadena base, i que et permeten manipular els paquets en un punt concret, són aquests:
ingress | El paquet acaba se ser processat per la controladora de la NIC | ||
prerouting | El paquet no ha estat acceptat pel “flowtable” i està pendent de ser encaminat (a una altre host) o no. | ||
input | El paquet es processarà al mateix host i està pendent de ser processat pel procés corresponent. | ||
forward | El paquet no es processarà al host | ||
output | El paquet s’ha originat per un procés del host. | ||
postrouting | El paquet està a punt de sortir del host. |
Però no tots els paquets passen per tots aquests hooks: no és el mateix processar un paquet ARP que un paquet IP.
Un paquet ARP només passa pels “hooks” input i output (a més de l’ingress), no hi ha “routing”.
{% panel %}
flowchart LR
S(("ARP")) --> I[input] --> H@{shape: fr-rect, label: "Process"} --> O[ouput]--> E(("ARP"))
style S fill:#ffaa50
style E fill:#ffaa50
{% endpanel %}
En canvi, un paquet IP passa per tots els “hooks”:
{% panel %}
flowchart LR
S(("IP")) --> Pre[Prerouting] --> R{Routing} -- no --> I[Input] --> Process@{shape: fr-rect, label: "Process"} --> O[ouput] --> Post
R -- yes --> F[Forward] --> Post[Postrouting] --> E(("IP"))
style S fill:#ffaa50
style E fill:#ffaa50
{% endpanel %}
base chain
Una cadena base s’ha d’enllaçar a alguna d’aquestes fases (o “hooks”).
Afegeix una cadena input a la taula filter:
#!/usr/sbin/nft -f
$ sudo ./test.nft
Verifica que la taula s’ha configurat com tu vols:
; ;
Has creat un cadena “input” que s’enllaça al hook input.
{% panel %}
flowchart LR
S(("IP")) --> Pre[Prerouting] --> R{Routing} -- no --> I[Input] --> Process@{shape: fr-rect, label: "Process"} --> O[ouput] --> Post
R -- yes --> F[Forward] --> Post[Postrouting] --> E(("IP"))
style I fill:#f44
style S fill:#ffaa50
style E fill:#ffaa50
{% endpanel %}
Per tant, veuràs tots els paquets IPv4 que no s’encaminen a una altre adreça.
default policy
Totes les cadenes “base” tenen una política per defecte que es pot especificar al crear la cadena.
Aquesta és la política que s’aplicarà a un paquet si no hi ha cap regla en la cadena que decideixi que fer amb un paquet.
Si no dius res, per defecte la cadena es crea amb policy accept.
Només pots escollir entre dues polítiques predeterminades:
Només pots escollir entre dues polítiques predeterminades:
accept | El paquet seguirà el seu camí. |
drop | El paquet serà eliminat. |
Pots canviar la política per defecte d’una cadena:
#!/usr/sbin/nft -f
; ;
Actualitza nftables:
A partir d’ara nftables no permet cap paquet d’entrada i perds la connexió a la màquina virtual.
Per sort, aquesta configuració només funciona mentre la màquina està funcionat.
Apaga la màquina, arranca de nou i connecta’t.
Verifica que no hi ha cap taula:
output hook
També pots registrar la cadena de sortida:
#!/usr/sbin/nft -f
Output chain s’enganxa al hook output i veurà els paquets de sortida un cop s’han processat:
{% panel %}
flowchart LR
S(("IP")) --> Pre[Prerouting] --> R{Routing} -- yes --> I[Input] --> Process@{shape: fr-rect, label: "Process"} --> O[ouput] --> Post
R -- no --> F[Forward] --> Post[Postrouting] --> E(("IP"))
style I fill:#f44
style O fill:#f44
style S fill:#ffaa50
style E fill:#ffaa50
{% endpanel %}
ingress hook
A diferència dels “hooks” anteriors, un ingress hook és específic d’un dispositiu de xarxa.
Has de crear una taula de la familia netdev:
#!/usr/sbin/nft -f
$ sudo nft list chains
Com que la cadena és específica d’un dispositiu de xarxa, és obligatori especificar el dispositiu on s’enganxarà la cadena.
type
Totes les cadenes que hem creat són de tipus filter:
; ;
Aquest tipus es fa servir per filtrar paquets de les families arp, bridge, ip, ip6 o inet.
L’altre tipus és route que es fa servir per redirigir els paquets en base a un camp de la capçalera IP o si es modifica la marca del paquet.
El tipus route només es pot fer servir amb les famílies ip, ip6 o inet
priority
Si defineixes més d’una cadena pel mateix “hook” el valor priority indica quina s’utilitzarà primer.
Quan més petit sigui el número, la cadena s’executarà abans.
Afegeix una cadena input-ssh que anirà abans que la cadena input:
#!/usr/sbin/nft -f
# ...
# ...Rules
Les regles prenen mesures sobre els paquets de xarxa (p. ex. acceptant-los o deixant-los caure) en funció de si coincideixen amb els criteris especificats.
Cada regla consta de zero o més expressions seguides d’una o més declaracions:
Expressions: Cada expressió prova si un paquet coincideix amb un camp de càrrega útil específic o metadades de paquet/flux. Les expressions múltiples s’avaluen linealment d’esquerra a dreta: si la primera expressió coincideix, llavors s’avalua la següent expressió i així successivament.
Si arribem a l’expressió final, aleshores el paquet coincideix amb totes les expressions de la regla i s’executen les declaracions de la regla.
Declaracions Cada declaració fa una acció, com ara establir la marca netfilter, comptar el paquet, registrar el paquet o emetre un veredicte com acceptar o deixar el paquet o saltar a una altra cadena.
Igual que amb les expressions, diverses declaracions s’avaluen linealment d’esquerra a dreta: una sola regla pot fer diverses accions utilitzant diverses sentències.
Tingues en compte que una declaració de veredicte per la seva naturalesa posa fi a la regla.
Afegeix una regla a la cadena output:
-
ip daddr 8.8.8.8és una expressió que ha de fet match: paquets IP que tenen com adreça destí 8.8.8.8 (daddr= destination address) -
counterés una declaració: va contant el número de bytes que han activat la regla.
Quan enumeres les regles d’una cadena pots utilitzar modificadors per traduir adreces IP a noms DNS, protocols TCP, etc.
; ;
Afegir un comptador és una tècnica molt útil per verificar si una regla funciona:
Fes un ping a 8.8.8.8 i verifica que la regla s’ha activat:
; ;
Un comptador compta tant el nombre total de paquets com el total de bytes que ha vist des de l’últim restabliment.
Fes un ping a dns.google i verifica que la regla s’ha activat:
; ;
Fes un ping a 1.1.1.1 i verifica que la regla no s’ha activat:
; ;
Afageix la regla repetida:
Si torne a fer un ping a google pots veure que s’han activat les dos regles:
; ;
Les regles es processen de dalt a baix.
Elimina el comptador de la primera regla i torna a fer un ping:
; ;
El que has fet és afegir dos regles noves!
El que fa el fitxer és afegir regles, etc.
Modifica el fitxer:
#!/usr/sbin/nft -f
Pots verur que el primer que fem és eliminar-ho tot amb flush ruleset 🙂!
Però les regles no estan només per contar paquets, sinó per acceptar o rebutjar paquets amb drop.
Si fas de nou un ping l’última regla no es processa i el comptador no s’activa.
; ;
Com que les regles es processen de dalt a baix, la segona regla elimina el paquet: el paquet ja no és processa més en la cadena de regles i no serà acceptat per continuar el seu camí i ser enviat a 8.8.8.8.
Comentaris i variables
A continuació modifica l’script afegint comentaris i variables:
#!/usr/sbin/nft -f
# Google Public DNs
Pots veure que els comentaris han desaparegut i enlloc de les variables hi ha els valors definits a les variables:
; ;
Fes un ping a 8.8.8.8 i un altre a 8.8.4.4, i verifica que la regla ip daddr { … } funciona:
; ;
ssh
Mira quina és la IP origen de la connexió ssh:
En el meu cas la IP és 10.0.29.109.
Modifica l’script per tal de que només permeti connexions ssh desde la màquina WSL:
#!/usr/sbin/nft -f
La regla ssh funciona, sinó ara mateix hauries perdut la connexió.
Pots veure que la regla té:
- 2 expressions:
tcp dport 22iip saddr != 10.0.29.109 - 1 declaració:
drop
Verifica que desde la màquina box-2 ja no et pots conectar per ssh.
Avançat
systemd
nftables és un servei del sistema que es controla a través de systemd.
El servei executa l’script /etc/nftables.conf.
Reemplaça el fitexer nftables.conf:
Arrenca el firewall amb systemctl:
A partir d’ara sempre que arrenquis la màquina el firewall estarà actiu amb aquesta configuració.
Per aquest motiu les proves sempre les has de fer amb el fitxer test.nft.
Activitat
1.- Modifica el firewall perquè per defecte només accepti connexions d’entrada ssh:
{% sol %}
#!/usr/sbin/nft -f
;
Has d’afegir la regla iifname "lo" accept perquè alguns serveis bàsics del sistema operatiu funcionen amb la interfície “loopback”, com per exemple, la resolució de noms.
{% endsol %}
2.- Arrenca un servidor nginx i verifica que ni la màquina wsl ni box-2 poden accedir al servidor.
3.- Configura nftables perquè també accepti connexions entrants tcp al port 80:
{% sol %}
;
{% endsol %}
4.- Verifica que encara que acceptes els paquets destinats al port 80, no et pots connectar als servidors http d’altres màquines:
Tracing
Quan tenim un problema amb la configuració de nftables a més d’utilitzar un contador (“counter”) per veure que una regla s’ha activat podem fer un “tracing”.
Afegeix al final de la cadena l’expressió: meta nftrace set 1
;
Torna a carregar nftables i comença a monitoritzar l’entrada de paquets: nft monitor trace.
Pots veure que la regla no s’activa (els paquest ssh sòn acceptats abans que li toqui el torn a aquesta regla).
Peró si executes curl https://10.0.0.50, llavors el paquet si que arriba a l’última regla:
Ara podem provar perquè no funciona curl google.es.
Obre una altre terminal:
Pots veure que el firewall no accepta el paquet de tornada de saddr 8.8.8.8:
conntrack
Fins ara estem aplicant regles sense estat: només tenim en compte el paquet que estem filtrant.
Però hi ha molts paquets que estan relacionats perquè pertanyen a una mateixa connexió.
Recordes que al principi al explicar el hooks hem parlat de la “flowtable”?
Netfilter té un sistema de seguiment de connexions que permet rastrejar quins paquets pertanyen a la mateixa connexió.
Pots afegir aquesta regla a la cadena input: ct state established,related accept
;
Aquesta regla diu que:
ct- És una expressió de tipus “connection tracking”state- Fem un tracking de l’estat de la connexióestablished, related— Són els estats de conntrack que volem fer “match”
Ara ja funciona:
|
docker
Contiua llegint aquest document
Expressions
En aquesta secció veurem diferents expressions per fet “match” en els paquets que s’estan filtrant.
Metainformació
Un meta-selector et permet fer match respecte informació que el host local té sobre el paquet (com ara com o quan es va rebre) i que no necessàriament està en el mateix paquet.
També en alguns casos pots incorporat metainformació tal com s’explica a Setting packet metainformation.
Nosaltres només veurem uns quants selectors, però en aquest enllaç tens tota la informació al respecte: Matching packet metainformation.
Socket UID / GID
Pots fer un match per uid o gid de l’usuari que ha creat el socket mitjançant les keywords skuid i skgid.
A continuació:
- Crea un nou usuari alice.
- Configura el firewall perquè no permeti a l’usuari alice realitzar connexions de sortida:
Verifica que el firewall funciona:
|
Modifica el firewall perquè faci servir el uid de l’usuari enlloc del seu nom, i verifica que funciona.
time
També pots fer match per time, day o hour.
D’aquesta manera pots controlar l’accés a determinats serveis en hores determinades o generar un log d’accéss en hores o dies no habituals.
A continuació tenim una regla que no permet cap connexió de sortida entre les 00:00 i les 06:00:
Modifica l’hora del sistema perquè s’activi la regla:
Headers
La informació completa està a Matching packet headers.
Ethernet headers
La informació de nivell 2 només està disponible en el camí d’entrada (ingress, prerouting, input)
Modifica el fitxer index.html del servidor nginx perquè respongui amb “Hello from box-1”.
Verifica que box-2 té accés al servidor:
Mira quina és l’adreça MAC de box-2:
Modifica el firewall perquè no permeti connexions de box-2 mitjançant un filtre MAC:
Verifica que la regla funciona:
IPv4
També pots fer un match segons l’adreça IPv4 origen i destí:
Verifica que box-2 no és pot conectar ni per HTTP ni per ping.
Modifica la IP de box-2 per evitar el firewall:
Enlloc d’eliminar els paquets entrants de la màquina box-1 pots eliminar els de sortida a la màquina box-1 mitjançant una cadena output.
Modifica el firewall i verifica que box-2 tampoc és pot conectar ni per HTTP ni per ping.
ICMP
Si vols pots eliminar totes les sol·licituds d’eco ICMP (conegudes popularment com a pings):
Verifica que funciona:
Pots utilitzar nft describe per trobar les paraules clau de tipus icmp disponibles.
Transport protocol
Per filtrar en un protocol de capa 4 com TCP, pots fer-ho amb aquesta regla:
Verifica que la màquina client pot fer ping, però no curl.
Torna a arrencar la màquina box-1 per poder conectar-te amb ssh.
TCP/UDP
Pots crear un regla per eliminar tots el paquets TCP en un rang de ports:
Amb aquesta regla server no pot establir una connexió HTTPS:
També pot utilitzar el conjunt anònim l4proto i un expressió th (transport header) per fer match a la vegada de paquets TCP i UDP dirigits al port 53 (DNS ) - el protocol fa servir els dos ports.
Pots veure que funciona, encara que el que està bloquejant és el local resolver cache:
;
Connection tracking
La informació completa està a: Matching connection tracking stateful metainformation
Les expressions conntrack (ct) permeten configurar tallafocs amb estat de tal manera que es poden identificar tots els paquets que pertanyen a una mateixa connexió.
Modifica el firewall per fer un tracking de les connexions:
Pots veure el tracking de la connexió ssh:
Rate limiting
Pots limitar el trànsit per paquets o bytes mitjançant limit.
Per packet
L’exemple següent mostra una regla que només permet com a màxim 1 paquet per segon de sol·licituts HTTP – és un exemple! :
Modifica la pàgina del servidor de box-1:
| Pots verificar que el firewall limita el número de paquets que s’admeten per segon protegint al servidor nginx de que el saturin amb peticions HTTP:
Per bytes
També pots limitar pel número de bytes per tal de limitar l’ample de banda del servidor.
Baixa el llibre “War and Peace”:
Modifica el firewall:
Verifica que el firewall limita la velocitat de baixada de fitxers:
Sentències
A continuació veurem algunes sentències que actuen sobre els paquets que han fet “match” en una regla.
Rejecting traffic
Enlloc d’eliminar un paquet el pots rebutjar:
Pots verificar que el servidor informa al client que rebutja la connexió amb un paquet “port unreachable”:
No funciona amb wireguard
Si vols pots canviar el motiu pel qual no s’accepta la connexió:
Logging traffic
Pots tenir un registre dels paquets que s’han processat mitjançant l’acció log.
Aquesta és la regla més senzilla que pots utilitzar:
Si fas una petició HTTP desde el client pots veure que el paquet queda registrat al syslog:
| |
A continuació tens una regla típica de match, log i accept de trànsit ssh entrant per deixar registrades totes les conexions ssh que s’han fet al servidor:
El prefix indica el string inicial que s’utilitza com a prefix per al missatge que es registra.
Si mires el registre pots veure que la teva connexió ssh ha quedat registrada (SRC=10.0.29.109):
|