Puffy is back !

L'heureux tour de PF :)

Geoffroy Desvernay – Ecole Centrale de Marseille

CC-BY

pourquoi ?

Avant 2005: OpenBSD/PF

…tout allait bien…

(c'était mieux avant …)

Puis on a eu les moyens d'acheter "mieux"

"mieux": n.m. (fin du XXème siècle) sorte d'incantation consistant à investir en matériel et licences sensée faire gagner du "temps ETP" (de la masse salariale).

A ce propos, Juniper® fait très bien:

  • des jeux de cartes très rigolos avec des blagues sur Cisco®
  • des tapis de souris
  • payer le support (par an, tous les ans trois zéros pour une boite)
  • de l'IPv4 en udp
  • du "sflow" (== netflow mais pour rire seulement), comme HP d'ailleurs
  • La peinture grise mat inrayable

Le nôtre fermait les connexions TCP tout seul (~25-30% de connexions "drop"ées)

Leur support a même reproduit le bug (donc ils ont bien un support)

jamais eu de suite (pour le bug hein, la maintenance était toujours payante)

Bilan:

  • X0000€ + 10ans *X000€
  • + 1 gros bug
  • + une bonne semaine ETP de debug
  • + un "bon" support bien payant

= … ben rien.

on a résolu le bug en débranchant l'appareil. 🚽

Si c'est tres cher, achètes toi un étage de juristes ou passe passe …au libre !

Je ne suis pas anti juniper, la preuve:

Le support Cisco® nous a quand même fait le coup du rayon cosmique pour expliquer un reboot violent…

chez ^(HPE?|Aruba), on a droit a 1024 espoirs d'ACLs sur notre super cœur de réseau (pour remplacer les ~2600 ACL's réelles qu'on utilisait sur le ciscosmique)

… et pas de moyen sérieux pour debugguer les acls, pas d'IPfix

(c'était 2 fois moins cher que cisco, on a 4 fois moins…)

Chez ArrubHPE on a au moins du filtrage IPv6 correct… si on a pas besoin de filtrage IPv4

Chui de bonne humeur, je parle pas du coup des alimentations :)

Au fait, 'ISSU' c'est bien le mot anglais pour 'problème' non ?

Bon… soyons constructifs ;-)

Donc, retour à OpenBSD …

OpenBSD: système d'exploitation BSD, fork de NetBSD en 1995

Axé sur la sécurité et la cryptographie

Sur des bonnes machines pas chères et garanties 7 ans, Merci MATINFO !!!

Du raid1 sur un firewall 🦄

Mise a jour majeure ~ tous les 6 mois depuis 23 ans, deux versions supportées

Ca doit être compliqué !

Installation: 10mn (automatisable avec un fichier de réponse)

BSD style: une fois installé (1Go) y'a tout ce qu'il faut

Pour le reste,

pkg_add vim zsh sl …

Mises à jour mineures:

syspatch # ne pas oublier <ENTREE>

OpenBSD, ca fait aussi…

  • openbgpd
  • openospfd
  • carp (VRRP on steroids)
  • mpls (pas testé)
  • load-balancing/failover de services avec relayd/carp (== VRRP+ha-proxy)
  • IPFIX avec (pflow, == netflow v10)

… et tout ce qu'on sait faire avec une machine *nix !

  • man !!!
  • un vrai terminal^H^H^H^H^H^H^H^H shell
  • mise à jour des machines toujours possible 10 ans après l'achat !
  • smartctl, systat, snmp, …
  • top, ping, tcpdump, mtr, nmap, munin, nagios, rsync, …
  • sh, ksh, zsh, perl, python, php, …
  • <HS>KDE, gnome, libreoffice, … </HS>

… et Packet Filter (PF)

  • Syntaxe claire
  • Facilement redondant (pfsync)
  • Performant (pas encore à 10G, en cours)

Lisible

pass in quick on bge0 to 192.0.2.0/30 port 80
block drop in quick on bge0
pass quick

souple

# variables
ext_if="bge0"
int_if="bge1"
serveurs_web="{ 192.0.2.1 192.0.2.26 }"

pass in quick on $ext_if to $serveurs_web port 80
pass in quick on $int_if
pass out quick

efficace

belle_maman="{ 203.0.113.66 2001:DB8:f1f1::f0le }"
# tables (max 200000 adresses/réseaux *par défaut*)
table <mechants> file "/etc/pf.tables/liste.zeus_cc" \
		file "/etc/pf.tables/liste.ransomeware" \
		{ $belle_maman }

# on colle les méchants avec du miel
pass in quick on $ext_if from <mechants> rdr-to $honeypot
# et on interdit aux gentils de leur parler
block return in quick on $int_if to <mechants>

pratique

Euh… on t'a dit ? ce matin y'a du personnel en formation dans une salle élèves, mais ils doivent aller sur le site a qui on a donné les IP du personnel ? Non ? paskeu c'est dans 5 minutes…

match out on $ext_if from $salle_formation \
		to $srv_formation \
		nat-to $ip_autorisee_la_bas

concis: a perimètre comparable

  • cisco+juniper: 385+312 acl's pour IPv4
  • pf: 129 lignes 'pass|block|match' (576 une fois parsées) pour:
    • la même chose qu'avant
    • + IPv6 partout
    • + le filtrage du wifi/filaire guest
    • + des milliers d'IPs filtrées (botnets etc)

arrubahpe:

rule 710 permit udp source 198.51.100.0 0.0.1.255 source-port eq sunrpc
rule 750 permit tcp source 198.51.100.0 0.0.1.255 destination-port eq 135
rule 755 permit udp source 198.51.100.0 0.0.1.255 destination-port range netbios-ns netbios-dgm
rule 760 permit tcp source 198.51.100.0 0.0.1.255 destination-port eq 139
rule 765 permit tcp source 198.51.100.0 0.0.1.255 destination-port eq 445
rule 770 permit udp source 198.51.100.0 0.0.1.255 destination-port eq sunrpc
rule 775 permit tcp source 198.51.100.0 0.0.1.255 destination-port eq sunrpc
rule 780 permit tcp source 198.51.100.0 0.0.1.255 destination-port eq 2049
rule 785 permit udp source 198.51.100.0 0.0.1.255 destination-port eq 2049
rule 790 permit tcp source 198.51.100.0 0.0.1.255 destination-port range 4045 4049
rule 795 permit udp source 198.51.100.0 0.0.1.255 destination-port range 4045 4049
rule 800 permit udp source 198.51.100.0 0.0.1.255 source-port eq 4047 destination-port gt 1024

J'autorise tout le réseau (1024 espoirs d'acl max)

A multiplier par le nombre de réseaux clients

ports_filers_tcp="{139 445 111 2049 4045:4049}"
ports_filers_udp="{135:137 111 2049 4045:4049}"

personnels="{ 198.51.100.0/24 2001:DB8:b0b::/64 2001:DB8:ba11::/64 }"
etudiants="{ 203.0.113.0/24 2001:DB8:ffff:e1e::/64 }"

table <filers> { 203.0.113.12/28 2001:DB8:f11e::/64 }
table <clients> { $personnels $etudiants }

pass in quick on $int_if proto tcp from <clients> \
	to <filers> port $ports_filers_tcp
pass in quick on $int_if proto udp from <clients> \
	to <filers> port $ports_filers_udp
pass in quick on $int_if proto udp from <clients> \
	port 4047 to <filers> port > 1024

Puissant

# rate-limiting
pass in quick proto {icmp icmp6} keep state (\
				pflow,\
				source-track rule,\
				max-src-states 10,\
				max-src-nodes 50)
pass in proto {tcp udp} to $dns_srv port 53 \
				set prio 4 \
				keep state (pflow, \
				source-track rule, \
				max-src-states 50, \
				max-src-nodes 500)
			

# import d'une liste d'IP depuis un fichier
# ( généré par domain2cidr.pl - http://ow.ly/TJKD30cKLBY )
table <mailsuckers> persist file "/etc/pf.tables/google.list" \
		file "/etc/pf.tables/mailru.list" \
		file "/etc/pf.tables/bluemail.list"
…
block return in quick on $ext_if tcp from <mailsuckers> \
		to $submission port {submission imaps}
			

# anti brute-force
block in quick drop from <casse-bonbons>
# 5 syn / 40 secondes max, sinon blacklist
pass in quick to $webauth port 443 keep state \
	(max-src-conn-rate 5/40, \
	overload <casse-bonbons> flush global)
			

# cron pour expiration des tables auto-remplies
*/15 * * * *	/sbin/pfctl -t casse-bonbons -T expire 3600
			

nat, redirection, …

match out on raimu inet from <nat_nets> nat-to $nat_addr
pass in on raimu proto {tcp,udp} to $oldns port 53 rdr-to $newdns

(re-)Routage (=~ cisco route-map):

pass in log quick on serveurs from $vpn_clients \
	to $vpn_dests route-to ($int_v4_if 198.51.100.1)

Forte intégration de pf avec tout le reste (tags, labels, tables pf depuis BGP, …)

On peut même simuler un réseau foireux ;)

pass in quick on $ext_if tcp from os { MacOS, Windows, Sega } \
			to $www_srv probability 10%

Redondant

quick ou pas quick ?

quick == first match wins

sinon == last match wins

C'est VOUS qui voyez… on peut aussi mélanger

Groupes d'interfaces

ifconfig vlan4 group postes
ifconfig vlan36 group postes
ifconfig vlan3333 group raimu
ifconfig vlan666 group guests
pass in quick on postes to …
block in on raimu …

Répartition de charge intelligente

relayd est comparable à ha-proxy. Il peut travailler au niveau 3 (il ajoute des règles PF de redirections vers les 'backends' valides), et/ou au dessus.

redirect wwws_lb {
        listen on $rproxy4_addr tcp port https
        listen on $rproxy6_addr tcp port https
        pftag HTTPS
        sticky-address
        forward to <webhosts> check tcp port https
}

relayd + DSR

DSR: Direct Server Return

  1. pf ne modifie pas les paquet L3
  2. Les serveurs ont tous (sur lo0) l'IP de destination
  3. Une autre IP de chaque serveur permet l'encapsulation L2
  4. Les paquets de réponse peuvent passer ailleurs

route to <webhosts> check tcp port https interface vlan234

Plusieurs tables de routage ?

ifconfig bge1 203.0.113.55/24 rtable 1

route -T1 add default 203.0.113.254

route -T1 exec mtr -t 8.8.8.8

# pf.conf
pass in quick on rdomain 0 from <via_freebox> rtable 1 keep state (floating)

Intégré dans openbgpd, openospfd, …

Chez nous

  • Routage redondant (bgpd.conf = 72 lignes avec filtres)
  • Routage interne (ospfd.conf + ospf6d.conf)
  • Filtrage
    • D'entrée de site
    • Des serveurs publics
    • Des réseaux "non-fiables" (wifi, guests, …)
  • relayd L3 (HA/HP) pour https, smtp, imap, dns (+ ldap ailleurs)

+ deux soekris (collector ! 15W, 2cœurs, 2G RAM) pour le confort…

  • Boot < 60s
  • faux masters DNS
  • resolveur DNS
  • replique ldap
  • + relayd pour ldap

Cout - matériel et licences

Nos machines: PE R230 + carte X520 2*10G DAC + carte 4*1G

Autant vous dire qu'il en reste sous la pédale…

Utilisé: ~1G RAM, max 12% de 1 CPU pour ~40000 etats

A benchmarker: apu2 ou equivalent (X00€, 3*1G, 2 ou 4 G RAM)

Combien d'ETP ?

~ 1 semaine ETP pour la mise en place

+ 6 jours de conseil/config/formation bienveillante d'Evolix (petite multinationale marseillaise)

Une ou deux mises à jour / an (~1 heure * 2 machines) SANS COUPURE !

… MAJ bios et firmwares comprise 🕶

Au quotidien

vipf # edite && verifie && charge && \
# idem sur le second && git commit

logpf # tcpdump -n -e -ttt -i pflog0
Jun 21 22:55:26.438328 rule 5/(match) block in on vlan196: \
147.94.24.6.60400 > 147.94.19.60.3493: R 2726651265:2726651265(0) win 0 (DF)