#!/usr/bin/env bash # Demo session for PF-on-Linux: a scripted terminal tour of the lab. # Recorded with: asciinema rec out/demo-pf-linux.cast -c "bash scripts/demo-session.sh" # # Everything printed is the REAL output of the lab (Alpine VMs under libvirt # running the kernel built from source with our pf.ko). The VM management # addresses are resolved from the hypervisor (they are DHCP), the lab # topology (10.0.1.0/24, 10.0.2.0/24, ...) is fixed. set -u DEB=${DEB:-root@192.168.1.242} CLI_IP=${CLI_IP:-10.0.1.10} SRV_IP=${SRV_IP:-10.0.2.20} KEY=${KEY:-$HOME/.ssh/pflab_ed} bold() { printf '\033[1m%s\033[0m\n' "$*"; } cyan() { printf '\033[1;36m%s\033[0m\n' "$*"; } # management address of a lab VM (DHCP, so ask the hypervisor) mgmt() { ssh -o ConnectTimeout=8 "$DEB" "virsh domifaddr $1 | awk '/vnet/{print \$4}' | cut -d/ -f1" \ 2>/dev/null | tr -d '\r' | head -1 } PF0_MGMT=$(mgmt pftest-pf0) CLI_MGMT=$(mgmt pftest-cli) [ -n "$PF0_MGMT" ] || PF0_MGMT=192.168.122.8 [ -n "$CLI_MGMT" ] || CLI_MGMT=192.168.122.80 # run a command on a lab VM (through the hypervisor) onvm() { local ip=$1; shift ssh -o ConnectTimeout=10 -o StrictHostKeyChecking=accept-new \ -o ProxyJump=root@192.168.1.242 -i "$KEY" root@"$ip" "$@" 2>&1 } p0() { cyan "pftest-pf0# $*" onvm "$PF0_MGMT" "$@" sleep 0.4 } vmc() { cyan "pftest-cli# $*" onvm "$CLI_MGMT" "$@" sleep 0.4 } CONF=/tmp/demo-pf.conf DIV=/tmp/demo-divert.conf cat > "$CONF" <<'CONF' # PF-on-Linux demo ruleset (pf.conf syntax, real OpenBSD pfctl) set block-policy return table { 10.0.1.0/24 } # transit traffic: stateful, table-driven pass in quick on ingress proto icmp from keep state pass out quick on egress proto icmp keep state # deny a specific destination block drop in quick proto icmp to 10.0.9.99 # everything else pass in quick pass out quick CONF cat > "$DIV" </dev/null bold "===============================================================" bold " PF on Linux - the OpenBSD PF 8.0 engine inside the Linux kernel" bold "===============================================================" echo echo "Lab: 3 Alpine VMs under libvirt." echo " pftest-pf0 = firewall (kernel 7.3.4 built from source + pf.ko)" echo " pftest-cli = client 10.0.1.10 pftest-srv = server 10.0.2.20" echo bold "--- 1. our kernel, the OpenBSD engine as a module ---" p0 "uname -r" p0 "lsmod | grep '^pf ' || echo 'pf: not loaded yet'" p0 "insmod /opt/pf/pf.ko && dmesg | tail -3" echo bold "--- 2. pfctl talks to the module over the OpenBSD ioctl ABI ---" p0 "/opt/pf/pfctl -s info | head -4" p0 "/opt/pf/pfctl -e && /opt/pf/pfctl -s info | head -1" echo bold "--- 3. a real pf.conf: tables, stateful rules, an explicit drop ---" p0 "cat /tmp/demo-pf.conf" p0 "/opt/pf/pfctl -f /tmp/demo-pf.conf && echo 'rules loaded'" p0 "/opt/pf/pfctl -sr" echo bold "--- 4. transit traffic is allowed and stateful ---" vmc "ping -c2 -W2 $SRV_IP | tail -3" p0 "/opt/pf/pfctl -ss | grep -E 'icmp' | head -4" echo bold "--- 5. tables are live objects ---" p0 "/opt/pf/pfctl -t trusted -T show" p0 "/opt/pf/pfctl -s Tables" echo bold "--- 6. the drop rule actually drops ---" p0 "cat > /tmp/demo-block.conf < /tmp/demo-nat.conf </tmp/pdivert.log 2>&1