Sysadmin, OpenBSD user since before the millenium
Wrote The Book of PF, now in its third edition
Blog at bsdly.blogspot.com about (lack of) sanity in IT
Works at Tietoevry Create
Yes, I'll do another book any decade now
Technical Advisor at The Internet Society
IPv6 "Enthusiast"
A quick introduction about yourself:
Your name
Your favourite BSD
Your experience with networking
Your experience with PF
Your goal(s)
This introduction serves for us to understand the level of the room, and decide on how to better suit the tutorial.
Getting an idea of the level makes it so that we might quickly go through the basics, and focus on something more advanced, rather than spend time on something that everybody knows already.
OpenBSD up to 2.9 (2001) used Darren Reed's IPFilter
Almost, but not quite BSD licensed
No right to distribute changed versions
IPFilter removed on May 29th, 2001
First commit of the new PF code June 24 2001 at 19:48:58 UTC
OpenBSD 3.0 release pushed to Dec 1 by the extra effort
But let's jump to the present -
How to build a maintainable network:
Start with something simple
Think dual stack (IPv6 and IPv4)
Users
The services you offer and the ones you consume
Keep it simple, not stupid
A firewall can:
There are limits though
A firewall cannot
Any software firewall can be overwhelmed with high packet rate Denial of Service attacks. If the scale of the attack exceeds the capability of the firewall
The basic rule format is,
verb criteria actions ... options
pass in on egress proto tcp to egress port ssh
match out on egress nat-to egress
block all
You can do complete rulesets with only these, but you may also want to declare things ->
( egress ? -> the interface group consisting of interfaces that have a default route )
Declaring objects your rules will use:
Macros:
macro-name = "definition"
Tables:
table <tablename>
Queues:
queue queuename on interface bandwidth number ...
You can also set options.
We'll be returning to all of those, in the meantime man pf.conf is always your friend.
A firewall is supposed to block by default, right?
Let's start with the ruleset for a client
Useful baseline:
block pass from (self)
$ doas pfctl -vnf /etc/pf.conf block drop all pass inet6 from ::1 to any flags S/SA pass on lo0 inet6 from fe80::1 to any flags S/SA pass on iwm0 inet6 from fe80::a2a8:cdff:fe63:abb9 to any flags S/SA pass inet6 from 2001:470:28:658:a2a8:cdff:fe63:abb9 to any flags S/SA pass inet6 from 2001:470:28:658:8c43:4c81:e110:9d83 to any flags S/SA pass inet from 127.0.0.1 to any flags S/SA pass inet from 192.168.103.126 to any flags S/SA
It would be nice in this slide to ask questions, to see the general knowledge about protocols:
This can give us an idea of how to proceed with IPv6 depending on the level of the participants.
Ruleset evaluated top to bottom
Last match wins (unless quick is used)
Stateful by default
For more details, see man pf.conf
Options
Macros
Tables
Redirections
Rules
The order of the items in the ruleset used to be important, but not anymore
General configuration for pf
Useful for debugging, applying default timeout values, etc.
set limit states 100000
set debug debug set loginterface dc0 set timeout tcp.first 120 set timeout tcp.established 86400 set timeout { adaptive.start 6000, adaptive.end 12000 }
Content that will later be expanded in context
Name must start with a letter, digit, or underscore
Useful to keep rulesets simple and clean
ext_if = "kue0" all_ifs = "{" $ext_if lo0 "}" pass out on $ext_if from any to any pass in on $ext_if proto tcp from any to any port 25
bacula_ports = "9101:9103" dmz_hosts = "192.0.2.1 - 192.0.2.254"
If we have more slide space, then separate the second example in its own slide
When IP address list macros grow too long, use tables instead
Hold only addresses and networks
Provide fast lookups
table <bruteforce> persist counters
block from <bruteforce>
table <popflooders> persist counters file "/home/peter/popflooders"
useful if you save contents to preserve across reboots, such as
$ doas pfctl -t popflooders -T show >/home/peter/popflooders
possibly in cron jobs
$ doas pfctl -t bruteforce -T add 192.0.2.11 2001:db8::dead:beef:baad:f00d
We ask the participants how they think they would implement this in their infrastructure.
This is to create discussion, and hopefully make it so that we all learn a bit by trial-and-error.
Let's put out the question, wait for some time to see if anyone comes up with a nice description, and then walk them through what is a sensible setup.
This is in preparation for the first exercise which comes next.
Get to the console for fwlabX
Check that pf is indeed loaded and running (hint: pfctl)
Wait until everyone has connected in order to proceed
Configure the external interface on gateway
vi /etc/hostname.vio0
inet 10.255.255.XX/24 !route add 0/0 10.255.255.254 inet6 fd18:b5d:cafe::XX/64 !route add -inet6 2000::/3 fd18:b5d:cafe::a !route add -inet6 fd00::/8 fd18:b5d:cafe::a
nameserver 10.255.255.254 nameserver fd18:b5d:cafe::a
followed by
sh /etc/netstart
block pass quick inet6 proto tcp from fd18::/16 to port ssh pass quick inet6 proto icmp6 from fd18::/16
pass from self
and then, reload pf.conf
pfctl -vnf /etc/pf.conf pfctl -f /etc/pf.conf
From your gateway ping a host
First IPv6
# ping6 fd18:b5d:cafe::a PING fd18:b5d:cafe::a (fd18:b5d:cafe::a): 56 data bytes 64 bytes from fd18:b5d:cafe::a: icmp_seq=0 hlim=64 time=0.548 ms 64 bytes from fd18:b5d:cafe::a: icmp_seq=1 hlim=64 time=0.492 ms 64 bytes from fd18:b5d:cafe::a: icmp_seq=2 hlim=64 time=0.494 ms
# ping stucchi.ch PING stucchi.ch (45.129.224.40): 56 data bytes 64 bytes from 45.129.224.40: icmp_seq=0 ttl=56 time=6.264 ms 64 bytes from 45.129.224.40: icmp_seq=1 ttl=56 time=6.273 ms 64 bytes from 45.129.224.40: icmp_seq=2 ttl=56 time=6.117 ms
Does ping work?
Do other commands work?
Access public web sites, other Internet resources.
What would it take to access the other lab hosts?
Let's ask if there are any questions before continuing. Make sure we have everyone onboard.
Make a 'firewall':
a point of policy enforcement
a gateway
filter for other hosts
redirection tricks
Network Address Translation (RFC1631 onwards)
-> 'Hide' several hosts behind 1 or more public addresses, using RFC1918 addresses
-> can be used by ISPs for conserving scarce IP addresses in large networks (CG-NAT) 100.64.0.0/10
Modern PF has nat-to on 'pass' and 'match' rules:
match out on $extif inet nat-to ($extif)
match out on egress inet nat-to (egress)
NAT, the stopgap measure that's old enough to drink, more than 22 years old. NAT was created as a temporary measure that hasn't been replaced by now. Not even IPv6.
We haven't discusses "egress" yet, so this is the right time to introduce it.
Spend some time discussing it, along with the rest of the NAT specifications.
Unfortunately, there's also NAT for IPv6, called NAT66
"I decide which packets pass"
# sysctl net.inet.ip.forwarding=1 # sysctl net.inet6.ip6.forwarding=1
net.inet.ip.forwarding=1 net.inet6.ip6.forwarding=1
Do you NAT for IPv4? Of course you do.
Do you run IPv6? Of course you do.
ext_if=bge0 int_if=bge1 match out on egress inet nat-to ($ext_if) block all pass proto tcp from { self, $int_if:network }
Keep in mind: This is a point of policy enforcement
ext_if=bge0 int_if=bge1 client_out = "{ ftp-data, ftp, ssh, domain, pop3, auth, nntp, http, \ https, 2628, 5999, 8000, 8080 } udp_services = "{ domain, ntp }" match out on egress inet nat-to ($ext_if) block pass quick proto { tcp, udp } to port $udp_services keep state pass proto tcp from $int_if:network to port $client_out pass proto tcp to self port ssh
Log to on the system we have for showing out, and then show the rules there and how they expand to different parts.
OpenBSD dhcpd(8) can interact with your ruleset via tables:
/etc/rc.conf.local
dhcpd_flags="-L leased_ip_table -A abandoned_ip_table -C changed_ip_table bge1"
ext_if=bge0 int_if=bge1 table <abandoned_ip_table> persist counters table <changed_ip_table> persist counters table <leased_ip_table> persist counters client_out = "{ ftp-data, ftp, ssh, domain, pop3, auth, nntp, http, \ https, 2628, 5999, 8000, 8080 }" udp_services = "{ domain, ntp }" match out on egress inet nat-to ($ext_if) block pass quick proto { tcp, udp } to port $udp_services keep state pass proto tcp from <leased_ip_table> to port $client_out pass proto tcp to self port ssh
=> only pass traffic from hosts with active leases from me
Maybe move this slide to a later section.
Modern PF has two classes of redirect
pass in on egress to port www rdr-to $webserver
pass in on egress to port smtp divert-to 127.0.0.1 port spamd
If your users need to access FTP services, ftp-proxy is what you need
FTP does not easily pass through a block firewall, some help is needed
$ doas rcctl enable ftpproxy6
$ doas rcctl enable ftpproxy
anchor "ftp-proxy/*" ... pass in quick inet proto tcp to port ftp divert-to 127.0.0.1 port 8021pass in quick inet6 proto tcp to port ftp divert-to ::1 port 8021pass out proto tcp from $proxy to port ftp
There is even a reverse mode (-R) for when you host FTP servers, see man ftp-proxy
Your network grows, you become a gateway
Extend the configuration to enable the network to access the internet
# sysctl net.inet.ip.forwarding=1 # sysctl net.inet6.ip6.forwarding=1
match out on egress inet nat-to (egress)
Also, pass traffic from that local net
Configure the hosts with the following IPv6 addresses
On Host1 and Host2, set fd18:b5d:XX::a as the default IPv6 gateway
and also the following IPv4 addresses
On Host1 and Host2 set 192.168.XX.1 as the default IPv4 gateway
From client 1, ping a host on the internet
First IPv6
# ping6 stucchi.ch PING stucchi.ch (2001:41d0:8:6ed8::80): 56 data bytes 64 bytes from 2001:41d0:8:6ed8::80: icmp_seq=1 hlim=56 time=7.414 ms 64 bytes from 2001:41d0:8:6ed8::80: icmp_seq=2 hlim=56 time=6.333 ms 64 bytes from 2001:41d0:8:6ed8::80: icmp_seq=3 hlim=56 time=6.441 ms
# ping stucchi.ch PING stucchi.ch (37.59.51.141): 56 data bytes 64 bytes from 37.59.51.141: icmp_seq=0 ttl=56 time=6.264 ms 64 bytes from 37.59.51.141: icmp_seq=1 ttl=56 time=6.273 ms 64 bytes from 37.59.51.141: icmp_seq=2 ttl=56 time=6.117 ms
Try fetching ftp://ftp.ripe.net/pub/stats/ripencc/delegated-ripencc-extended-latest
# wget ftp://ftp.ripe.net/pub/stats/ripencc/delegated-ripencc-extended-latest
Check your result
If it didn't work, configure FTP-proxy and try again.
Now you actually want some 'pass in on egress' rules :)
Get your specifications clear, put in writing:
the names could be in /etc/services already
the hosts (IP addresses) that run the services
decide who/what/where to be reachable for/from
Check services requiring extra help (i.e. proxying).
OpenBSD comes with several proxies and other service helper programs in the base system:
ext_if=bge0 # adjust to what your system has int_if=bge1 # adjust to what your system has client_out = "{ ftp-data, ftp, ssh, domain, pop3, auth, nntp, http, \ https, 2628, 5999, 8000, 8080 }" udp_services = "{ domain, ntp }" webserver = "192.0.2.227" webports = "{ http, https }" emailserver = "192.0.2.225" email = "{ smtp, pop3, imap, imap3, imaps, pop3s }" nameservers = "{ 192.0.2.221, 192.0.2.223 }" match out on egress inet nat-to ($ext_if) block pass quick proto { tcp, udp } to port $udp_services keep state pass proto tcp from $int_if:network to port $client_out pass proto tcp to self port ssh pass proto tcp to $webserver port $webports pass proto tcp to $emailserver port $email pass log proto tcp from $emailserver to port smtp pass inet proto { tcp, udp } to $nameservers port domain
Now try loading this with pfctl -vnf /etc/pf.conf and see what this expands to (you can also fetch pf.services01.conf and try)
In our previous ruleset, add
table <bruteforce> persist countersblock from <bruteforce>
and change the ssh rule to
pass proto tcp to port ssh flags S/SA keep state \ (max-src-conn 15, max-src-conn-rate 2/10, overload <bruteforce> flush global)
max-src-conn - see man page but max number of simultaneous connections from one host max-src-conn-rate - number of new connections per number of seconds
Wordpress is a usual target for many different attacks
-> Make the attackers suffer by forcing them through smaller "windows"
Mix and match settings, consult man pf.conf and remember pfctl expire
Also see Forcing the password gropers through a smaller hole with OpenBSD's PF queues, Badness, Enumerated by Robots (blog posts) + The Book of PF
table <web_brutes> persist counters block from <web_brutes>
pass proto tcp to port $webports flags S/SA keep state \ (max-src-conn 15, max-src-conn-rate 5/10, overload <web_brutes> flush global)
grep wp-login /var/www/logs/access.log | awk '{print $1}' | \ sort -u | xargs doas pfctl -t web_brutes -T add
max-src-conn - see man page but max number of simultaneous connections from one host max-src-conn-rate - number of new connections per number of seconds You could also look into max-src-nodes, max-src-states
table <spamd-white> persisttable <nospamd> persist file "/etc/mail/nospamd"pass in on egress proto tcp to any port smtp divert-to 127.0.0.1 port spamdpass in on egress proto tcp from <nospamd> to any port smtppass in log on egress proto tcp from <spamd-white> to any port smtppass out log on egress proto tcp to any port smtp
$ doas rcctl enable spamd$ doas rcctl start spamd
TIP: check out smtpctl spf walk <nospamd_domains.txt to feed your nospamd table from live SPF data (in OpenBSD 6.3 onwards), see the blog post Goodness, Enumerated by Robots. Or, Handling Those Who Do Not Play Well With Greylisting
A common Denial-of-Service (DOS) technique is to send large numbers of SYNs from spoofed addresses, filling up the state table.
In OpenBSD 6.3 and newer we have the pf.conf option
set syncookies
The default is off, if you enable with
set syncookies on
all SYNs get SYNACK answer, but no resources allocated until ACK received (pending match to pass rule)
The other option is adaptive with syncookies used only when half open TCP connections reach the start percentage, until the end level is reached
set syncookies adaptive (start 29%, end 15%)
Let's ask if there are any questions before continuing. Make sure we have everyone onboard.
This is not exactly the network we have, but could be thought as such.
Host1 and Host2 are in what could be considered our DMZ.
# cp /etc/examples/httpd.conf /etc/httpd.conf < comment out the HTTPS part > # rcctl enable httpd # rcctl start httpd httpd(ok)
listen on all
# rcctl enable smtpd # rcctl start smtpd smtpd(ok)
webserver_v4 = "$IP_addr_of_host1"webserver_v6 = "fd18:b5d:XX::80"webports = "{ http, https }"emailserver_v4 = "$IP_addr_of_host2"emailserver_v6 = "fd18:b5d:XX::25"email = "{ smtp, pop3, imap, imap3, imaps, pop3s }"match in on egress inet proto tcp to egress port $webports rdr-to $webserver_v4match in on egress inet proto tcp to egress port $email rdr-to $emailserver_v4pass inet proto tcp to $webserver_v4 port $webportspass inet proto tcp to $emailserver_v4 port $emailpass log inet proto tcp from $emailserver_v4 to port smtppass inet6 proto tcp to $webserver_v6 port $webportspass inet6 proto tcp to $emailserver_v6 port $emailpass log inet6 proto tcp from $emailserver_v6 to port smtp
telnet -6 fd18:b5d:XX::80 80 telnet -4 10.255.255.XX 80
telnet -6 fd18:b5d:XX::25 25 telnet -4 10.255.255.XX 25
Decide your network topology
Segment off your subnets
Per subnet (customer)
OpenBSD has three separate shaping techniques:
priorities (set prio), introduced in OpenBSD 5.0
queues, introduced in OpenBSD 5.5, and
flows, introduced in OpenBSD 6.2 (aka FQ-CoDel)
Remember:
Traffic shaping is about dropping packets
But is only relevant when there's a reason to start dropping
Then you get to pick which ones using the traffic shaping tools
pass proto tcp to port ssh set prio 6
ACKs are tiny and have their TOS set to lowdelay, and this trick will cheat the FIFO:
match out on $ext_if proto tcp from $ext_if set prio (3, 7)match in on $ext_if proto tcp to $ext_if set prio (3, 7)
as per the man page,
If two priorities are given, TCP ACKs with no data payload and packets which have a TOS of lowdelay will be assigned to the second one. Packets with a higher priority number are processed first, and packets with the same priority are processed in the order in which they are received.
See Prioritizing empty TCP ACKs with pf and ALTQ by Daniel Hartmeier for the ALTQ way
Introduced in OpenBSD 6.2, the FQ-CoDel algorithm (see RFC8290) defines flows to set up fair sharing for a specified number of simultaneous connections
Enable for your configuration with something like
queue outq on bge0 bandwidth 18M max 18M flows 1024 qlimit 1024 \ default
Estimate your approximate high number of simultaneously actively transmitting sessions, put that number in (up to 32767)
queue main on $ext_if bandwidth 20M queue defq parent main bandwidth 3600K default queue ftp parent main bandwidth 2000K queue udp parent main bandwidth 6000K queue web parent main bandwidth 4000K queue ssh parent main bandwidth 4000K queue ssh_interactive parent ssh bandwidth 800K queue ssh_bulk parent ssh bandwidth 3200K queue icmp parent main bandwidth 400K
match log quick on $ext_if proto tcp to port ssh \ queue (ssh_bulk, ssh_interactive)match in quick on $ext_if proto tcp to port ftp queue ftpmatch in quick on $ext_if proto tcp to port www queue httpmatch out on $ext_if proto udp queue udpmatch out on $ext_if proto icmp queue icmp
treat filtering (block, pass) in separate rules,
or
Any traffic not explicitly assigned goes in the default queue
You can build in flexibility:
queue rootq on $ext_if bandwidth 20M queue main parent rootq bandwidth 20479K min 1M max 20479K qlimit 100 queue qdef parent main bandwidth 9600K min 6000K max 18M default queue qweb parent main bandwidth 9600K min 6000K max 18M queue qpri parent main bandwidth 700K min 100K max 1200K queue qdns parent main bandwidth 200K min 12K burst 600K for 3000ms queue spamd parent rootq bandwidth 1K min 0K max 1K qlimit 300
Note here the added min and max values: combinded queue bandwidth can exceed actual sum; this gets you upper and lower bound within physical limits.
burst N for M - allow bursts of that size and length
qlimit - size of the queue's holding buffer - larger values may delay (packets are kept longer before sending)
Available on all the BSDs
Offers view into your system's traffic
Check how your rules are behaving on your system in realtime
More info in the man page systat(1)
1 users Load 2.56 2.27 2.28 skapet.bsdly.net 20:55:50QUEUE BW SCH PRI PKTS BYTES DROP_P DROP_B QLEN BOR SUS P/S B/Srootq on bge0 20M 0 0 0 0 0 0 0 main 20M 0 0 0 0 0 0 0 qdef 9M 6416363 2338M 136 15371 0 462 30733 qweb 9M 431590 144565K 0 0 0 0.6 480 qpri 2M 2854556 181684K 5 390 0 79 5243 qdns 100K 802874 68379K 0 0 0 0.6 52 spamd 1K 596022 36021K 1177533 72871514 299 2 136
or
$ doas pfctl -vsq [ pkts: 0 bytes: 0 dropped pkts: 0 bytes: 0 ] [ qlength: 0/ 50 ]queue rootq on bge0 bandwidth 20M qlimit 50 [ pkts: 0 bytes: 0 dropped pkts: 0 bytes: 0 ] [ qlength: 0/ 50 ]queue main parent rootq bandwidth 20M, min 1M, max 20M qlimit 100 [ pkts: 0 bytes: 0 dropped pkts: 0 bytes: 0 ] [ qlength: 0/100 ]queue qdef parent main bandwidth 9M, min 8M, max 18M default qlimit 50 [ pkts: 6517150 bytes: 2458545319 dropped pkts: 136 bytes: 15371 ] [ qlength: 0/ 50 ]queue qweb parent main bandwidth 9M, min 8M, max 18M qlimit 50 [ pkts: 431741 bytes: 148072219 dropped pkts: 0 bytes: 0 ] [ qlength: 0/ 50 ]queue qpri parent main bandwidth 2M, min 700K, max 2M burst 4M for 3000ms qlimit 50 [ pkts: 2855418 bytes: 186101241 dropped pkts: 5 bytes: 390 ] [ qlength: 0/ 50 ]queue qdns parent main bandwidth 100K, min 12K burst 600K for 3000ms qlimit 50 [ pkts: 803548 bytes: 70079760 dropped pkts: 0 bytes: 0 ] [ qlength: 0/ 50 ]queue spamd parent rootq bandwidth 1K, max 1K qlimit 300 [ pkts: 596424 bytes: 36910940 dropped pkts: 1178456 bytes: 72928604 ] [ qlength: 300/300 ]
add another v (pfctl -vvsq) for continuously updating display
Let's ask if there are any questions before continuing. Make sure we have everyone onboard.
queue rootq on $ext_if bandwidth 20M queue main parent rootq bandwidth 20479K min 1M max 20479K qlimit 100 queue default parent main bandwidth 9600K min 6000K max 18M default queue http parent main bandwidth 9600K min 6000K max 18M queue smtp parent main bandwidth 9600K min 6000K max 18M queue spamd parent rootq bandwidth 1K min 0K max 1K qlimit 300
match in on egress inet proto tcp to egress port $webports rdr-to $webserver_v4 \ queue httpmatch in on egress inet proto tcp to egress port $email rdr-to $emailserver_v4 \ queue smtppass inet6 proto tcp to $webserver_v6 port $webports set queue httppass inet6 proto tcp to $emailserver_v6 port $email set queue smtppass log inet6 proto tcp from $emailserver_v6 to port smtp set queue smtp
# systat queues
# pfctl -vsq
Border Gateway Protocol
Once you have the ASn registered, do the basic config.
In your pf.conf:
Neat trick: Define tables in your pf.conf
OSPF: Open Shortest Path First
BGP: announces and receives routes
"BCP38" -- Discussed also in another effort
Mutually Agreed Norms for Routing Security (MANRS)
vxlan - the Virtual eXtensible Local Area Network tunnel interface
# ifconfig vxlan0 tunnel 192.168.100.101 192.168.200.201 vnetid 17 # ifconfig vxlan0 10.11.12.100/24
# ifconfig vxlan0 tunnel 192.168.200.201 192.168.100.101 vnetid 17 # ifconfig vxlan0 10.11.12.101/24
table <vxendpoints> { 192.168.200.201 192.168.200.204 }pass from <vxendpoints> to port vxlan
Buy Reyk a beer.
Service names vs port numbers
Comments - yes, you will forget why this was a good idea
OpenBSD base operating system can be supplimented by the following packages and features:
pftop - a curses-based utility for real-time display of active states and rules for pf. It is a cross between top and pfctl -sr and pfctl -ss.
pkg_add pftop
pkg_add nsh
Let's ask if there are any questions before continuing. Make sure we have everyone onboard.
ICMP: Internet Control Message Protocol
The ping of death scare is almost over, let's enable ping:
icmp_types = "{ echoreq, unreach }"pass inet proto icmp all icmp-type $icmp_types keep statepass inet proto icmp from $localnet icmp-type $icmp_typespass inet proto icmp to $ext_if icmp-type $icmp_typespass inet6 proto icmp6 from $localnet icmp6-type $icmp6_typespass inet6 proto icmp6 to $ext_if icmp6-type $icmp6_types
echoreq: lets ping do its thing
unreach: lets you do path MTU discovery (PMTUD)
Basic IP connectivity
ping <host>
traceroute <host>
telnet <host> <tcp port>
tcpdump -i <interfacename>
nc -z <host> <startport>-<endport>
ping packets can be manipulated in many ways
ping [-I <src>address> ] <destaddress>
-set ping packet size (MTU path Discovery Issues) ping [-s <packet-size>] <destaddress>
ping [ -D ] <destaddress>
-set ping packet size (MTU / fragmentation issuesping [-s <packet-size>] <destaddress>
DNS functionality basic commands
ping <hostdnsnae>
telnet <hostdnsnae> <tcp port>
dig <hostdnsname>
nslookup <hostdnsname>
IP connectivity
mtr <host>
tcptraceroute <host>
For statistics (bytes/packets passed per rule) attach labels per rule
pass log proto { tcp, udp } to $emailserver port smtp label "mail-in"pass log proto { tcp, udp } from $emailserver to port smtp label "mail-out"
$ doas pfctl -vs rulespass inet proto tcp from any to 192.0.2.225 port = smtp flags S/SA keep state label "mail-in"[ Evaluations: 1664158 Packets: 1601986 Bytes: 763762591 States: 0 ][ Inserted: uid 0 pid 24490 ]pass inet proto tcp from 192.0.2.225 to any port = smtp flags S/SA keep state label "mail-out"[ Evaluations: 2814933 Packets: 2711211 Bytes: 492510664 States: 0 ][ Inserted: uid 0 pid 24490 ]
$ doas pfctl -zvslmail-in 1664158 1601986 763762591 887895 682427415 714091 81335176mail-out 2814933 2711211 492510664 1407278 239776267 1303933 252734397
Rules with the log keyword log packet data to the pflog device(s)
# log blocked packetsblock log(all)# logs initial packet of matching connections: pass log proto tcp to port ssh# logs all matching packets:pass log(all) proto tcp to port ssh log(all) # logs matches on this and all succeeding rulespass log(matches) proto tcp to port ssh # logs all packets matches on this and all succeeding rulespass log(all, matches) proto tcp to port ssh
match log(all, matches) # log *everything*
tcpdump is your friend
Let it loose on the pflog device:
$ doas tcpdump -n -e -ttt -i pflog0 tcpdump: WARNING: snaplen raised from 116 to 160tcpdump: listening on pflog0, link-type PFLOGMay 29 21:06:27.165561 rule def/(match) pass in on bge1: 192.168.103.126.15526 >213.187.179.198.22: . ack 2951513182 win 16332 (DF) [tos 0x10]May 29 21:06:27.166934 rule 16/(match) pass in on bge0: 158.36.191.135.22 >213.187.179.198.59516: . ack 1734404306 win 64800 [tos 0x8]May 29 21:06:27.166939 rule 2/(match) match in on bge0: 158.36.191.135.22 >213.187.179.198.59516: . ack 1 win 64800 [tos 0x8]May 29 21:06:27.168340 rule def/(match) pass out on bge1: 213.187.179.198.22 >192.168.103.126.15526: P 69:153(84) ack 0 win 17520 [tos 0x10]May 29 21:06:27.169150 rule def/(match) pass out on bge1: 213.187.179.198.22 >192.168.103.126.15526: P 153:333(180) ack 0 win 17520 [tos 0x10]May 29 21:06:27.169265 rule def/(match) pass out on bge1: 213.187.179.198.22 >
On busy systems, you may need to raise limits from default values
Check with:
$ doas pfctl -s info
versus the output of pfctl -s memory and pfctl -s timeouts
You may need to bump up from defaults:
# increase state limit from 10'000 states on busy systemsset limit states 100000# increase no of source nodes set limit src-nodes 100000
Sometimes we need to check what traffic is flowing on a given interface
iftop ncurses package can be installed and allows visibility on top traffic flows on an interface
iftop allows that quick diagnostics check without needing a full flow anlaysis infrastructure
iftop ncurses package can be installed on any OpenBSD system using the following command
pkg_add iftop
iftop -i em0
1.91Mb 3.81Mb 5.72Mb 7.63Mb 9.54Mb└───────────────┴───────────────┴───────────────┴───────────────┴───────────────10.0.2.15 => di-in-f190.1e100.net 23.5Kb 11.8Kb 2.95Kb <= 3.22Mb 805Kb 201Kb10.0.2.15 => dj-in-f106.1e100.net 0b 18.1Kb 6.53Kb <= 0b 708Kb 218Kb10.0.2.15 => dj-in-f94.1e100.net 0b 4.41Kb 1.10Kb <= 0b 57.5Kb 14.4Kb10.0.2.15 => dj-in-f100.1e100.net 0b 4.14Kb 1.03Kb <= 0b 43.3Kb 10.8Kb10.0.2.15 => dh-in-f95.1e100.net 5.69Kb 4.36Kb 1.60Kb <= 133Kb 29.5Kb 8.06Kb10.0.2.15 => dj-in-f119.1e100.net 0b 3.26Kb 834b <= 0b 19.6Kb 4.91Kb10.0.2.15 => ig-in-f94.1e100.net 0b 4.66Kb 1.16Kb <= 0b 12.8Kb 3.21Kb10.0.2.15 => dj-in-f148.1e100.net 12.5Kb 2.49Kb 638b <= 49.9Kb 9.99Kb 2.50Kb10.0.2.15 => dg-in-f102.1e100.net 0b 6.57Kb 1.64Kb <= 0b 5.51Kb 1.38Kb────────────────────────────────────────────────────────────────────────────────TX: cum: 832KB peak: 89.6Kb rates: 51.7Kb 62.4Kb 20.1KbRX: 20.6MB 3.43Mb 3.43Mb 1.66Mb 472KbTOTAL: 21.4MB 3.48Mb 3.48Mb 1.72Mb 492Kb
Records TCP/IP flow metadata
OpenBSD has the pflow(4) virtual network interface
Useful for network monitoring, DDoS protection, etc.
$ cat /etc/hostname.pflow0flowsrc 192.168.103.1 flowdst 192.168.103.252:9995pflowproto 10
Lots of collector options available in ports: nfsen, flow-tools, pmacct, FastNetMon and others.
More info:
# fastnetmon_clientFastNetMon 1.1.7 master git- Try Advanced edition: https://fastnetmon.comIPs ordered by: packetsIncoming traffic 1505664 pps 15397 mbps 85 flows37.203.[redacted] 59184 pps 485 mbps 0 flows37.203.[redacted] 45040 pps 504 mbps 0 flows37.203.[redacted] 26924 pps 270 mbps 0 flows185.55.[redacted] 24211 pps 240 mbps 0 flows5.134.[redacted] 23872 pps 290 mbps 0 flows45.11.[redacted] 23634 pps 250 mbps 0 flows185.55.[redacted] 22451 pps 255 mbps 0 flows45.11.[redacted] 20943 pps 254 mbps 0 flows185.55.[redacted] 20298 pps 246 mbps 0 flows5.134.[redacted] 20188 pps 236 mbps 0 flows
Single-stack clients will only have IPv6
Translator box will strip all headers and replace them with IPv4
Extension to NAT64 to access IPv4-only applications (like Skype or Whatsapp)
Handset pretends there is an IPv4 address (CLAT) and sends IPv4 packets in UDP over IPv6
Define the translating prefix
Add NAT64 configuration to PF
Remove IPv4 from the internal network
Configure DNS64 on unbound
pass in quick on $int_if inet6 from any to 64:ff9b::/96 \ af-to inet from (egress:0) keep state
module-config: "dns64 validator iterator"dns64-prefix: 64:FF9B::/96
and it's done!
Let's ask if there are any questions before continuing. Make sure we have everyone onboard.
http://www.openbsd.org/ The official OpenBSD website – to donate: http://www.openbsd.org/donations.html and please do donate, corporates may prefer https://www.openbsdfoundation.org/ - a Canadian non-profit
The PF User Guide on the OpenBSD web site
Note: You can convert the man page of pf.conf to PDF for reading in your favourite reader with the command:
man -T pdf pf.conf > pf.conf.pdf
Michael W Lucas: Absolute OpenBSD, 2nd ed.
Peter N. M. Hansteen: The Book of PF, 3rd ed.
Elizabeth D. Zwicky, Simon Cooper, D. Brent Chapman Building Internet Firewalls, 2nd ed.
http://undeadly.org/ - The OpenBSD Journal news site
http://bsdly.blogspot.com/ - Peter's rants^H^H^H^H^Hblog posts
http://www.tedunangst.com/flak/ tedu@ on developments
Notes for this slide
Notes for this other slide
Sysadmin, OpenBSD user since before the millenium
Wrote The Book of PF, now in its third edition
Blog at bsdly.blogspot.com about (lack of) sanity in IT
Works at Tietoevry Create
Yes, I'll do another book any decade now
Keyboard shortcuts
↑, ←, Pg Up, k | Go to previous slide |
↓, →, Pg Dn, Space, j | Go to next slide |
Home | Go to first slide |
End | Go to last slide |
Number + Return | Go to specific slide |
b / m / f | Toggle blackout / mirrored / fullscreen mode |
c | Clone slideshow |
p | Toggle presenter mode |
t | Restart the presentation timer |
?, h | Toggle this help |
Esc | Back to slideshow |