Tags

ipfw shell

Powered by

blOg
maRkdown
awK
shEll

25/01/2020

[ ipfw shell ]

202001251100 ipfw shell

ipfw: modern FreeBSD, services

Pour illustrer mon propos, il me faut un service. Un truc simple qui fasse udp/tcp et ipv6/ipv4. J'aurais pu utiliser nc -k -l 1234 mais il n'est pas possible de faire tcp/udp/ipv6/ipv4 en même temps (sauf à lancer 4 nc). J'ai donc jeter mon dévolu sur daytime fourni par inetd:

$ sed -n '/#daytime/s/^.//p' /etc/inetd.conf > /tmp/inetd.conf
$ sudo /usr/sbin/inetd -d -l /tmp/inetd.conf
[ snip du debug ]

Depuis la même machine, je peux m'y connecter en tcp:

$ nc -d -6 localhost 13
$ nc -d -4 localhost 13

et vérifier l'udp:

$ nc -z -u -6 localhost 13
$ nc -z -u -4 localhost 13

On doit voir une trace de ces connexions dans la console où est lancé inetd (je ne sais pas pourquoi l'udp génère 4 lignes de debug). Toute tentative depuis une machine distante se soldera par un échec car l'accès au port "13" n'est pas autorisé.

Service

J'aime assez le concept de mur de feu piloté par des variables. Mon service ssh est géré par les variables suivantes:

Le nommage de ces variables suit cette règle: 'firewall' + nom_du_service + 4|6 ou 'firewall' + 't' + nom_du_service. Cette règle à (au moins) 2 défauts:

Pour simplifier mes règles, je pars du principe qu'un service est à la fois tcp et udp. Pour les noms/numéros de port je peux utiliser les noms dans le script mais comment définir un service qui écoute sur le port 3128 ? Ou pire, sur plusieurs ports ? Voici ce que je voudrais pouvoir faire:

 1	firewall_services="proxy http,dns ,foo bar, baz ,, ,  ,   mail_int daytime"
 2	
 3	firewall_proxy_port=3128
 4	firewall_proxy4="192.168.0.0/24 172.16.0.0/24, 172.16.2.0/24"
 5	
 6	firewall_dns_ports=",53,853, 8853"
 7	
 8	firewall_foo_ports=9000
 9	firewall_t_foo=t_bar
10	
11	firewall_bar_port="9001 9002"
12	firewall_t_bar=/etc/rc.conf.d/bar.*
13	
14	firewall_baz_port=9003
15	firewall_t_baz=t_bar
16	
17	firewall_mail_int_ports="smtp 587 imap 993 4190"
18	firewall_t_mail_int=t_ssh

Sans configuration particulière, les services "http" et "daytime" écoutent sur leur port respectif en mode "ouvert aux 4 vents".

Je veux aussi pouvoir traiter les gros doigts ("port" vs "ports") et les listes ("1 2 3" vs "a,b, c ,, d").

Code

60	set_31
61	$fwcmd -f flush
62	
63	$add pass via lo0
64	$add pass out keep-state
65	
66	for svc in $(echo ${firewall_services:-} | tr ',' ' '); do
67		eval "port=\"\${firewall_${svc}_ports:-\${firewall_${svc}_port:-$svc}}\""
68		[ "$port" = $svc ] || port=$(echo $port | sed -E -e 's/[ ,]+/,/g' -e 's/(^,|,$)//')
69	
70		eval "table=\"\${firewall_t_${svc}:-}\""
71		case "$table" in
72			t_*) [ $table = "t_$svc" ] || $add pass in dst-port $port lookup src-ip $table keep-state; continue;;
73		esac
74		
75		eval "table_src=\"\${firewall_${svc}6:-} \${firewall_${svc}4:-} \${firewall_t_${svc}:-}\""
76		if [ "$table_src" = "  " ]; then
77			$add pass in dst-port $port keep-state
78			continue
79		fi
80	
81		$fwcmd table t_$svc create type addr or-flush
82		load_table_addr t_$svc $table_src
83		$add pass in dst-port $port lookup src-ip t_$svc keep-state
84	done
85	
86	$add pass { proto ipv6-icmp or proto icmp }
87	$add deny log in

Au final

 1	$ sudo service ipfw restart
 2	Flushed all rules.
 3	00500 allow via lo0
 4	00000 allow out keep-state :default
 5	added: 172.16.2.0/24 0
 6	added: 192.168.0.0/24 0
 7	added: 172.16.0.0/24 0
 8	00000 allow in dst-port 3128 dst-ip lookup src-ip t_proxy keep-state :default
 9	00000 allow in dst-port 80 keep-state :default
10	00000 allow in dst-port 53,853,8853 keep-state :default
11	00000 allow in dst-port 9000 dst-ip lookup src-ip t_bar keep-state :default
12	00000 allow in dst-port 9001,9002 dst-ip lookup src-ip t_bar keep-state :default
13	00000 allow in dst-port 9003 dst-ip lookup src-ip t_bar keep-state :default
14	00000 allow in dst-port 25,587,143,993,4190 dst-ip lookup src-ip t_ssh keep-state :default
15	00000 allow in dst-port 13 keep-state :default
16	01500 allow { proto ipv6-icmp or proto icmp }
17	01600 deny log in
18	Firewall rules loaded.
19	Firewall logging enabled.

On retrouve bien les règles définies au paragraphe "Service". Je pense que 'dst-ip' n'est qu'un bug d'affichage.

Si depuis une machine distante je teste le port "13" en udp ("nc -z -u -4 ip.de.la.machine 13") je peux voir qu'une règle dynamique a bien été ajoutée:

$ sudo ipfw -d -S show | grep ' 13 '
01400    8    332 set 0 allow in dst-port 13 keep-state :default
01400    8    332 (9s) STATE udp 172.16.200.193 10930 <-> 172.16.100.50 13 :default

C'est un peu plus compliqué avec le tcp car la règle est supprimée dès que la connexion est terminée (et elle se termine très vite :).

01400    7     406 (1s) STATE tcp 172.16.200.193 24467 <-> 172.16.100.50 13 :default

Commentaires: https://github.com/bsdsx/blog_posts/issues/3


Lien vers ce billet