FreeBSD as Router/Firewall

Posted on Jun 20, 2024
Using FreeBSD as a high-performance Router and Firewall with full IPv6 suppor

BASIC FREEBSD CONFIGURATION

# install same  commonly used
pkg install vim bash sudo htop tree xauth wget curl nmap git cpuid pftop bind-tools sysutils/bsdinfo lsof freecolor lscpu bwm-ng dmidecode

pkg install dnsmasq tailscale vnstat radvd

# OpenBSD Packet Filter (PF) & ALTQ
kldload pf.ko
service pf enable
service pflog enable
service pfsync enable

/boot/loader.conf bridge_load="YES"

NETWORK INTERFACES & SERVICE

/etc/rc.conf

hostname="gw.example.net"

defaultrouter="10.11.1.1"

ifconfig_igc0="inet 10.11.1.254/24"
ifconfig_igc1="inet 10.20.0.1/16"

gateway_enable="YES"
ipv6_privacy="YES"
ifconfig_igc0_ipv6="inet6 accept_rtadv"
ifconfig_igc1_ipv6="inet6 xxxx:xxxx::1/64"

ipv6_gateway_enable="YES"
ipv6_defaultrouter="fe80::1%igc0"
netwait_enable="YES"
netwait_if="igc0"

sshd_enable="YES"
ntpd_enable="YES"
ntpd_sync_on_start="YES"
pf_enable="YES"
pflog_enable="YES"
pfsync_enable="YES"
dnsmasq_enable="YES"
vnstat_enable="YES"
tailscaled_enable="YES"
rtsold_enable="YES"
radvd_enable="YES"
kld_list="tcp_rack tcp_bbr"

/usr/local/etc/radvd.conf

interface igc1 {
    AdvSendAdvert      on;
    MinRtrAdvInterval  3;
    MaxRtrAdvInterval  10;
    AdvDefaultPreference medium;
    AdvManagedFlag     off;
    AdvOtherConfigFlag off;

        prefix ::/64 {
        AdvOnLink           on;
        AdvAutonomous       on;
        AdvRouterAddr       off;
        AdvPreferredLifetime 3600;
        AdvValidLifetime     7200;
    };

        route ::/0 {
        AdvRoutePreference  medium;
        AdvRouteLifetime    1800;
    };

    RDNSS xxxx:xxxx::1 {
        AdvRDNSSLifetime 3600;
    };

    DNSSL example.net {
        AdvDNSSLLifetime 3600;
    };
};

ROUTING

/etc/rc.conf

# routing
static_routes="asus"
route_asus="192.168.33.0/24 192.168.33.1"

service routing restart

PF SETUP

/etc/pf.conf

#################################
#### Packet Firewall Ruleset ####
#################################

#### Variables ####
ext_if="igc0"
int_if="igc1"

# Set allowed ICMP types
icmp_types = "{ 0, 3, 4, 8, 11, 12, 134 }"
icmp6_types = "{1,2,3,4,128,129,133,134,135,136,137}"

# Allow inbound traffic ports
in_ports = "{ 22, 80, 443, 8118, 8388, 8389 }"

# Internal networks
internal_nets = "{ 10.20.0.0/16, 10.21.0.0/16 10.22.1.0/16, 192.168.3.0/24 }"

#### Options and Optimizations #######
set loginterface $ext_if
set skip on lo0
set optimization aggressive
set block-policy return
scrub on $ext_if all no-df fragment reassemble

#### NAT Settings ####
nat on $ext_if from $int_if:network to any -> ($ext_if)
nat on $ext_if inet6 from $int_if:network to any -> ($ext_if)

#### Firewall Configuration #######

# ipv6
pass in on $int_if inet6 all
pass out on $int_if inet6 all

# pass in on $ext_if inet6 proto icmp6 all
pass in on $ext_if inet6 all
pass out on $ext_if inet6 all

#### Rules inbound (int_if) ####

# Allow all traffic on internal interfaces
pass in log on $int_if from $int_if:network to any

#### Rules outbound (int_if) ####

# Allow all traffic outbound from internal interfaces
pass out log on $int_if from any to any

#### Rules inbound (ext_if) ####

# Allow inbound traffic on external interface for specific services
pass in log on $ext_if inet proto {tcp, udp} to ($ext_if) port $in_ports
pass in log on $ext_if inet6 proto {tcp, udp} to ($ext_if) port $in_ports

# Allow ICMP
pass in quick on $ext_if inet proto icmp all icmp-type $icmp_types
pass in quick proto icmp6 all keep state
pass out quick proto icmp6 all keep state

#### Rules outbound (ext_if) ####

# Allow outbound traffic from the external interface
pass out log on $ext_if from any to any

# tailscale
pass in quick on tailscale0 proto udp from any to any port 41641
pass out quick on tailscale0 proto { udp, tcp } from any to { 100.100.100.100, 100.73.97.124 } port 53

pfctl -f /etc/pf.conf

check pflog tcpdump -n -e -ttt -i pflog0
tshark -i pflog0 -f "src net (10.20.0.0/16 or 192.168.31.0/24) and (tcp port 80 or tcp port 443)"
tshark -i pflog0 -f "src net (10.20.0.0/16 or 192.168.31.0/24) and (tcp port 80 or tcp port 443)" -T fields -e frame.time -e ip.src -e ip.dst -e tcp.dstport -e http.host -e tls.handshake.extensions_server_name

KERNEL && FORWARDING

/etc/sysctl.conf

# forwarding
net.inet.ip.forwarding=1
net.inet6.ip6.forwarding=1

# set autotuning maximum to at least 16MB too
net.inet.tcp.sendbuf_max=16777216
net.inet.tcp.recvbuf_max=16777216
net.inet.tcp.sendbuf_auto=1
net.inet.tcp.recvbuf_auto=1

kern.ipc.maxsockbuf=16777216
kern.ipc.nmbclusters=988914
net.inet.tcp.sendbuf_inc=16384

# socket max connect
kern.ipc.somaxconn=32768

# ack delayed
net.inet.tcp.delayed_ack=1

kern.ipc.nmbjumbop=247228
net.inet.tcp.cc.algorithm=cubic

net.inet.udp.recvspace=2097152

net.inet.icmp.icmplim=300
net.inet.tcp.msl=2000
net.inet.tcp.fast_finwait2_recycle=1

net.inet.ip.intr_queue_maxlen=4096

net.inet.tcp.functions_default=bbr

net.inet6.icmp6.errppslimit=1000

/boot/loader.conf

tcp_bbr_load="YES"
autoboot_delay="3"
coretemp_load="YES"

net.isr.maxthreads=4
net.isr.bindthreads=0
net.inet.tcp.syncache.hashsize="2048"
net.inet.tcp.syncache.bucketlimit="128"

DNSMASQ

/usr/local/etc/dnsmasq.conf

bind-interfaces
port=53
except-interface=igc0

no-dhcp-interface=lo

conf-dir=/usr/local/etc/dnsmasq.d

# Log
log-queries
log-dhcp
log-facility=/var/log/dnsmasq/dnsmasq.log
log-async=120

# Domain & Hosts
local=/example.net/
domain=example.net
address=/.example.net/10.20.0.1

# expand-hosts
no-hosts
expand-hosts
addn-hosts=/usr/local/etc/dnsmasq.hosts

# option:ntp-server
dhcp-option=42,10.20.0.1

# DNS
all-servers
# dnssec
# strict-order
# cache-size=1048576
cache-size=4096
max-cache-ttl=86400
min-cache-ttl=60
resolv-file=/usr/local/etc/dnsmasq.resolv.conf
# strict-order
dns-forward-max=512
localise-queries
# bogus-priv
# bogus-nxdomain=39.156.66.10
dns-loop-detect
server=223.5.5.5

/usr/local/etc/dnsmasq.d/dhcp.conf

# optimization sort
dhcp-authoritative
dhcp-rapid-commit
dhcp-ignore-clid
# dhcp-leasefile=/var/db/dnsmasq.leases

# dhcpd
interface=igc1
dhcp-option=option:domain-search,example.net
dhcp-range=10.20.2.1,10.20.5.254,48h

# gateway
dhcp-option=1,255.255.0.0 # option:subnet-mask
dhcp-option=3,10.20.0.1 # option:router
dhcp-option=6,10.20.0.1 # option:dns-server
dhcp-option=15,example.net # option:domain-name
dhcp-option=28,10.20.255.255 # option:broadcast

# bypass
dhcp-option=tag:bypass,3,10.20.0.3

## bypass host
dhcp-host=xx:xx:xx:xx:xx:xx,set:bypass

# static hosts
dhcp-host=wifi-master,xx:xx:xx:xx:xx:xx,10.20.0.166,infinite

/usr/local/etc/dnsmasq.resolv.conf

server=10.20.0.1
server=114.114.114.114
server=/cn/114.114.114.114

/usr/local/etc/dnsmasq.hosts

10.20.0.1       gw.example.net example.net wpad.example.net

/etc/dnsmasq.d/adblock-for-dnsmasq.conf github.com/privacy-protection-tools/anti-AD.git

wget https://raw.githubusercontent.com/privacy-protection-tools/anti-AD/master/adblock-for-dnsmasq.conf -O /etc/dnsmasq.d/adblock-for-dnsmasq.conf

VNSTAT

mkdir -p /var/db/vnstat/
chown -R vnstat:vnstat /var/db/vnstat
service vnstat enable
service vnstat start

TEMPERATURE

kldload coretemp
echo 'coretemp_load="YES"' >> /boot/loader.conf
echo 'sysctl -a | grep temperature' > /usr/local/bin/sensors
chmod +x /usr/local/bin/sensors

REFERENCE

https://github.com/shaojing1779/ufrt/blob/main/docs/freebsd-rt.md