Freifunk

Gateway Konfiguration

alles zu fuß weil lol egal

System

apt-get install sudo tmux git
timedatectl set-timezone Europe/Berlin
hostnamectl set-hostname gwNN
useradd -m -U -G sudo user
  • Set PermitRootLogin in /etc/ssh/sshd_config to no; append AllowGroups sudo
  • Match /etc/hosts with the previously set hostname. Do not forget to set a FQDN (e.g. gw01.marburg.freifunk.net).

/etc/sudoers.d/99-ffmr

# do not as root for password
root	ALL=(ALL:ALL) NOPASSWD: ALL

# Allow members of group sudo to execute any command without providing password
%sudo	ALL=(ALL:ALL) NOPASSWD: ALL

GRUB

Serial Console

/etc/default/grub

GRUB_CMDLINE_LINUX_DEFAULT="console=ttyS1,115200"

GRUB_TERMINAL="serial"
GRUB_SERIAL_COMMAND="serial --speed=115200 --unit=1 --word=8 --parity=no --stop=1"

update-grub

/etc/sysctl.d/99-ffmr.conf

# IP forwarding
net.ipv4.ip_forward = 1
net.ipv4.conf.default.rp_filter = 1
net.ipv4.conf.all.rp_filter = 1

net.ipv6.conf.all.forwarding = 1

# Disable IPv6 autoconf
net.ipv6.conf.all.autoconf = 0
net.ipv6.conf.default.autoconf = 0
net.ipv6.conf.eth0.autoconf = 0

net.ipv6.conf.all.accept_ra = 0
net.ipv6.conf.default.accept_ra = 0
net.ipv6.conf.eth0.accept_ra = 0

# Do not process traffic on bridges with iptables
net.bridge.bridge-nf-call-arptables = 0
net.bridge.bridge-nf-call-ip6tables = 0
net.bridge.bridge-nf-call-iptables = 0

/etc/systemd/network/10-eth0.link

[Match]
MACAddress=aa:aa:aa:aa:aa:aa

[Link]
Name=eth0

sudo update-initramfs -k all -u

General networking packages

sudo apt-get install bridge-utils isc-dhcp-server radvd unbound

Freifunk packages

sudo apt install batctl fastd

/etc/network/interfaces

# Freifunk Marburg
auto ffmr-br
iface ffmr-br inet static
  bridge_ports    none
  bridge_fd       0
  bridge_maxwait  0
  address         10.128.0.XXX
  netmask         255.255.192.0
  mtu             1280

iface ffmr-br inet6 static
  address 2a06:4b00:1000::a80:XXX
  netmask 64

allow-hotplug ffmr-bat
iface ffmr-bat inet6 manual
  pre-up    /sbin/modprobe batman-adv
  post-up   /sbin/brctl addif ffmr-br $IFACE
  post-up   /usr/sbin/batctl -m $IFACE orig_interval 10000
  # pick one, or none, lol idk
  # post-up /usr/sbin/batctl -m $IFACE gw server 96mbit/96mbit
  # post-up batctl meshif $IFACE gw server 960mbit/960mbit
  pre-down  /sbin/brctl delif ffmr-br $IFACE || true

allow-hotplug ffmr-fastd-1
iface ffmr-fastd-1 inet6 manual
  hwaddress 00:01:01:80:00:02
  post-up ip link set dev ffmr-fastd-1 up
  post-up batctl meshif ffmr-bat if add $IFACE
  post-up ip link set dev ffmr-bat up

allow-hotplug ffmr-fastd-2
iface ffmr-fastd-2 inet6 manual
  hwaddress 00:01:01:80:00:02
  post-up ip link set dev ffmr-fastd-2 up
  post-up batctl meshif ffmr-bat if add $IFACE
  post-up ip link set dev ffmr-bat up

allow-hotplug ffmr-fastd-3
iface ffmr-fastd-3 inet6 manual
  hwaddress 00:01:01:80:00:02
  post-up ip link set dev ffmr-fastd-3 up
  post-up batctl meshif ffmr-bat if add $IFACE
  post-up ip link set dev ffmr-bat up

allow-hotplug ffmr-fastd-4
iface ffmr-fastd-4 inet6 manual
  hwaddress 00:01:01:80:00:02
  post-up ip link set dev ffmr-fastd-4 up
  post-up batctl meshif ffmr-bat if add $IFACE
  post-up ip link set dev ffmr-bat up

allow-hotplug ffmr-fastd-5
iface ffmr-fastd-5 inet6 manual
  hwaddress 00:01:01:80:00:02
  post-up ip link set dev ffmr-fastd-5 up
  post-up batctl meshif ffmr-bat if add $IFACE
  post-up ip link set dev ffmr-bat up

allow-hotplug ffmr-fastd-6
iface ffmr-fastd-6 inet6 manual
  hwaddress 00:01:01:80:00:02
  post-up ip link set dev ffmr-fastd-6 up
  post-up batctl meshif ffmr-bat if add $IFACE
  post-up ip link set dev ffmr-bat up

/etc/fastd/ffmr

fastd-base.conf

log level debug;
log to syslog level debug;

hide ip addresses yes;
hide mac addresses yes;

method "null";
method "salsa2012+umac";
method "null+salsa2012+umac";

mtu 1312;
mode tap;

include "secret.conf";
include peers from "peers";

secret.conf

secret "0000…ffff";

peers/GW-gwXX

key "ffff…0000";

systemctl cat fastd@

# /etc/systemd/system/fastd@.service
[Unit]
Description=Fast and Secure Tunnelling Daemon (fastd no %I)
After=network.target

[Service]
Type=notify
ExecStart=/usr/bin/fastd \
            --syslog-level info \
            --syslog-ident fastd@%I \
            --status-socket /var/run/ffmr-fastd-%I.status \
            --interface ffmr-fastd-%I \
            --bind any:1000%I \
            --config /etc/fastd/ffmr/fastd-base.conf
ExecReload=/bin/kill -HUP $MAINPID

[Install]
WantedBy=multi-user.target
$ for i in $(seq 1 6); do systemctl enable --now fastd@$i; done

fastd Peer Synchronization

Only if the yolokeyserver is not in use.

systemctl cat fastd-peers-pull.service
# /etc/systemd/system/fastd-peers-pull.service
[Unit]
Description=Refresh fastd ffmr peers

[Service]
WorkingDirectory=/etc/fastd/ffmr/peers
ExecStart=/usr/bin/git pull
ExecStartPost=/usr/bin/systemctl reload fastd@1
ExecStartPost=/usr/bin/systemctl reload fastd@2
ExecStartPost=/usr/bin/systemctl reload fastd@3
ExecStartPost=/usr/bin/systemctl reload fastd@4
ExecStartPost=/usr/bin/systemctl reload fastd@5
ExecStartPost=/usr/bin/systemctl reload fastd@6


systemctl cat fastd-peers-pull.timer
# /etc/systemd/system/fastd-peers-pull.timer
[Unit]
Description=Refresh fastd ffmr peers every five minutes

[Timer]
OnCalendar=*:0/5

[Install]
WantedBy=timers.target

/etc/dhcp/dhcpd.conf

# The ddns-updates-style parameter controls whether or not the server will
# attempt to do a DNS update when a lease is confirmed. We default to the
# behavior of the version 2 packages ('none', since DHCP v2 didn't
# have support for DDNS.)
ddns-update-style none;

option domain-name "marburg.link";

default-lease-time 600;
max-lease-time 3600;

# Use this to send dhcp log messages to a different log file (you also
# have to hack syslog.conf to complete the redirection).
log-facility local7;

subnet 10.128.0.0 netmask 255.255.192.0 {
	authoritative;
	range 10.128.XX.1 10.128.XX.254;
	option routers 10.128.0.XX;
	option domain-name-servers 10.128.0.XX;
	option ntp-servers ntp01.marburg.link;
	option interface-mtu 1280;
}

/etc/default/isc-dhcp-server

INTERFACESv4="ffmr-br"
sudo systemctl enable isc-dhcp-server
sudo systemctl start isc-dhcp-server

/etc/radvd.conf

interface ffmr-br {
	AdvSendAdvert on;
	MaxRtrAdvInterval 200;
	AdvLinkMTU 1280;
	prefix 2a06:4b00:1000::/64 {
	};
	RDNSS 2a06:4b00:1000::a80:2XX {
	};
}
sudo systemctl enable radvd
sudo systemctl start radvd

/etc/systemd/system/alfred@.service

# This .service-file does not define a socket per instance because of compatibility reasons with older versions of alfred. Therefore can't start multiple instances.
[Unit]
Description=A.L.F.R.E.D. (connection %I)
After=network.target

[Service]
Type=simple
ExecStart=/usr/sbin/alfred -m -i %i-br -b %i-bat
ExecStartPost=/bin/chown root:alfred /var/run/alfred.sock
ExecStartPost=/bin/chmod 0660 /var/run/alfred.sock

[Install]
WantedBy=multi-user.target
sudo systemctl enable alfred@ffmr.service
sudo systemctl start alfred@ffmr.service

Batadv-vis Server

/etc/systemd/system/batadv-vis@.service

[Unit]
Description=batadv-vis server (connection %I)
Requires=alfred@%i.service

[Service]
Type=simple
ExecStart=/usr/sbin/batadv-vis -s -i %i-bat

[Install]
WantedBy=multi-user.target
sudo systemctl enable batadv-vis@ffmr
sudo systemctl start batadv-vis@ffmr

Alfred Announce

git clone https://github.com/freifunk-mwu/ffnord-alfred-announce.git

/etc/sudoers.d/map

map ALL = NOPASSWD: /usr/sbin/batctl

/etc/systemd/system/alfred-announce@.service

[Unit]
Description=announces information on the batman network (connection %I)
Requires=alfred@%i.service

[Service]
Type=oneshot
User=map
ExecStart=/home/map/ffnord-alfred-announce/announce.sh -b %i-bat -i %i-br -f %i-fastd

/etc/systemd/system/alfred-announce@ffmr.timer

[Unit]
Description=announces information on the batman network (connection %I)
Requires=alfred@%i.service

[Timer]
OnBootSec=0
OnUnitActiveSec=60
Persistent=false

[Install]
WantedBy=timers.target
sudo systemctl enable alfred-announce@ffmr.timer
sudo systemctl start alfred-announce@ffmr.timer

NAT + (iptables | nftables)

iptables

sudo iptables -t nat -A POSTROUTING -s 10.128.0.0/18 -o eth0 -j SNAT --to-source XXX.XXX.XXX.XXX
sudo iptables -A FORWARD -o eth0 -m state --state ESTABLISHED,RELATED -j ACCEPT
sudo iptables -A FORWARD -o eth0 -m state --state INVALID -j DROP
sudo iptables -A FORWARD -i eth0 -o ffmr-br -p tcp -m tcp --tcp-flags SYN,RST SYN -j TCPMSS --clamp-mss-to-pmtu
sudo iptables -A FORWARD -i ffmr-br -o eth0 -p tcp -m tcp --tcp-flags SYN,RST SYN -j TCPMSS --clamp-mss-to-pmtu
sudo iptables -A FORWARD -p tcp -s 10.128.0.0/18 --dport 25 -j REJECT
sudo iptables -A FORWARD -d 10.0.0.0/8 -o eth0 -j REJECT --reject-with icmp-net-unreachable
sudo iptables -A FORWARD -d 172.16.0.0/12 -o eth0 -j REJECT --reject-with icmp-net-unreachable
sudo iptables -A FORWARD -d 192.168.0.0/16 -o eth0 -j REJECT --reject-with icmp-net-unreachable

sudo ip6tables -A FORWARD -i eth0 -o ffmr-br -p tcp -m tcp --tcp-flags SYN,RST SYN -j TCPMSS --clamp-mss-to-pmtu
sudo ip6tables -A FORWARD -i ffmr-br -o eth0 -p tcp -m tcp --tcp-flags SYN,RST SYN -j TCPMSS --clamp-mss-to-pmtu
sudo ip6tables -A FORWARD -p tcp -s 2a06:4b00:1000::/56 --dport 25 -j REJECT
sudo ip6tables -t raw -A PREROUTING -m rpfilter -j ACCEPT
sudo ip6tables -t raw -A PREROUTING -j DROP
sudo ip6tables -A FORWARD -d fc00::/7 -o eth0 -j REJECT --reject-with icmp6-no-route

sudo iptables-save | sudo tee /etc/iptables.up.rules
echo -e '#!/bin/sh\n/sbin/iptables-restore < /etc/iptables.up.rules' | sudo tee /etc/network/if-pre-up.d/iptables
sudo chmod +x /etc/network/if-pre-up.d/iptables

sudo ip6tables-save | sudo tee /etc/ip6tables.up.rules
echo -e '#!/bin/sh\n/sbin/ip6tables-restore < /etc/ip6tables.up.rules' | sudo tee /etc/network/if-pre-up.d/ip6tables
sudo chmod +x /etc/network/if-pre-up.d/ip6tables

nftables

/etc/nftables.conf

#!/usr/sbin/nft -f

flush ruleset

table inet filter {
	chain input {
		type filter hook input priority filter;
	}
	chain forward {
		type filter hook forward priority filter;
		policy drop;

		# ipv4 (ipv6 may be asymmetric): allow related, drop invalid (conntrack)
                #meta nfproto ipv4 oifname "eth0" ct state related,established accept;
                #meta nfproto ipv4 oifname "eth0" ct state invalid drop;

		# mss clamping (path mtu discovery is hard)
                iifname "eth0" oifname "ffmr-br" meta l4proto tcp tcp flags & (syn|rst) == syn tcp option maxseg size set rt mtu;
                iifname "ffmr-br" oifname "eth0" meta l4proto tcp tcp flags & (syn|rst) == syn tcp option maxseg size set rt mtu;

		# do not forward to martians
                oifname "eth0" ip daddr { 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16, 100.64.0.0/10 } reject with icmpx type no-route;
                oifname "eth0" ip6 daddr { fc00::/7 } reject with icmpx type no-route;

		# reject port 25
		oifname "eth0" tcp dport 25 reject with icmpx type admin-prohibited;

		# allow freifunk to internet
		iifname "ffmr-br" oifname "eth0" ip saddr { 10.128.0.0/18 } accept;
		iifname "ffmr-br" oifname "eth0" ip6 saddr { 2a06:4b00:1000::/56 } accept;
		iifname "eth0" oifname "ffmr-br" ip daddr { 10.128.0.0/18 } accept;
		iifname "eth0" oifname "ffmr-br" ip6 daddr { 2a06:4b00:1000::/56 } accept;

	}
	chain output {
		type filter hook output priority filter;
	}
}

table inet raw {
        chain PREROUTING {
                type filter hook prerouting priority raw;
		policy drop;
		# rpfilter (BCP 38)
                fib saddr . iif oif != 0 accept
        }
}

table inet nat {
	chain PREROUTING {
		type nat hook prerouting priority dstnat;
		# load balance fastd
		udp dport 10000 redirect to numgen inc mod 6 map { 0 : 10001, 1 : 10002, 2 : 10003, 3 : 10004, 4 : 10005, 5 : 10006 };
	}
}

table ip nat {
        chain POSTROUTING {
                type nat hook postrouting priority srcnat;
		policy accept;
                oifname "eth0" ip saddr 10.128.0.0/18 snat to 195.138.247.20;
        }
}

sudo /etc/nftables.conf

/etc/unbound/unbound.conf

server:
    # The following line will configure unbound to perform cryptographic
    # DNSSEC validation using the root trust anchor.
    auto-trust-anchor-file: "/var/lib/unbound/root.key"
    interface: 127.0.0.1
    interface: ::1
    interface: 10.128.0.XXX
    interface: 2a06:4b00:1000::a80:2XX
    access-control: 10.128.0.0/18 allow
    access-control: 2a06:4b00:1000::/56 allow

    private-domain: "dn42"
    domain-insecure: "dn42"
    local-zone: "20.172.in-addr.arpa." nodefault
    local-zone: "22.172.in-addr.arpa." nodefault
    local-zone: "23.172.in-addr.arpa." nodefault
    local-zone: "d.f.ip6.arpa." nodefault

forward-zone:
    name: "dn42"
    forward-addr: fd42:d42:d42:53::1
    forward-addr: 172.22.0.53

forward-zone:
    name: "20.172.in-addr.arpa"
    forward-addr: fd42:d42:d42:53::1
    forward-addr: 172.22.0.53

forward-zone:
    name: "22.172.in-addr.arpa"
    forward-addr: fd42:d42:d42:53::1
    forward-addr: 172.22.0.53

forward-zone:
    name: "23.172.in-addr.arpa"
    forward-addr: fd42:d42:d42:53::1
    forward-addr: 172.22.0.53

forward-zone:
    name: "d.f.ip6.arpa"
    forward-addr: fd42:d42:d42:53::1
    forward-addr: 172.22.0.53
sudo systemctl enable unbound
sudo systemctl start unbound

yolokey-server

sudo apt-get install python3-pip python3-dev
git clone https://github.com/hackspace-marburg/yolokey-server

/etc/sudoers.d/map

map ALL = NOPASSWD: /bin/systemctl reload fastd@ffmr.service

/etc/systemd/system/yolokey-server.service

[Unit]
Description=Automatic deployment of fastd public keys. Accept everyone's keys!
After=network.target

[Service]
Type=simple
User=map
Environment="FASTD_PEERS_DIR=/etc/fastd/ffmr/peers/" "FASTD_SITE=ffmr" "TRAVIS_REPO_SLUG=hackspace-marburg/ffmr-peers" "TRAVIS_TOKEN=XXXX"
WorkingDirectory=/home/map/yolokey-server
ExecStart=/home/map/yolokey-server/bootstrap.sh

[Install]
WantedBy=multi-user.target
sudo systemctl enable yolokey-server.service
sudo systemctl start yolokey-server.service

/etc/nginx/sites-enabled/api_marburg_freifunk_net.conf

server {
	listen 80;
	listen [::]:80;

	server_name api.marburg.freifunk.net;

	location / {
		rewrite ^(.*)$ https://$host$1 permanent;
	}

	location /.well-known/acme-challenge/ {
		alias /var/lib/acme/challenges;
		try_files $uri =404;
	}

	location /yolokey/ {
		proxy_pass http://[::1]:8081/;
	}
}

/etc/nginx/conf.d/default.conf

server {
	listen 80 default_server;
	listen [::]:80 default_server ipv6only=on;

	server_name gw01.marburg.freifunk.net;
	server_name _;

	location / {
		rewrite ^(.*)$ https://gw01.marburg.freifunk.net$1 permanent;
	}

	location /.well-known/acme-challenge/ {
		alias /var/lib/acme/challenges;
		try_files $uri =404;
	}

	location /yolokey/ {
		proxy_pass http://[::1]:8081/;
	}
}

server {
	listen 443 default_server ssl;
	listen [::]:443 default_server ssl ipv6only=on;

        ssl_certificate_key /var/lib/acme/live/gw01.marburg.freifunk.net/privkey;
        ssl_certificate /var/lib/acme/live/gw01.marburg.freifunk.net/fullchain;
        ssl_trusted_certificate /var/lib/acme/live/gw01.marburg.freifunk.net/chain;

	server_name gw01.marburg.freifunk.net;
	server_name _;

	location / {
		return 403;
	}

	location /yolokey/ {
		proxy_pass http://[::1]:8081/;
	}
}
sudo acmetool want gw01.marburg.freifunk.net out.gw01.marburg.freifunk.net

ACME V2-Update

Hackisches Update um auf einem gut abgehangenen Debian via ACME v2 noch Zertifikate zu bekommen. Dieser Abschnitt ist als Update zu dem obigen gedacht.

Als Software wird dehydrated eingesetzt, da es minimale Abhängigkeiten hat - lediglich coreutils, curl und openssl.

Installation

  1. dehydrated-Repository nach /opt/dehydrated geklont und letzten Tag - v0.7.0 - auschecken.
  2. ln -s /opt/dehydrated/dehydrated /usr/bin/dehydrated

Konfiguration, dehydrated

root@gw01 /etc/dehydrated # ls -la
total 32
drwxr-xr-x  6 www-data www-data 4096 Jul 22 20:56 .
drwxr-xr-x 92 root     root     4096 Jul 22 20:05 ..
drwxr-xr-x  3 www-data www-data 4096 Jul 22 20:29 accounts
drwxr-xr-x  3 www-data www-data 4096 Jul 22 20:30 certs
drwx------  2 www-data www-data 4096 Jul 22 20:30 chains
-rw-r--r--  1 root     root      285 Jul 22 20:55 config
-rw-r--r--  1 root     root      144 Jul 22 20:21 domains.txt
drwxr-xr-x  2 www-data www-data 4096 Jul 22 20:31 well-known

root@gw01 /etc/dehydrated # cat config
DEHYDRATED_USER=www-data
DEHYDRATED_GROUP=www-data

CHALLENGETYPE="http-01"

BASEDIR="/etc/dehydrated"
DOMAINS_TXT="${BASEDIR}/domains.txt"
CERTDIR="${BASEDIR}/certs"
ACCOUNTDIR="${BASEDIR}/accounts"
WELLKNOWN="${BASEDIR}/well-known"

KEY_ALGO=secp384r1
CONTACT_EMAIL=freifunk@hsmr.cc

root@gw01 /etc/dehydrated # cat domains.txt
gw01.marburg.freifunk.net api.marburg.freifunk.net firmware.marburg.freifunk.net map.marburg.freifunk.net opkg.marburg.link update.marburg.link

Konfiguration, nginx

server {
        ssl_certificate_key /etc/dehydrated/certs/gw01.marburg.freifunk.net/privkey.pem;
        ssl_certificate /etc/dehydrated/certs/gw01.marburg.freifunk.net/cert.pem;
        ssl_trusted_certificate /etc/dehydrated/certs/gw01.marburg.freifunk.net/fullchain.pem;

        location ^~ /.well-known/acme-challenge {
                alias /etc/dehydrated/well-known;
        }
}

Konfiguration, systemd

root@gw01 ~ # systemctl cat letsencrypt-renew.service
# /etc/systemd/system/letsencrypt-renew.service
[Unit]
Description=Renew Let's Encrypt certificates

[Service]
Type=oneshot
RequiredBy=nginx
ExecStart=/usr/bin/dehydrated -c
ExecStartPost=/bin/systemctl reload nginx

root@gw01 ~ # systemctl cat letsencrypt-renew.timer
# /etc/systemd/system/letsencrypt-renew.timer
[Unit]
Description=Daily renewal of Let's Encrypt's certificates

[Timer]
OnCalendar=04:15
Persistent=true

[Install]
WantedBy=timers.target