FreeBSD is a free, open-source operating system based on the Berkeley Software Distribution (BSD), a branch of UNIX developed at the University of California, Berkeley. It’s known for being stable, secure, highly performant, and extremely well-suited for servers, networking, storage, and appliances.
In this blog post I will document basic FreeBSD 14.3 operational procedures.
Procedure to check vulnerabilities of operating system
FreeBSD is full Operating System with external software packages installed into the base OS. Base OS known and fixed vulnerabilities can be checked by command
freebsd-update fetch
Procedure to check what has changed in operating system
FreeBSD utility freebsd-update has IDS (Integrity Detection System) feature which compares the system against a "known good" index of the installed release.
root@fbsd01:~ #freebsd-update IDSsrc component not installed, skipped Looking up update.FreeBSD.org mirrors... 3 mirrors found. Fetching public key from update1.freebsd.org... done. Fetching metadata signature for 14.3-RELEASE from update1.freebsd.org... done. Fetching metadata index... done. Fetching 2 metadata files... done. Inspecting system... done. /etc/group has SHA256 hash 6c7e6fa5f3253e4218e190a4de53de98a407e90485872ce5d96a8949ac5342f3, but should have SHA256 hash a76791033e18dcb526c30a6417bdb31ef774649f84e7f4ca0e745549cb15729c. /etc/master.passwd has SHA256 hash d35837e8347afb147b3eaa4a075386c20df70ffc5a49ed71439d661659055403, but should have SHA256 hash 55dfb5a41ebad44523b26cba443d94c3d55e0b39a32558f81a1d50fed964ec34. /etc/passwd has SHA256 hash c707fb0612c13b3ef378fab9907a735d59fc73a5d7437fb5d5a61772a36d7cc5, but should have SHA256 hash 57d2a756f16439eb2bc13af8d4b0a958ccec88643c6246cfc00e5b0894417eec. /etc/pwd.db has SHA256 hash 798e5edf1f3bebc8ef16a1a857a6022402269dbfde563f2bf62c0f9df5f8d0c1, but should have SHA256 hash bd30e09f6e06e4430bbb8fa20c4ed46babaec585d5580a92244c6a4227c5af56. /etc/spwd.db has SHA256 hash e17660df39eb0f8785e2db7d54c356d96d53c3051c40296762a7b61050d004c7, but should have SHA256 hash 5b8454a1d288eef2ed215f2280ac5cf9e9197ac1d2a1e46a67ba38c2c0c370e7. /etc/sysctl.conf has SHA256 hash 90082ca647578d2c6ff359d86bf65cc46cb9d8aba2518e4625a60c3b4a2f03b4, but should have SHA256 hash 45f469e7a9b4eef887bab7b55397305043fe101e1d6ce6f7e23d758e72f56dc6. root@fbsd01:~ #
This is very handy tool to trust your system. In the example above, everything looks OK, because it is expected to have /etc/* files different to original OS installation. If you want, you can exclude multiple noisy paths from the output by following one-liner ...
freebsd-update IDS | egrep -v '^/etc/|^/var/|^/root/'
Excluded directories can be extended as you wish.
Note, that this is just a FreeBSD base operating system verification. A proper security integrity check on FreeBSD is layered. No single tool is enough and you should combine base-system verification, package verification, and behavior checks.
Procedure to check vulnerabilities of external software packages
FreeBSD pkg system can do an audit of installed packages against known vulnerabilities.
root@r1.c4c.uw.cz:/etc #pkg auditpkcs11-helper-1.29.0_3 is vulnerable: pkcs11-helper -- deserialize buffer overflow WWW: https://vuxml.FreeBSD.org/freebsd/1a46e84d-c406-11f0-b513-0da7be77c170.html curl-8.11.1_1 is vulnerable: curl -- Multiple vulnerabilities CVE: CVE-2025-4947 CVE: CVE-2025-5025 WWW: https://vuxml.FreeBSD.org/freebsd/533b4470-3f25-11f0-b440-f02f7432cf97.html git-2.48.1 is vulnerable: git -- multiple vulnerabilities CVE: CVE-2025-48386 CVE: CVE-2025-48385 CVE: CVE-2025-48384 CVE: CVE-2025-46835 CVE: CVE-2025-27614 CVE: CVE-2025-27613 WWW: https://vuxml.FreeBSD.org/freebsd/2a4472ed-5c0d-11f0-b991-291fce777db8.html p5-Authen-SASL-2.17_1 is vulnerable: p5-Authen-SASL -- Insecure source of randomness CVE: CVE-2025-40918 WWW: https://vuxml.FreeBSD.org/freebsd/defe9a20-781e-11f0-97c4-40b034429ecf.html expat-2.6.4 is vulnerable: expat: improper restriction of xml entity expansion depth CVE: CVE-2024-8176 WWW: https://vuxml.FreeBSD.org/freebsd/8f71ad3b-14f5-11f0-87ba-002590c1f29c.html python311-3.11.11 is vulnerable: python -- several vulnerabilities CVE: CVE-2025-13836 CVE: CVE-2025-12084 WWW: https://vuxml.FreeBSD.org/freebsd/613d0f9e-d477-11f0-9e85-03ddfea11990.html cpython -- Use-after-free in "unicode_escape" decoder with error handler CVE: CVE-2025-4516 WWW: https://vuxml.FreeBSD.org/freebsd/e587b52d-38ac-11f0-b7b6-dcfe074bd614.html 7 problem(s) in 6 package(s) found. root@r1.c4c.uw.cz:/etc #
You can use Procedure to upgrade external software packages (previous section) to secure your external applications by upgrading to the latest version.
Procedure to update operating system
The FreeBSD OS update is a procedure for performing a minor version upgrade while remaining within the current major version.
For example, FreeBSD 14-RELEASE (14.0) --> 14.1 --> 14.2 --> 14.3 are all OS updates.
Let's document my typical OS update procedure ...
# Check current version of kernel, runtime, userland
freebsd-version -kru
# OS update
freebsd-update fetch
freebsd-update install
# Check current version of kernel, runtime, userland
freebsd-version -kru
# if kernel patch level is higher than runtime patch level, reboot the system to use new kernel
reboot
# try if there is something more to install
freebsd-update install
After minor FreeBSD OS update you may or may not need upgrade of external software packages. Packages upgrade is covered later in this blog post.
Procedure to upgrade operating system
The FreeBSD OS upgrade performs a major version upgrade of the operating system. It may be a bit contradictory, but freebsd-update utility with a special options (for example -r 14.0-RELEASE upgrade) is used for upgrade to the next major version.
In other words, FreeBSD 13.4 --> 14-RELEASE (14.0) is OS upgrade.
Let's document my typical OS upgrade procedure ...
Major FreeBSD OS upgrade usually require upgrade of external software packages. Packages upgrade is covered in next section.
Procedure to upgrade external software packages
FreeBSD includes some software suites as part of the base operating system, while additional software is provided and managed separately through external packages.
# Upgrade software packages installed in OS as additional software
pkg update # This is refresh of repository metadata. This is not partial (minor ver or security) upgrade
pkg upgrade # upgrade installed packages
How can I do a Software Package partial upgrade (aka update)?
- pkg upgrade always upgrades to the latest available version.
- In other words, pkg does not support partial upgrades (aka updates).
- You cannot say “upgrade only security fixes” or “stay on nginx 1.24”
- The repository always contains exactly one version per port
- So within FreeBSD, pkg upgrade installs the newest version of every installed package.
- There is no concept of “stable vs updates” repos like on some Linux distros. This is by design.
If you want to keep some Software Package version, you can prevent specific packages from upgrading by using pkg lock.
For example, to lock particular NGINX version, use
pkg lock nginx
If you want to remove such restriction, use
pkg unlock nginx
To list all package locks, use
pkg lock -l
This is how FreeBSD pkg system works.
Procedure to change hostname
# Check current hostname
hostname
# Change hostname
hostname r1.int.msp.businesscloud.cz
# Change hostname permanently
sysrc hostname="r1.int.msp.businesscloud.cz"
Procedure to use DNS
To use particular DNS server, edit configuration file /etc/resolve.conf. File should contain something like ...
search int.example.com
nameserver 10.1.10.254
Procedure to change IP settings permanently
FreeBSD configuration is saved in /etc/rc.conf and managed by sysrc.
# Set IP settings on vmx0 interface
sysrc ifconfig_vmx0="inet 10.1.0.254 netmask 255.255.255.0"
# Set IP on loopback
sysrc ifconfig_lo0_alias0="inet 10.0.0.1/32"
# set default router (default gateway)
sysrc defaultrouter="10.1.0.1"
# show the current IP settings on vmx0 interface
ifconfig vmx0
Procedure to change IP settings on running system
# Set IP settings on vmx0 interface
ifconfig vmx0 inet 10.1.0.254 netmask 255.255.255.0
# Set IP alias on loopback
ifconfig lo0 alias 10.0.0.1/32
Procedure to set Time Servers
To use above Time Servers, edit configuration file /etc/ntp.conf and ensure time servers are there …
# These servers must be defined
server time.cloudflare.com iburst
server time.google.com iburst
server ntp.cesnet.cz iburst
# Default public servers from the pool.ntp.org must be disabled
#pool 0.freebsd.pool.ntp.org iburst
#pool 2.freebsd.pool.ntp.org iburst
Procedure to enable and restart time service
# Enable NTPD as a service
sysrc ntpd_enable="YES"
sysrc ntpd_sync_on_start="YES"
# Start service
service ntpd start
# Restart service
service ntpd restart
# Check and Verify NTP time servers
ntpq -p
Procedure to set and verify Time Zone
timedatectl set-timezone UTC
timedatectl
Procedure to configure IPFW firewall
sysrc firewall_enable="YES"
sysrc firewall_script="/etc/ipfw.rules"
sysrc firewall_nat_enable="YES"
Firewall Script /etc/ipfw.rules to apply Zero Trust policy and allow only specific connections.
#!/bin/sh
# Define binaries
ipfw="/sbin/ipfw"
# Define interfaces
wan_if="vmx0"
mgmt_if="vmx4"
################# DESTROY FIREWALL – FW RULES, NAT RULES, TABLES #################
# FLUSH EXISTING IPFW RULES
$ipfw -q -f flush
# FLUSH EXISTING DYNAMIC NAT TRANSITION TABLE
$ipfw -q nat flush
# DELETE EXISTING NAT INSTANCE DEFINITION
$ipfw nat delete 1
# DELETE ALL EXISTING TABLES (FW ZONES)
ipfw table all destroy
################# NAT CONFIGURATION #################
# DNAT RULE
$ipfw nat 1 config if $wan_if redirect_port tcp 10.1.10.1:22 2222 redirect_port tcp 10.1.3.10:443 4443 redirect_port tcp 10.1.3.10:80 8080
# SNAT RULE
$ipfw add 50 nat 1 ip from any to any via $wan_if
# Allow the traffic destined for the NAT's rewritten destination (10.1.10.1 - JUMP-01)
$ipfw add 51 allow ip from any to 10.1.10.1 via $mgmt_if
$ipfw add 52 allow ip from 10.1.10.1 to any via $mgmt_if
# BEST PRACTICE INIT FIREWALL RULES
$ipfw add 100 allow ip from any to any via lo0
$ipfw add 200 deny ip from any to 127.0.0.0/8
$ipfw add 300 deny ip from 127.0.0.0/8 to any
$ipfw add 400 deny ip from any to ::1
$ipfw add 500 deny ip from ::1 to any
$ipfw add 600 allow ipv6-icmp from :: to ff02::/16
$ipfw add 700 allow ipv6-icmp from fe80::/10 to fe80::/10
$ipfw add 800 allow ipv6-icmp from fe80::/10 to ff02::/16
$ipfw add 900 allow ipv6-icmp from any to any icmp6types 1
$ipfw add 1000 allow ipv6-icmp from any to any icmp6types 2,135,136
################# DEFINE FIREWALL TABLES as ZONES #################
# Create the table of ZABBIX-JUMP zone
$ipfw table ZABBIX-JUMP create type addr
$ipfw table ZABBIX-JUMP add 10.1.10.1
# Create the table of ZABBIX-DEVOPS zone
$ipfw table ZABBIX-DEVOPS create type addr
$ipfw table ZABBIX-DEVOPS add 10.1.10.10
# Create the table of ZABBIX-FE-LB zone
$ipfw table ZABBIX-FE-LB create type addr
ipfw table ZABBIX-FE-LB add 10.1.3.10
# Create the table of ZABBIX-FE zone
$ipfw table ZABBIX-FE create type addr
$ipfw table ZABBIX-FE add 10.1.3.1
$ipfw table ZABBIX-FE add 10.1.3.2
# Create the table of ZABBIX-BE zone
$ipfw table ZABBIX-BE create type addr
$ipfw table ZABBIX-BE add 10.1.2.1
$ipfw table ZABBIX-BE add 10.1.2.2
# Create the table of ZABBIX-DB zone
$ipfw table ZABBIX-DB create type addr
$ipfw table ZABBIX-DB add 10.1.1.1
$ipfw table ZABBIX-DB add 10.1.1.2
$ipfw table ZABBIX-DB add 10.1.1.3
$ipfw table ZABBIX-DB add 10.1.1.10
# Create the table of ZABBIX-PROXY zone
$ipfw table ZABBIX-PROXY create type addr
$ipfw table ZABBIX-PROXY add 172.16.0.0/16
# Create the table of PRIVATE zone
$ipfw table PRIVATE create type addr
$ipfw table PRIVATE add 10.0.0.0/8
$ipfw table PRIVATE add 172.16.0.0/12
$ipfw table PRIVATE add 192.168.0.0/16
################# FIREWALL OPENINGS #############################
#################################################################
# Allow WAN access and ICMP
# ===============================
# Allow (WAN) Internet Access
$ipfw add 2000 allow ip from any to any via $wan_if
# Allow Access to everything, but not PRIVATE IP Subnets
$ipfw add 2010 allow ip from any to not "table(PRIVATE)"
$ipfw add 2011 allow ip from not "table(PRIVATE)" to any
# Allow ICMP traffic everywhere. Good for diagnostic purposes
$ipfw add 2020 allow icmp from any to any
#################################################################
# ME (R1) ALLOW RULES
# ===============================
# ALLOW RULES TO ME
# ===============================
# Allow UDP/53 (DNS) running on me
$ipfw add 3010 allow udp from any to me 53 in keep-state
# Allow TCP/22 (ssh) from JumpHost to Me (R1)
$ipfw add 3020 allow tcp from "table(ZABBIX-JUMP)" to me 22 in keep-state
# ===============================
# ALLOW RULES FROM ME
# ===============================
# Allow all IP traffic originating from Me (R1) to any destination going out to WAN
$ipfw add 3100 allow ip from me to any out via $wan_if keep-state
# Allow TCP/22 (ssh) from Me (R1) to JumpHost
$ipfw add 3110 allow tcp from me to "table(ZABBIX-JUMP)" 22 out keep-state
################################################################
#################################################################
# ZABBIX-DEVOPS RULES
# ===============================
# ALLOW RULES TO ZABBIX-DEVOPS
# ===============================
# Allow TCP/10050 (zabbix-server-agent) from ZABBIX-BE to ZABBIX-DEVOPS
$ipfw add 4000 allow tcp from "table(ZABBIX-BE)" to "table(ZABBIX-DEVOPS)" 10050 keep-state
# ===============================
# ALLOW RULES FROM ZABBIX-DEVOPS
# ===============================
# Allow TCP/22 (ssh) from ZABBIX-DEVOPS to ZABBIX-FE
$ipfw add 4100 allow tcp from "table(ZABBIX-DEVOPS)" to "table(ZABBIX-FE)" 22 keep-state
# Allow TCP/22 (ssh) from ZABBIX-DEVOPS to ZABBIX-BE
$ipfw add 4110 allow tcp from "table(ZABBIX-DEVOPS)" to "table(ZABBIX-BE)" 22 keep-state
# Allow TCP/22 (ssh) from ZABBIX-DEVOPS to ZABBIX-DB
$ipfw add 4120 allow tcp from "table(ZABBIX-DEVOPS)" to "table(ZABBIX-DB)" 22 keep-state
################################################################
#################################################################
# ZABBIX-BE RULES
# ===============================
# ALLOW RULES TO ZABBIX-BE
# ===============================
# Allow TCP/22 (ssh) from ZABBIX-DEVOPS to ZABBIX-FE
$ipfw add 5000 allow tcp from "table(ZABBIX-DEVOPS)" to "table(ZABBIX-BE)" 22 keep-state
# Allow TCP/10051 (Zabbix-agent-server) from ZABBIX-PROXY to ZABBIX-BE
$ipfw add 5010 allow tcp from "table(ZABBIX-PROXY)" to "table(ZABBIX-BE)" 10051 keep-state
# Allow TCP/10051 (Zabbix-agent-server) from ZABBIX-FE-LB to ZABBIX-BE
$ipfw add 5020 allow tcp from "table(ZABBIX-FE-LB)" to "table(ZABBIX-BE)" 10051 keep-state
# Allow TCP/10051 (Zabbix-agent-server) from ZABBIX-DB to ZABBIX-BE
$ipfw add 5021 allow tcp from "table(ZABBIX-DB)" to "table(ZABBIX-BE)" 10051 keep-state
# Allow TCP/10051 (Zabbix-agent-server) from ZABBIX-FE to ZABBIX-BE
$ipfw add 5022 allow tcp from "table(ZABBIX-FE)" to "table(ZABBIX-BE)" 10051 keep-state
# Allow TCP/10051 (Zabbix-agent-server) from ZABBIX-DEVOPS to ZABBIX-BE
$ipfw add 5022 allow tcp from "table(ZABBIX-DEVOPS)" to "table(ZABBIX-BE)" 10051 keep-state
# ===============================
# ALLOW RULES FROM ZABBIX-BE
# ===============================
# Allow TCP/10050 (zabbix-server-agent) from ZABBIX-BE to ZABBIX-FE
$ipfw add 5100 allow tcp from "table(ZABBIX-BE)" to "table(ZABBIX-FE)" 10050 keep-state
# Allow TCP/10050 (zabbix-server-agent) from ZABBIX-BE to ZABBIX-DB
$ipfw add 5110 allow tcp from "table(ZABBIX-BE)" to "table(ZABBIX-DB)" 10050 keep-state
# Allow TCP/5432 (postgresql) from ZABBIX-BE to ZABBIX-DB
$ipfw add 5120 allow tcp from "table(ZABBIX-BE)" to "table(ZABBIX-DB)" 5432 keep-state
################################################################
#################################################################
# ZABBIX-FE RULES
# ===============================
# ALLOW RULES TO ZABBIX-FE
# ===============================
# Allow TCP/22 (ssh) from ZABBIX-DEVOPS to ZABBIX-FE
$ipfw add 6000 allow tcp from "table(ZABBIX-DEVOPS)" to "table(ZABBIX-FE)" 22 keep-state
# Allow TCP/443 (https) from ZABBIX-DEVOPS to ZABBIX-FE
$ipfw add 6010 allow tcp from "table(ZABBIX-DEVOPS)" to "table(ZABBIX-FE)" 443 keep-state
# Allow TCP/10050 (zabbix-server-agent) from ZABBIX-BE to ZABBIX-FE
$ipfw add 6020 allow tcp from "table(ZABBIX-BE)" to "table(ZABBIX-FE)" 10050 keep-state
# Allow TCP/80,443 (web) from ZABBIX-FE-LB to ZABBIX-FE
$ipfw add 6030 allow tcp from "table(ZABBIX-FE-LB)" to "table(ZABBIX-FE)" 80 keep-state
$ipfw add 6040 allow tcp from "table(ZABBIX-FE-LB)" to "table(ZABBIX-FE)" 443 keep-state
# ===============================
# ALLOW RULES FROM ZABBIX-FE
# ===============================
# Allow TCP/5432 (postgresql) from ZABBIX-FE to ZABBIX-DB
$ipfw add 6100 allow tcp from "table(ZABBIX-FE)" to "table(ZABBIX-DB)" 5432 keep-state
################################################################
#################################################################
# ZABBIX-FE-LB RULES
# ===============================
# ALLOW RULES TO ZABBIX-FE-LB
# ===============================
# Allow TCP/22 (ssh) from ZABBIX-DEVOPS to ZABBIX-FE-LB
$ipfw add 7000 allow tcp from "table(ZABBIX-DEVOPS)" to "table(ZABBIX-FE-LB)" 22 keep-state
# Allow TCP/443 (https) from ZABBIX-DEVOPS to ZABBIX-FE-LB
$ipfw add 7010 allow tcp from "table(ZABBIX-DEVOPS)" to "table(ZABBIX-FE-LB)" 443 keep-state
# Allow TCP/80,443 (web) from ANY to ZABBIX-FE-LB
$ipfw add 7020 allow tcp from any to "table(ZABBIX-FE-LB)" 80 keep-state
$ipfw add 7030 allow tcp from any to "table(ZABBIX-FE-LB)" 443 keep-state
# ===============================
# ALLOW RULES FROM ZABBIX-FE-LB
# ===============================
# Allow TCP/10051 (Zabbix-agent-server) from ZABBIX-FE-LB to ZABBIX-BE
$ipfw add 7100 allow tcp from "table(ZABBIX-FE-LB)" to "table(ZABBIX-BE)" 10051 keep-state
################################################################
#################################################################
# RULES TO DENY EVERYTHING ELSE
# ===============================
# EVERYTHING ELSE IS DENY – ZERO TRUST
$ipfw add 65500 deny ip from any to any
Procedure to reload IPFW firewall script
# Run ipfw script on background
/etc/ipfw.rules &
IPFW DNAT Configuration Management
# Show NAT Configuration
ipfw nat show config
# Add DNAT rule
ipfw nat 1 config if vmx0 redirect_port 10.1.10.1:22 2222
# Delete DNAT rule
ipfw nat delete 1
IPFW TABLES Configuration Management
# Show particular Table configuration. In this example we use Table name PRIVATE
ipfw table PRIVATE list
ipfw table PRIVATE info
Conclusion
This is how I do manage my FreeBSD systems. Of course, there are other ways how to manage your FreeBSD Operating Systems. Your mileage may vary.
For example
- You can prefer to use PORTS (/usr/ports) instead of PACKAGES (pkg) and your update and upgrade procedures must be different.
- You can prefer to use /etc/rc.firewall with rc.conf variables
- firewall_enable="YES"
- firewall_type="OPEN
- firewall_script="/etc/rc.firewall" # Default FreeBSD firewall script
- such use requires different approach to firewall management
Anyway, these are my preferred operational procedures and I hope it will help someone else in FreeBSD community or it opens discussion about best practices and specific use cases.
No comments:
Post a Comment