PF PF config suggestions - web server?

Hi there. I have a VPS running nginx as web server, local unbound, local maria-db and sshd.

I'd be glad if anyone could confirm that I have no weird rule for the main server purposes I listed above and so that I'd continue studying PF. :)

So this is my pf.conf (FreeBSD 13)
(the table "f2b" belongs to fail2ban)

Thanks in advance!

Bash:
ext_if="vtnet0"
set skip on lo
set block-policy drop
set loginterface $ext_if
set optimization normal
set ruleset-optimization basic
set limit { states 200000, frags 200000, src-nodes 100000, table-entries 400000 }

privip = "{ 127.0.0.0/8, 192.168.0.0/16, 172.16.0.0/12, 10.0.0.0/8, 169.254.0.0/16, 192.0.2.0/24, 0.0.0.0/8, 240.0.0.0/4 }"
int_services = "{domain, ntp, smtp, smtps, submission, pop3, pop3s, imap, imaps, www, https, ftp, sftp, ssh}"
icmp_types = "{ echoreq unreach }"

# Blocklists
table <blocklist1> persist file "/usr/pf/banlist.txt"
table <f2b> persist

#Whitelist table
whitelist ="{213.133.99.100, 213.133.99.99,213.133.98.98}"

scrub in on $ext_if all fragment reassemble
antispoof for $ext_if inet

anchor "f2b/*"
block return in log all

### Block spooks
block in from no-route to any
block in from urpf-failed to any
block in quick on $ext_if from any to 255.255.255.255

### block probes that can possibly determine our operating system by disallowing
### certain combinations that are commonly used by nmap, queso and xprobe2, who
### are attempting to fingerprint the server.
block in quick on $ext_if proto tcp flags FUP/WEUAPRSF
block in quick on $ext_if proto tcp flags WEUAPRSF/WEUAPRSF
block in quick on $ext_if proto tcp flags SRAFU/WEUAPRSF
block in quick on $ext_if proto tcp flags /WEUAPRSF
block in quick on $ext_if proto tcp flags SR/SR
block in quick on $ext_if proto tcp flags SF/SF

block drop in quick from <f2b>

# Drop all Non-Routable Addresses
block drop in quick on $ext_if from $privip to any
block drop out quick on $ext_if from any to $privip

block drop in log quick on $ext_if from <blocklist1>
block drop out log quick on $ext_if to <blocklist1>

#pass in quick on lo0 all - needed?
pass quick on $ext_if proto { tcp udp } to port $int_services
pass quick from $whitelist to any keep state

pass quick inet proto icmp icmp-type $icmp_types
pass quick proto ipv6-icmp from any to any
 
I didn't look at everything, nor am I a great PF expert, but a few things stick out.

First: You are allowing a whole lot of inbound traffic. Is your machine really going to be a DNS, NTP, mail (including POP and IMAP), and ftp server? You'll run a local unbound, so you need incoming port 53 (domain), and you need incoming NTP, but all mail related and ftp stuff can probably go.

Second: Do you need UDP and TCP for all these protocols? I don't think anyone would for example use port 80 or 443 over UDP.

Third: These days, the use of http (port 80) is quite deprecated. I know many web servers still support it (usually, with nothing but a redirect to the equivalent https page). But I'm wondering whether it's beginning to be best practice to just outright block port 80. Why? Because stupid script kiddies beat it up all the time, and at the minimum that costs network bandwidth and fills up logs. And if (god forbid) you have something misconfigured, they might get in.

Last: We discussed in another thread that ssh is very important to secure, both because breaking in via ssh is so powerful, and because script kiddies try so hard on it. If there is any way to avoid it, I would not offer an ssh server on the standard port 22, without more interesting security. Like door knocking, moving it to a non-standard port, having special "a few hacks and you're out" blockers, and such. None of those are perfect security, but they help. Layers ...
 
Thank you so much Ralph, for your answer.

1- I do have POP, IMAP and mail services, yes. And FTPd, from time to time.
2- Well, true on 80/443 UDP thing.. But I guess HTTP/3 will be implemented on UDP-only, right? Just a curious question.
3- I'd completely turn 80 off, as all my sites are https-only (redirect from 80 to 443), but would that badly affect SEO? I mean don't SEO robots first check the 80 port/http default?
4- I run a different SSH port (not the default one) but just removed that port from my example conf above. Indeed, sshd.log file gets filled dramatically fast when it's on 22-default.

5- How about "antispoof for $ext_if inet" and "block in from urpf-failed to any" being together?
Would only having "block in from urpf-failed to any" be enough for antispoofing? As I've heard that it's new implementation of "antispoof".

Finally, my system automatically updates time through NTP servers. But it isn't an NTP server of course. Would that differ, in terms of PF rules above?
Thank you once again.
 
int_services = "{domain, ntp, smtp, smtps, submission, pop3, pop3s, imap, imaps, www, https, ftp, sftp, ssh}"
You don't need to allow incoming DNS and NTP.

Code:
#pass in quick on lo0 all - needed?
Not needed because you have set skip on lo at the top.

You don't have to block drop everything, you've set the default response to drop with set block-policy drop. Just a block will be enough.

Code:
block return in log all
return is fine for TCP connections but you don't want to respond to UDP. UDP is easily spoofed, just drop those. You can also just block TCP connections, as you've already set the default action to drop already.
 
"Pass quick" from whitelist must be before block rule from blacklist otherwise if some of your white listed IP get inside the dynamic blacklist they will be blocked.

Code:
pass quick from $whitelist to any keep state
block drop in log quick on $ext_if from <blocklist1>
block drop out log quick on $ext_if to <blocklist1>

To reduce SSH noise from the bots use different port from 22.
 
You don't need to allow incoming DNS
This reminds me of a quote from Cor Bosman. He worked for a well-known Dutch ISP at the time Windows users began to use firewalls: "I'M BEING HACKED FROM YOUR COMPUTER!!! AT PORT 53!! At least 50 complaints a week.¨.
 
I'd be glad if anyone could confirm that I have no weird rule for the main server purposes I listed above and so that I'd continue studying PF. :)
Bash:
privip = "{ 127.0.0.0/8, 192.168.0.0/16, 172.16.0.0/12, 10.0.0.0/8, 169.254.0.0/16, 192.0.2.0/24, 0.0.0.0/8, 240.0.0.0/4 }"
whitelist ="{213.133.99.100, 213.133.99.99,213.133.98.98}"
You really want to convert these into tables, as those macros will expand to an unnecessarily large number of rules. Ruleset optimization might do that for you, but it's still better to do it right in the first place.
 
Really useful suggestions - thank you all, will make the changes and return here :)

You need port 80 to hit the web server to perform the redirect which is done via HTTP.

Well, that's right but my website (hosted on my VPS) has HSTS-Preload enabled and my httpS submission was accepted by most of current browsers (Firefox, Chrome, Edge, Opera). So I assume in such case, no 80 port needed at all?

For reference: https://hstspreload.org/


This reminds me of a quote from Cor Bosman. He worked for a well-known Dutch ISP at the time Windows users began to use firewalls: "I'M BEING HACKED FROM YOUR COMPUTER!!! AT PORT 53!! At least 50 complaints a week.¨.
That was fun. :)

You don't need to allow incoming DNS and NTP.


Not needed because you have set skip on lo at the top.

You don't have to block drop everything, you've set the default response to drop with set block-policy drop. Just a block will be enough.


return is fine for TCP connections but you don't want to respond to UDP. UDP is easily spoofed, just drop those. You can also just block TCP connections, as you've already set the default action to drop already.

How can I separate TCP and UDP in such "block in all" case to return to only TCP queries?


Best.
 
So I assume in such case, no 80 port needed at all?
If users enter https://yoursite.example.com then no, port 80 isn't needed. I often do enable port 80 and provide a redirect to the https site. This is so unsuspecting users might just enter yoursite.example.com without entering http or https in front of it. But you are going to see quite a bit of noise from bots and other malware poking at the 'regular' website though.
 
If users enter https://yoursite.example.com then no, port 80 isn't needed. I often do enable port 80 and provide a redirect to the https site. This is so unsuspecting users might just enter yoursite.example.com without entering http or https in front of it. But you are going to see quite a bit of noise from bots and other malware poking at the 'regular' website though.

AFAIK, when (and if) a browser accepts your HSTS submission (through https://hstspreload.org), it defaults your site (within its local database) as httpS://yoursite.com since acceptance of the submission towards any new versions of it, so even if you have 80 port closed and a visitor would type: http://yoursite.com, the browser itself would automatically redirect him/her to -> httpS://
 
You're relying on the browser behavior. Again: you need port 80 to hit the web server to perform the redirect which is done via HTTP.
 
Back
Top