Create role for nftables router
Some checks failed
continuous-integration/drone/push Build is failing

This commit is contained in:
jeltz 2021-03-10 03:16:51 +01:00
parent 22c970d9b4
commit 592d3a630a
10 changed files with 394 additions and 0 deletions

View file

@ -26,6 +26,8 @@ bird_router_prefsrc: 10.132.0.254
bird_ospf_ifaces: bird_ospf_ifaces:
ens19: ens19:
stub: true stub: true
ens20:
stub: true
gs: gs:
type: pointopoint type: pointopoint
cost: 2000 cost: 2000

View file

@ -0,0 +1,7 @@
---
- name: Reload nftables
become: true
systemd:
name: nftables.service
state: reloaded
...

View file

@ -0,0 +1,38 @@
---
- name: Install nftables
become: true
apt:
name: nftables
state: latest
- name: Create nftables.d directory
become: true
file:
path: /etc/nftables.d
state: directory
owner: root
group: root
mode: u=rwx,g=rx,o=
- name: Configure nftables
become: true
template:
src: "{{ item }}.j2"
dest: "/etc/{{ item }}"
loop:
- nftables.d/10-vars.conf
- nftables.d/20-blacklist.conf
- nftables.d/30-rp-filter.conf
- nftables.d/40-signup.conf
- nftables.d/50-filter.conf
- nftables.d/60-nat.conf
- nftables.conf
notify: Reload nftables
- name: Enable and start nftables
become: true
systemd:
name: nftables.service
state: started
enabled: true
...

View file

@ -0,0 +1,5 @@
{{ ansible_managed | comment }}
flush ruleset
include "/etc/nftables.d/*.conf"

View file

@ -0,0 +1,55 @@
{{ ansible_managed | comment }}
## Interconnexion
# Réseaux d'interconnexion
define interco_v4 = { 192.168.0.0/31, 192.168.0.2/31, 10.129.0.0/16 }
define interco_v6 = { 2a09:6840:129::0/48 }
## Administration
# Réseaux d'administration
define adm_v4 = { 10.128.0.0/16, 10.133.0.0/16 }
define adm_v6 = { 2a09:6840:128::0/48, 2a09:6840:133::0/48 }
# Serveurs de centralisation des journaux
define syslog_adm_v4 = { 10.128.0.51 }
define syslog_adm_v6 = { 2a09:6840:128::251 }
# Adresses des bastions autorisés
define bastion_v4 = { 10.128.0.224, 10.133.0.250 }
define bastion_v6 = { 2a09:6840:133::250 }
## Services
# Réseaux de services privés
define svc_v4 = { 10.132.0.0/16 }
define svc_v6 = { 2a09:6840:132::0/48 }
## Adhérents
# Réseaux des adhérents
define member_v4 = { 10.50.0.0/16 }
define member_v6 = { 2a09:6840:50::0/48 }
# Sous-réseau d'inscription des adhérents
define signup_v4 = { 10.50.0.0/16 }
define signup_v6 = { 2a09:6840:50::0/48 }
# Hôtes déclencheurs d'accès à Internet pour inscription
define signup_trigger_v4 = { 1.1.1.1 }
define signup_trigger_v6 = { 2606:4700:4700::1111 }
## NAT
# Interface sur laquelle appliquer le NAT
define wan_iface = "ens18"
define member_priv_v4 = { 10.50.0.0/16 }
define member_nat_v4 = 92.222.211.198
define any_nat_v4 = 92.222.211.198

View file

@ -0,0 +1,31 @@
{{ ansible_managed | comment }}
table inet blacklist {
set blacklist_v4 {
type ipv4_addr
flags interval
}
set blacklist_v6 {
type ipv6_addr
flags interval
}
# Compteur des paquets ignorés car les adresses étaient en liste noire
counter blacklist {}
# Cette chaîne est appliquée très tôt (avant le conntrack entre autres)
# afin de limiter autant que possible l'impact des hôtes en liste noire
# (notamment en cas d'attaque par déni de service)
chain filter {
type filter hook prerouting priority -310
policy accept
# On ne journalise pas pour limiter la charge sur les serveurs de
# journalisation
ip saddr @blacklist_v4 counter name blacklist drop
ip6 saddr @blacklist_v6 counter name blacklist drop
}
}

View file

@ -0,0 +1,20 @@
{{ ansible_managed | comment }}
# Simule le comportement de rp_filter=1, mais avec support d'IPv6 (ce qui
# n'est pas le cas de l'implémentation du noyau)
#
# https://wiki.nftables.org/wiki-nftables/index.php/Routing_information
# Le "eq 0" n'est pas très joli, mais ça semble être la façon
# "normale" de le faire
# Voir : https://netdevconf.info/1.2/slides/oct6/08_nft_netdev12_florian.pdf
table inet reverse_path_filter {
chain filter {
type filter hook prerouting priority -300
policy accept
fib saddr . iif oif eq 0 \
log prefix "rp-filter" group 0 counter drop
}
}

View file

@ -0,0 +1,48 @@
{{ ansible_managed | comment }}
table inet signup {
set triggered {
type ether_addr
timeout 24h
}
set allowed {
type ether_addr
timeout 30m
}
chain trigger {
log prefix "signup-trigger" group 0
add @triggered { ether saddr }
add @allowed { ether saddr }
}
chain filter {
# Si l'adresse MAC est temporairement autorisée, on ne bloque pas
ether saddr @allowed return
# Si l'adresse n'est pas autorisée (cf. règle précédente) mais qu'elle
# a accédé récemment à un déclencheur, cela signifie qu'elle a déjà
# « consommé son crédit », donc on bloque
ether saddr @triggered drop
# Si la machine tente de se connecter à un des hôtes déclencheurs,
# on enregistre son adresse MAC et on laisse passer la connexion
ip daddr $signup_trigger_v4 goto trigger
ip6 daddr $signup_trigger_v6 goto trigger
# La machine a tenté de se connecter vers une destination qui ne
# déclenche pas l'accès à Internet, donc on bloque
drop
}
chain forward {
type filter hook forward priority -10
policy accept
ip saddr $signup_v4 goto filter
ip6 saddr $signup_v6 goto filter
}
}

View file

@ -0,0 +1,164 @@
{{ ansible_managed | comment }}
table inet filter {
chain conntrack {
ct state invalid counter drop
ct state { established, related } counter accept
}
chain input_from_anywhere {
# C'est pas gentil de bloquer ICMP(v6), alors on le fait pas
ip protocol icmp counter accept
ip6 nexthdr icmpv6 counter accept
# Wireguard
udp dport { 5412, 5413 } counter accept
# Temporaire
tcp dport 22 counter accept
}
chain input_from_interco {
# Il faut n'accepter que le multicast OSPF et des trucs
# comme ça
counter accept
}
chain input_from_member {
log prefix "in-from-member" group 0
}
chain input_from_svc {
log prefix "in-from-svc" group 0
}
chain input_from_adm {
log prefix "in-from-adm" group 0
tcp dport 22 counter accept
}
chain input {
type filter hook input priority 0
policy drop
iif lo accept
jump conntrack
jump input_from_anywhere
ip saddr $interco_v4 goto input_from_interco
ip6 saddr $interco_v6 goto input_from_interco
ip saddr $member_v4 goto input_from_member
ip6 saddr $member_v6 goto input_from_member
ip saddr $svc_v4 goto input_from_svc
ip6 saddr $svc_v6 goto input_from_svc
ip saddr $adm_v4 goto input_from_adm
ip6 saddr $adm_v6 goto input_from_adm
}
chain output {
type filter hook output priority 0
policy accept
}
chain forward_to_interco {
ip saddr $interco_v4 accept
ip6 saddr $interco_v6 accept
}
chain forward_to_member_re2o_ports {
# TODO
}
chain forward_to_member {
# Les adhérents peuvent communiquer entre eux
ip saddr $member_v4 accept
ip6 saddr $member_v6 accept
# L'administration n'a pas accès à l'extérieur
ip saddr $adm_v4 drop
ip6 saddr $adm_v6 drop
# Les ouvertures de ports sont générées par re2o
goto forward_to_member_re2o_ports
}
chain forward_to_svc {
}
chain forward_to_adm {
log prefix "fwd-to-adm" group 0
# Seules les machines du réseau d'administration peuvent accéder au
# réseau d'administration
ip saddr != $adm_v4 drop
ip6 saddr != $adm_v6 drop
# Les bastions ont accès à toute l'administration
ip saddr $bastion_v4 accept
ip6 saddr $bastion_v6 accept
# Tous les serveurs ont accès au collecteur de logs
ip daddr $syslog_adm_v4 tcp dport 20514 accept
ip daddr $syslog_adm_v4 udp dport 514 accept
ip6 daddr $syslog_adm_v6 tcp dport 20514 accept
ip6 daddr $syslog_adm_v6 udp dport 514 accept
# ntp + apt + dns
}
chain forward_to_inet {
log prefix "fwd-to-inet" group 0
# On évite certains problèmes de spam
ip saddr $member_v4 udp dport 25 drop
ip6 saddr $member_v6 udp dport 25 drop
# Les adhérents ont accès à internet
ip saddr $member_v4 accept
ip6 saddr $member_v6 accept
# Les réseaux de services ont accès à Internet
ip saddr $svc_v4 accept
ip6 saddr $svc_v6 accept
}
# Remarque : on utilise 'drop' et pas 'reject' pour conntrackd
chain forward {
type filter hook forward priority 0
policy drop
iif lo accept
jump conntrack
# http://lists.netfilter.org/pipermail/netfilter-buglog/2017-August/003868.html
#ip daddr vmap {
# $interco_v4 : goto forward_to_interco,
# $member_v4 : goto forward_to_member,
# $svc_v4 : goto forward_to_svc,
# $adm_v4 : goto forward_to_adm,
#}
ip daddr $interco_v4 goto forward_to_interco
ip6 daddr $interco_v6 goto forward_to_interco
ip daddr $member_v4 goto forward_to_member
ip6 daddr $member_v6 goto forward_to_member
ip daddr $svc_v4 goto forward_to_svc
ip6 daddr $svc_v6 goto forward_to_svc
ip daddr $adm_v4 goto forward_to_adm
ip6 daddr $adm_v6 goto forward_to_adm
goto forward_to_inet
}
}

View file

@ -0,0 +1,24 @@
{{ ansible_managed | comment }}
table ip nat {
chain prerouting {
type nat hook prerouting priority -100
policy accept
}
chain snat_to_wan {
log prefix "snat-to-wan" group 0
ip saddr $member_priv_v4 snat $member_nat_v4 persistent
snat $any_nat_v4 persistent
}
chain postrouting {
type nat hook prerouting priority 100
policy accept
# oifname $wan_iface goto snat_to_wan
}
}