Configuring Bourne shell on FreeBSD

What is a Bourne shell?

The Bourne shell, also often referred to as /bin/sh or just sh, has been a default shell for users on FreeBSD for many major versions, while C shell was the default shell for root. From FreeBSD 14, the default shell for root is the Bourne shell.

Changing to Bourne shell on FreeBSD.

The shell for a user in FreeBSD can be changed with the chsh utility.

Configuring the Bourne shell in FreeBSD.

The Bourne shell in FreeBSD is configured by editing the global configuration file /etc/profile and/or the local user configuration file .shrc. These files contains commands, that will be executed, when the shell is started, such as when the user logs into the system.

In this example, the global configuration sets the blocksize for default file operations, ccache settings, the default editor, he default pager and a prompt. The prompt shows a “$” for a normal user and a “#” for root.

# nano /etc/profile
BLOCKSIZE=K
CCACHE=1
CCACHE_DIR=/var/cache/ccache
EDITOR=/usr/local/bin/nano
PAGER=less
VISUAL=$EDITOR
if [ "$(id -u)" -eq 0 ]; then
  PS1='# '
else
  PS1='$ '
fi
export BLOCKSIZE CCACHE CCACHE_DIR EDITOR PAGER PS1 VISUAL

If the user would like to add or change custom settings, then this can be set in the local user configuration file. If this is not relavant, the file should be deleted, because it does contain settings, that could interfere with the global settings.

$ rm .shrc
# rm .shrc

Testing.

The Bourne shell can now be tested. In this example, a user log in and switch to root.

$ whoami
jennifer
$ su -
Password: Pencil
# whoami
root
# echo $EDITOR
/usr/local/bin/nano

The different kinds of shells.

There are different kinds of shells, such as login shells and sub-shells. The sub-shells are also known as non-login shells. The difference is, that a login shell reads global configuration files, while sub-shells just inherits the settings from the original shell. In this example, the first shell will be a login-shell, while the second will be a sub-shell.

FreeBSD/amd64 (wopr) (ttyv0)

Login: stephen
Password: Pencil
__ __________ ____________________
/ \ / \_____ \\______ \______ \
\ \/\/ // | \| ___/| _/
\ // | \ | | | \
\__/\ / \_______ /____| |____|_ /
\/ \/ \/
Greetings, professor Falken. How about a nice game of chess?
$ sh
$ su stephen
$ logout
$ logout

More about shells.

DOAS for FreeBSD

What is DOAS?

doas is a utility, that can permit users to execute commands as root or as another user. Groups as well as users can be permitted to do.

Installing DOAS on FreeBSD.

# pkg install doas

The manual for doas is nice and simple.

# man doas.conf

Configuring DOAS.

In this example, I will configure doas to permit members of the wheel group to use the FreeBSD service command script, that can start and stop services, such as an nginx web server, without a password. This is a safe and trivial command to disable password requirement for.

# nano /usr/local/etc/doas.conf
permit nopass :wheel as root cmd service

If I wanted to limit, which arguments, that can be passed to the command, I could specifiy this with the args feature.

# nano /usr/local/etc/doas.conf
permit nopass :wheel as root cmd service args nginx onestart

Testing.

$ doas service nginx onestart
nginx: the configuration file /usr/local/etc/nginx/nginx.conf syntax is ok
nginx: configuration file /usr/local/etc/nginx/nginx.conf test is successful
Starting nginx

Be aware, that doas requires, that the command is matched exactly. The following definition will not work.

# nano /usr/local/etc/doas.conf
permit nopass :wheel as root cmd /usr/sbin/service args nginx onestart
$ doas service nginx onestart
doas: Operation not permitted

SUDO for FreeBSD

Do not use sudo for FreeBSD. I tested sudo for FreeBSD and despite, that it is widely used in online procedures and documentation, I had to conclude, that it is somewhat poorly documented and simply does not work properly for FreeBSD.

# visudo
david ALL=(ALL) NOPASSWD: /usr/sbin/service nginx onestart

One thing, in particular, that did not work, was the ability for a member of the wheel group to execute a trivial safe command as root without the requirement for a password. sudo kept asking for password.

$ id
$ sudo service nginx onestart
Password:
sudo: a password is required

I recommend deinstalling sudo and instead using security/doas. This is also recommended on the FreeBSD Forums.

# rm /usr/local/etc/sudo.conf
# rm /usr/local/etc/sudoers
# pkg remove sudo

References

Renaming, resizing and reducing screenshots from Android

The raw screenshot images from an Android phone usually come in with a filename in the following naming format, is fairly compressed and contain metadata about the phone itself.

$ du -sh -- *
309K Screenshot_20250108_144158_Thunderbird.jpg
373K Screenshot_20250108_144212_Thunderbird.jpg
185K Screenshot_20250108_144506_Thunderbird.jpg

Renaming screenshots from Android.

I prefer to rename them, so they begin with ISO date and time and use system friendly characters.

$ for file in Screenshot_*; do mv "$file" "$(echo "$file" | sed -E 's/Screenshot_([0-9]{4})([0-9]{2})([0-9]{2})_([0-9]{2})([0-9]{2})([0-9]{2}).*/\1-\2-\3-\4-\5-\6-screenshot.jpg/')"; done
$ du -sh -- *
309K 2025-01-08-14-41-58-screenshot.jpg
373K 2025-01-08-14-42-12-screenshot.jpg
185K 2025-01-08-14-45-06-screenshot.jpg

Resizing screenshots from Android with ImageMagick.

The default size is the size of the display, which is usually fine, but they can be optionally resized with ImageMagick to save space. The change in size and quality has no significant impact on the screenshot image quality, while the reduction in file size is huge as it schrinks to about 15% of the original size.

$ magick mogrify -resize 900x900 -quality 75 *-screenshot.jpg
$ du -sh -- *
33K 2025-01-08-14-41-58-screenshot.jpg
45K 2025-01-08-14-42-12-screenshot.jpg
25K 2025-01-08-14-45-06-screenshot.jpg

Reducing screenshots from Android with JPEGOPTIM.

The filesize can be lossless optimized even further, and metadata removed for added privacy, with JPEGOPTIM.

$ jpegoptim --strip-all --all-progressive *-screenshot.jpg
$ du -sh -- *
33K 2025-01-08-14-41-58-screenshot.jpg
41K 2025-01-08-14-42-12-screenshot.jpg
25K 2025-01-08-14-45-06-screenshot.jpg

Final screenshot images.

The original screenshots have now been renamed, resized and reduced, so they use a minimum of storage space and no longer contains metadata for added privacy. The proces has overwritten the original ones, so the storage space has been freed. The screenshots are ready for archiving and publishing on Internet.

Thunderbird for Android

I wanted to test a privacy aware IMAP mail client for Android and a bit of research lead to Thunderbird for Android.

Requirements for a mobile mail client.

An IMAP mail client for Android and mobile phone users must be simple to configure and use.

  1. It should be written by a trusted developer.
  2. It should have a simple configuration in which you can enter the name of the incoming mail server, outgoing mail server, your user name and your password.
  3. It must support a simple way to use SSL/TLS and STARTTLS.
  4. It should have support for installing the mail server encryption certificate.
  5. It should have a simple display of the account and its mail folders, such as the inbox and other folders, you might have. It should not use virtual folders nor re-arrange things away from how they really are stored on the mail server.
  6. It should support correct use of special folders, such as draft, archive and trash.
  7. It should support a preference for dark theme.

Installing Thunderbird for Android.

The download was only 7 MB. The intro stated, that I could import settings from the desktop computer client, but the instructions was incorrect. There are no export to mobile feature nor any QR code. I configured an account manually.

  1. Release by Mozilla Foundation. Highly trusted for open source and privacy.
  2. The entering of account information was straight forward correct and worked in first attempt.
  3. SSL and STARTTLS was supported.
  4. The install of encryption certificate was not an option, but I could “accept the risc” and it accepted the server certificate. This happens, when mail servers has an encryption certificate, that is signed by a certificate authority, that is not known by the Android phone.
  5. The display of email was correct and directly as they are stored in the server mail folders.
  6. The special folders was correctly supported and setup in first run.
  7. The dark theme was on by default, which should be following the system preference.

Using Thunderbird for Android.

Reading email was as simple as opening Thunderbird for Android and tapping the email from the inbox folder. The content was quickly displayed in a simple and readable format.

Sending email worked in first attempt. The sender name, email address and content format was perfect.

Conclusion.

Thunderbird for Android proved to be far beyond my expectations. It fully meet my requirements.

Setting Default Audio Output on FreeBSD

% cat /dev/sndstat
Installed devices:
pcm0: <Realtek ALC897 (Rear Analog)> (play/rec) default
pcm1: <Realtek ALC897 (Front Analog)> (play/rec)
pcm2: <Realtek ALC897 (Internal Digital)> (play)
pcm3: <Intel (0x2818) (HDMI/DP 8ch)> (play)
No devices installed from userspace.

Set the preferred default audio output.

% sysctl hw.snd.default_unit=1
hw.snd.default_unit: 0 -> 1

It can be made persistant across reboots.

# echo "hw.snd.default_unit=1" >> /etc/sysctl.conf

Direct Rendering Manager (DRM) kernel module for Intel and AMD chips on FreeBSD.

The Direct Rendering Manager (DRM) kernel module for Intel or AMD chips for FreeBSD is a subsystem of the Linux kernel. DRM is responsible for interfacing with GPUs on modern video cards. It is graphics drivers for Intel and AMD video cards and are used for console and graphics.

Install the DRM metaport. This will automatically detect the correct version of the DRM kernel module and install the GPU firmware.

# pkg install drm-kmod

The kernel module should be loaded at boot time.

# sysrc kld_list="i915kms"

The user must be a member of the video group.

# pw groupmod video -m foo

Issue: Blank screen after boot with DRM 6.1 for Intel on FreeBSD 14.2.

When FreeBSD boots and kernel modules are loaded, the HDMI screen goes blank, when DRM is loaded. There is no longer an active console. The system does accept SSH and does provide a virtual shell.

I installed the DRM kernel module from the latest binary repository branch on FreeBSD 14.2 on a computer with Intel Raptor Lake UHD Graphics 770.

# pciconf -lv | grep -B3 display
vgapci0@pci0:0:2:0: class=0x030000 rev=0x04 hdr=0x00 vendor=0x8086 device=0xa780 subvendor=0x1043 subdevice=0x8882
vendor = 'Intel Corporation'
device = 'Raptor Lake-S GT1 [UHD Graphics 770]'
class = display
# pkg install drm-kmod
Updating FreeBSD repository catalogue...
FreeBSD repository is up to date.
All repositories are up to date.
The following 130 package(s) will be affected (of 0 checked):
New packages to be INSTALLED:
drm-61-kmod: 6.1.92.1401000_3
drm-kmod: 20220907_3
gpu-firmware-amd-kmod-aldebaran: 20230625.1401000_2
gpu-firmware-amd-kmod-arcturus: 20230625.1401000_2
gpu-firmware-amd-kmod-banks: 20230625.1401000_2
gpu-firmware-amd-kmod-beige-goby: 20230625.1401000_2
gpu-firmware-amd-kmod-bonaire: 20230625.1401000_2
gpu-firmware-amd-kmod-carrizo: 20230625.1401000_2
gpu-firmware-amd-kmod-cyan-skillfish2: 20230625.1401000_2
gpu-firmware-amd-kmod-dcn-3-1-4: 20230625.1401000_2
gpu-firmware-amd-kmod-dcn-3-1-5: 20230625.1401000_2
gpu-firmware-amd-kmod-dcn-3-1-6: 20230625.1401000_2
gpu-firmware-amd-kmod-dcn-3-2-0: 20230625.1401000_2
gpu-firmware-amd-kmod-dcn-3-2-1: 20230625.1401000_2
gpu-firmware-amd-kmod-dimgrey-cavefish: 20230625.1401000_2
gpu-firmware-amd-kmod-fiji: 20230625.1401000_2
gpu-firmware-amd-kmod-gc-10-3-6: 20230625.1401000_2
gpu-firmware-amd-kmod-gc-10-3-7: 20230625.1401000_2
gpu-firmware-amd-kmod-gc-11-0-0: 20230625.1401000_2
gpu-firmware-amd-kmod-gc-11-0-1: 20230625.1401000_2
gpu-firmware-amd-kmod-gc-11-0-2: 20230625.1401000_2
gpu-firmware-amd-kmod-gc-11-0-3: 20230625.1401000_2
gpu-firmware-amd-kmod-gc-11-0-4: 20230625.1401000_2
gpu-firmware-amd-kmod-green-sardine: 20230625.1401000_2
gpu-firmware-amd-kmod-hainan: 20230625.1401000_2
gpu-firmware-amd-kmod-hawaii: 20230625.1401000_2
gpu-firmware-amd-kmod-kabini: 20230625.1401000_2
gpu-firmware-amd-kmod-kaveri: 20230625.1401000_2
gpu-firmware-amd-kmod-mullins: 20230625.1401000_2
gpu-firmware-amd-kmod-navi10: 20230625.1401000_2
gpu-firmware-amd-kmod-navi12: 20230625.1401000_2
gpu-firmware-amd-kmod-navi14: 20230625.1401000_2
gpu-firmware-amd-kmod-navy-flounder: 20230625.1401000_2
gpu-firmware-amd-kmod-oland: 20230625.1401000_2
gpu-firmware-amd-kmod-picasso: 20230625.1401000_2
gpu-firmware-amd-kmod-pitcairn: 20230625.1401000_2
gpu-firmware-amd-kmod-polaris10: 20230625.1401000_2
gpu-firmware-amd-kmod-polaris11: 20230625.1401000_2
gpu-firmware-amd-kmod-polaris12: 20230625.1401000_2
gpu-firmware-amd-kmod-psp-13-0-0: 20230625.1401000_2
gpu-firmware-amd-kmod-psp-13-0-10: 20230625.1401000_2
gpu-firmware-amd-kmod-psp-13-0-11: 20230625.1401000_2
gpu-firmware-amd-kmod-psp-13-0-4: 20230625.1401000_2
gpu-firmware-amd-kmod-psp-13-0-5: 20230625.1401000_2
gpu-firmware-amd-kmod-psp-13-0-7: 20230625.1401000_2
gpu-firmware-amd-kmod-psp-13-0-8: 20230625.1401000_2
gpu-firmware-amd-kmod-raven: 20230625.1401000_2
gpu-firmware-amd-kmod-raven2: 20230625.1401000_2
gpu-firmware-amd-kmod-renoir: 20230625.1401000_2
gpu-firmware-amd-kmod-sdma-5-2-6: 20230625.1401000_2
gpu-firmware-amd-kmod-sdma-5-2-7: 20230625.1401000_2
gpu-firmware-amd-kmod-sdma-6-0-0: 20230625.1401000_2
gpu-firmware-amd-kmod-sdma-6-0-1: 20230625.1401000_2
gpu-firmware-amd-kmod-sdma-6-0-2: 20230625.1401000_2
gpu-firmware-amd-kmod-sdma-6-0-3: 20230625.1401000_2
gpu-firmware-amd-kmod-si58: 20230625.1401000_2
gpu-firmware-amd-kmod-sienna-cichlid: 20230625.1401000_2
gpu-firmware-amd-kmod-smu-13-0-0: 20230625.1401000_2
gpu-firmware-amd-kmod-smu-13-0-10: 20230625.1401000_2
gpu-firmware-amd-kmod-smu-13-0-7: 20230625.1401000_2
gpu-firmware-amd-kmod-stoney: 20230625.1401000_2
gpu-firmware-amd-kmod-tahiti: 20230625.1401000_2
gpu-firmware-amd-kmod-tonga: 20230625.1401000_2
gpu-firmware-amd-kmod-topaz: 20230625.1401000_2
gpu-firmware-amd-kmod-vangogh: 20230625.1401000_2
gpu-firmware-amd-kmod-vcn-3-1-2: 20230625.1401000_2
gpu-firmware-amd-kmod-vcn-4-0-0: 20230625.1401000_2
gpu-firmware-amd-kmod-vcn-4-0-2: 20230625.1401000_2
gpu-firmware-amd-kmod-vcn-4-0-4: 20230625.1401000_2
gpu-firmware-amd-kmod-vega10: 20230625.1401000_2
gpu-firmware-amd-kmod-vega12: 20230625.1401000_2
gpu-firmware-amd-kmod-vega20: 20230625.1401000_2
gpu-firmware-amd-kmod-vegam: 20230625.1401000_2
gpu-firmware-amd-kmod-verde: 20230625.1401000_2
gpu-firmware-amd-kmod-yellow-carp: 20230625.1401000_2
gpu-firmware-intel-kmod-alderlake: 20230625.1401000
gpu-firmware-intel-kmod-broxton: 20230625.1401000
gpu-firmware-intel-kmod-cannonlake: 20230625.1401000
gpu-firmware-intel-kmod-dg1: 20230625.1401000
gpu-firmware-intel-kmod-dg2: 20230625.1401000
gpu-firmware-intel-kmod-elkhartlake: 20230625.1401000
gpu-firmware-intel-kmod-geminilake: 20230625.1401000
gpu-firmware-intel-kmod-icelake: 20230625.1401000
gpu-firmware-intel-kmod-kabylake: 20230625.1401000
gpu-firmware-intel-kmod-rocketlake: 20230625.1401000
gpu-firmware-intel-kmod-skylake: 20230625.1401000
gpu-firmware-intel-kmod-tigerlake: 20230625.1401000
gpu-firmware-kmod: 20241114,1
gpu-firmware-radeon-kmod-aruba: 20220511.1401000
gpu-firmware-radeon-kmod-barts: 20220511.1401000
gpu-firmware-radeon-kmod-bonaire: 20220511.1401000
gpu-firmware-radeon-kmod-btc: 20220511.1401000
gpu-firmware-radeon-kmod-caicos: 20220511.1401000
gpu-firmware-radeon-kmod-cayman: 20220511.1401000
gpu-firmware-radeon-kmod-cedar: 20220511.1401000
gpu-firmware-radeon-kmod-cypress: 20220511.1401000
gpu-firmware-radeon-kmod-hainan: 20220511.1401000
gpu-firmware-radeon-kmod-hawaii: 20220511.1401000
gpu-firmware-radeon-kmod-juniper: 20220511.1401000
gpu-firmware-radeon-kmod-kabini: 20220511.1401000
gpu-firmware-radeon-kmod-kaveri: 20220511.1401000
gpu-firmware-radeon-kmod-mullins: 20220511.1401000
gpu-firmware-radeon-kmod-oland: 20220511.1401000
gpu-firmware-radeon-kmod-palm: 20220511.1401000
gpu-firmware-radeon-kmod-pitcairn: 20220511.1401000
gpu-firmware-radeon-kmod-r100: 20220511.1401000
gpu-firmware-radeon-kmod-r200: 20220511.1401000
gpu-firmware-radeon-kmod-r300: 20220511.1401000
gpu-firmware-radeon-kmod-r420: 20220511.1401000
gpu-firmware-radeon-kmod-r520: 20220511.1401000
gpu-firmware-radeon-kmod-r600: 20220511.1401000
gpu-firmware-radeon-kmod-r700: 20220511.1401000
gpu-firmware-radeon-kmod-redwood: 20220511.1401000
gpu-firmware-radeon-kmod-rs600: 20220511.1401000
gpu-firmware-radeon-kmod-rs690: 20220511.1401000
gpu-firmware-radeon-kmod-rs780: 20220511.1401000
gpu-firmware-radeon-kmod-rv610: 20220511.1401000
gpu-firmware-radeon-kmod-rv620: 20220511.1401000
gpu-firmware-radeon-kmod-rv630: 20220511.1401000
gpu-firmware-radeon-kmod-rv635: 20220511.1401000
gpu-firmware-radeon-kmod-rv670: 20220511.1401000
gpu-firmware-radeon-kmod-rv710: 20220511.1401000
gpu-firmware-radeon-kmod-rv730: 20220511.1401000
gpu-firmware-radeon-kmod-rv740: 20220511.1401000
gpu-firmware-radeon-kmod-rv770: 20220511.1401000
gpu-firmware-radeon-kmod-sumo: 20220511.1401000
gpu-firmware-radeon-kmod-sumo2: 20220511.1401000
gpu-firmware-radeon-kmod-tahiti: 20220511.1401000
gpu-firmware-radeon-kmod-turks: 20220511.1401000
gpu-firmware-radeon-kmod-verde: 20220511.1401000
Number of packages to be installed: 130
The process will require 123 MiB more space.
31 MiB to be downloaded.
Proceed with this action? [y/N]: y
Message from drm-61-kmod-6.1.92.1401000_3:
The drm-61-kmod port can be enabled for amdgpu (for AMD
GPUs starting with the HD7000 series / Tahiti) or i915kms (for Intel
APUs starting with HD3000 / Sandy Bridge) through kld_list in
/etc/rc.conf. radeonkms for older AMD GPUs can be loaded and there are
some positive reports if EFI boot is NOT enabled.
For amdgpu: kld_list="amdgpu"
For Intel: kld_list="i915kms"
For radeonkms: kld_list="radeonkms"
Please ensure that all users requiring graphics are members of the
"video" group.
Please note that this package was built for FreeBSD 14.1.
If this is not your current running version, please rebuild
it from ports to prevent panics when loading the module.
# pkg version | grep kmod
drm-61-kmod-6.1.92.1401000_3 =
drm-kmod-20220907_3 =
gpu-firmware-amd-kmod-aldebaran-20230625.1401000_2 =
gpu-firmware-amd-kmod-arcturus-20230625.1401000_2 =
gpu-firmware-amd-kmod-banks-20230625.1401000_2 =
gpu-firmware-amd-kmod-beige-goby-20230625.1401000_2 =
gpu-firmware-amd-kmod-bonaire-20230625.1401000_2 =
gpu-firmware-amd-kmod-carrizo-20230625.1401000_2 =
gpu-firmware-amd-kmod-cyan-skillfish2-20230625.1401000_2 =
gpu-firmware-amd-kmod-dcn-3-1-4-20230625.1401000_2 =
gpu-firmware-amd-kmod-dcn-3-1-5-20230625.1401000_2 =
gpu-firmware-amd-kmod-dcn-3-1-6-20230625.1401000_2 =
gpu-firmware-amd-kmod-dcn-3-2-0-20230625.1401000_2 =
gpu-firmware-amd-kmod-dcn-3-2-1-20230625.1401000_2 =
gpu-firmware-amd-kmod-dimgrey-cavefish-20230625.1401000_2 =
gpu-firmware-amd-kmod-fiji-20230625.1401000_2 =
gpu-firmware-amd-kmod-gc-10-3-6-20230625.1401000_2 =
gpu-firmware-amd-kmod-gc-10-3-7-20230625.1401000_2 =
gpu-firmware-amd-kmod-gc-11-0-0-20230625.1401000_2 =
gpu-firmware-amd-kmod-gc-11-0-1-20230625.1401000_2 =
gpu-firmware-amd-kmod-gc-11-0-2-20230625.1401000_2 =
gpu-firmware-amd-kmod-gc-11-0-3-20230625.1401000_2 =
gpu-firmware-amd-kmod-gc-11-0-4-20230625.1401000_2 =
gpu-firmware-amd-kmod-green-sardine-20230625.1401000_2 =
gpu-firmware-amd-kmod-hainan-20230625.1401000_2 =
gpu-firmware-amd-kmod-hawaii-20230625.1401000_2 =
gpu-firmware-amd-kmod-kabini-20230625.1401000_2 =
gpu-firmware-amd-kmod-kaveri-20230625.1401000_2 =
gpu-firmware-amd-kmod-mullins-20230625.1401000_2 =
gpu-firmware-amd-kmod-navi10-20230625.1401000_2 =
gpu-firmware-amd-kmod-navi12-20230625.1401000_2 =
gpu-firmware-amd-kmod-navi14-20230625.1401000_2 =
gpu-firmware-amd-kmod-navy-flounder-20230625.1401000_2 =
gpu-firmware-amd-kmod-oland-20230625.1401000_2 =
gpu-firmware-amd-kmod-picasso-20230625.1401000_2 =
gpu-firmware-amd-kmod-pitcairn-20230625.1401000_2 =
gpu-firmware-amd-kmod-polaris10-20230625.1401000_2 =
gpu-firmware-amd-kmod-polaris11-20230625.1401000_2 =
gpu-firmware-amd-kmod-polaris12-20230625.1401000_2 =
gpu-firmware-amd-kmod-psp-13-0-0-20230625.1401000_2 =
gpu-firmware-amd-kmod-psp-13-0-10-20230625.1401000_2 =
gpu-firmware-amd-kmod-psp-13-0-11-20230625.1401000_2 =
gpu-firmware-amd-kmod-psp-13-0-4-20230625.1401000_2 =
gpu-firmware-amd-kmod-psp-13-0-5-20230625.1401000_2 =
gpu-firmware-amd-kmod-psp-13-0-7-20230625.1401000_2 =
gpu-firmware-amd-kmod-psp-13-0-8-20230625.1401000_2 =
gpu-firmware-amd-kmod-raven-20230625.1401000_2 =
gpu-firmware-amd-kmod-raven2-20230625.1401000_2 =
gpu-firmware-amd-kmod-renoir-20230625.1401000_2 =
gpu-firmware-amd-kmod-sdma-5-2-6-20230625.1401000_2 =
gpu-firmware-amd-kmod-sdma-5-2-7-20230625.1401000_2 =
gpu-firmware-amd-kmod-sdma-6-0-0-20230625.1401000_2 =
gpu-firmware-amd-kmod-sdma-6-0-1-20230625.1401000_2 =
gpu-firmware-amd-kmod-sdma-6-0-2-20230625.1401000_2 =
gpu-firmware-amd-kmod-sdma-6-0-3-20230625.1401000_2 =
gpu-firmware-amd-kmod-si58-20230625.1401000_2 =
gpu-firmware-amd-kmod-sienna-cichlid-20230625.1401000_2 =
gpu-firmware-amd-kmod-smu-13-0-0-20230625.1401000_2 =
gpu-firmware-amd-kmod-smu-13-0-10-20230625.1401000_2 =
gpu-firmware-amd-kmod-smu-13-0-7-20230625.1401000_2 =
gpu-firmware-amd-kmod-stoney-20230625.1401000_2 =
gpu-firmware-amd-kmod-tahiti-20230625.1401000_2 =
gpu-firmware-amd-kmod-tonga-20230625.1401000_2 =
gpu-firmware-amd-kmod-topaz-20230625.1401000_2 =
gpu-firmware-amd-kmod-vangogh-20230625.1401000_2 =
gpu-firmware-amd-kmod-vcn-3-1-2-20230625.1401000_2 =
gpu-firmware-amd-kmod-vcn-4-0-0-20230625.1401000_2 =
gpu-firmware-amd-kmod-vcn-4-0-2-20230625.1401000_2 =
gpu-firmware-amd-kmod-vcn-4-0-4-20230625.1401000_2 =
gpu-firmware-amd-kmod-vega10-20230625.1401000_2 =
gpu-firmware-amd-kmod-vega12-20230625.1401000_2 =
gpu-firmware-amd-kmod-vega20-20230625.1401000_2 =
gpu-firmware-amd-kmod-vegam-20230625.1401000_2 =
gpu-firmware-amd-kmod-verde-20230625.1401000_2 =
gpu-firmware-amd-kmod-yellow-carp-20230625.1401000_2 =
gpu-firmware-intel-kmod-alderlake-20230625.1401000 =
gpu-firmware-intel-kmod-broxton-20230625.1401000 =
gpu-firmware-intel-kmod-cannonlake-20230625.1401000 =
gpu-firmware-intel-kmod-dg1-20230625.1401000 =
gpu-firmware-intel-kmod-dg2-20230625.1401000 =
gpu-firmware-intel-kmod-elkhartlake-20230625.1401000 =
gpu-firmware-intel-kmod-geminilake-20230625.1401000 =
gpu-firmware-intel-kmod-icelake-20230625.1401000 =
gpu-firmware-intel-kmod-kabylake-20230625.1401000 =
gpu-firmware-intel-kmod-rocketlake-20230625.1401000 =
gpu-firmware-intel-kmod-skylake-20230625.1401000 =
gpu-firmware-intel-kmod-tigerlake-20230625.1401000 =
gpu-firmware-kmod-20241114,1 =
gpu-firmware-radeon-kmod-aruba-20220511.1401000 =
gpu-firmware-radeon-kmod-barts-20220511.1401000 =
gpu-firmware-radeon-kmod-bonaire-20220511.1401000 =
gpu-firmware-radeon-kmod-btc-20220511.1401000 =
gpu-firmware-radeon-kmod-caicos-20220511.1401000 =
gpu-firmware-radeon-kmod-cayman-20220511.1401000 =
gpu-firmware-radeon-kmod-cedar-20220511.1401000 =
gpu-firmware-radeon-kmod-cypress-20220511.1401000 =
gpu-firmware-radeon-kmod-hainan-20220511.1401000 =
gpu-firmware-radeon-kmod-hawaii-20220511.1401000 =
gpu-firmware-radeon-kmod-juniper-20220511.1401000 =
gpu-firmware-radeon-kmod-kabini-20220511.1401000 =
gpu-firmware-radeon-kmod-kaveri-20220511.1401000 =
gpu-firmware-radeon-kmod-mullins-20220511.1401000 =
gpu-firmware-radeon-kmod-oland-20220511.1401000 =
gpu-firmware-radeon-kmod-palm-20220511.1401000 =
gpu-firmware-radeon-kmod-pitcairn-20220511.1401000 =
gpu-firmware-radeon-kmod-r100-20220511.1401000 =
gpu-firmware-radeon-kmod-r200-20220511.1401000 =
gpu-firmware-radeon-kmod-r300-20220511.1401000 =
gpu-firmware-radeon-kmod-r420-20220511.1401000 =
gpu-firmware-radeon-kmod-r520-20220511.1401000 =
gpu-firmware-radeon-kmod-r600-20220511.1401000 =
gpu-firmware-radeon-kmod-r700-20220511.1401000 =
gpu-firmware-radeon-kmod-redwood-20220511.1401000 =
gpu-firmware-radeon-kmod-rs600-20220511.1401000 =
gpu-firmware-radeon-kmod-rs690-20220511.1401000 =
gpu-firmware-radeon-kmod-rs780-20220511.1401000 =
gpu-firmware-radeon-kmod-rv610-20220511.1401000 =
gpu-firmware-radeon-kmod-rv620-20220511.1401000 =
gpu-firmware-radeon-kmod-rv630-20220511.1401000 =
gpu-firmware-radeon-kmod-rv635-20220511.1401000 =
gpu-firmware-radeon-kmod-rv670-20220511.1401000 =
gpu-firmware-radeon-kmod-rv710-20220511.1401000 =
gpu-firmware-radeon-kmod-rv730-20220511.1401000 =
gpu-firmware-radeon-kmod-rv740-20220511.1401000 =
gpu-firmware-radeon-kmod-rv770-20220511.1401000 =
gpu-firmware-radeon-kmod-sumo-20220511.1401000 =
gpu-firmware-radeon-kmod-sumo2-20220511.1401000 =
gpu-firmware-radeon-kmod-tahiti-20220511.1401000 =
gpu-firmware-radeon-kmod-turks-20220511.1401000 =
gpu-firmware-radeon-kmod-verde-20220511.1401000 =
# sysrc kld_list="i915kms"
# pw groupmod video -m root
# pw groupmod video -m foo

The system message buffer does mention a problem with binary firmware, that could not be loaded.

# dmesg
[drm] Got Intel graphics stolen memory base 0x7c800000, size 0x4000000
drmn0: <drmn> on vgapci0
vgapci0: child drmn0 requested pci_enable_io
vgapci0: child drmn0 requested pci_enable_io
adls_dmc_ver2_01.bin: could not load binary firmware /boot/firmware/adls_dmc_ver2_01.bin either
i915/adls_dmc_ver2_01.bin: could not load binary firmware /boot/firmware/i915/adls_dmc_ver2_01.bin either
i915_adls_dmc_ver2_01.bin: could not load binary firmware /boot/firmware/i915_adls_dmc_ver2_01.bin either
lkpi_iic0: <LinuxKPI I2C> on drmn0
iicbus0: <Philips I2C bus> on lkpi_iic0
iic0: <I2C generic I/O> on iicbus0
lkpi_iic1: <LinuxKPI I2C> on drmn0
iicbus1: <Philips I2C bus> on lkpi_iic1
iic1: <I2C generic I/O> on iicbus1
lkpi_iic2: <LinuxKPI I2C> on drmn0
iicbus2: <Philips I2C bus> on lkpi_iic2
iic2: <I2C generic I/O> on iicbus2
lkpi_iic3: <LinuxKPI I2C> on drmn0
iicbus3: <Philips I2C bus> on lkpi_iic3
iic3: <I2C generic I/O> on iicbus3
lkpi_iic4: <LinuxKPI I2C> on drmn0
iicbus4: <Philips I2C bus> on lkpi_iic4
iic4: <I2C generic I/O> on iicbus4
lkpi_iic5: <LinuxKPI I2C> on drmn0
iicbus5: <Philips I2C bus> on lkpi_iic5
iic5: <I2C generic I/O> on iicbus5
lkpi_iic6: <LinuxKPI I2C> on drmn0
iicbus6: <Philips I2C bus> on lkpi_iic6
iic6: <I2C generic I/O> on iicbus6
lkpi_iic7: <LinuxKPI I2C> on drmn0
iicbus7: <Philips I2C bus> on lkpi_iic7
iic7: <I2C generic I/O> on iicbus7
lkpi_iic8: <LinuxKPI I2C> on drmn0
iicbus8: <Philips I2C bus> on lkpi_iic8
iic8: <I2C generic I/O> on iicbus8
drmn0: successfully loaded firmware image 'i915/adls_dmc_ver2_01.bin'
drmn0: [drm] Finished loading DMC firmware i915/adls_dmc_ver2_01.bin (v2.1)
sysctl_warn_reuse: can't re-use a leaf (hw.dri.debug)!
lkpi_iic9: <LinuxKPI I2C> on drm1
iicbus9: <Philips I2C bus> on lkpi_iic9
iic9: <I2C generic I/O> on iicbus9
[drm] Initialized i915 1.6.0 20201103 for drmn0 on minor 0
VT: Driver priority 0 too low. Current 101
fbd0: not attached to vt(4) console; another device has precedence (err=17)
# find / -type f -name '*adls*'
/boot/modules/i915_adls_dmc_ver2_01_bin.ko
# kldstat | grep adls
# kldload i915_adls_dmc_ver2_01_bin.ko
# kldstat | grep adls
17 1 0xffffffff835aa000 69f0 i915_adls_dmc_ver2_01_bin.ko

The system messages log confirms, that the firmware drivers were loaded.

# cat /var/log/messages | grep drm
Dec 25 23:08:54 wopr pkg[1974]: drm-61-kmod-6.1.92.1401000_3 installed
Dec 25 23:08:54 wopr pkg[1974]: drm-kmod-20220907_3 installed
Dec 25 23:12:11 wopr pkg[2119]: libdrm-2.4.123,1 installed
Dec 26 00:37:18 wopr kernel: [drm] Got Intel graphics stolen memory base 0x7c800000, size 0x4000000
Dec 26 00:37:18 wopr kernel: drmn0: <drmn> on vgapci0
Dec 26 00:37:18 wopr kernel: vgapci0: child drmn0 requested pci_enable_io
Dec 26 00:37:18 wopr kernel: lkpi_iic0: <LinuxKPI I2C> on drmn0
Dec 26 00:37:18 wopr kernel: lkpi_iic1: <LinuxKPI I2C> on drmn0
Dec 26 00:37:18 wopr kernel: lkpi_iic2: <LinuxKPI I2C> on drmn0
Dec 26 00:37:18 wopr kernel: lkpi_iic3: <LinuxKPI I2C> on drmn0
Dec 26 00:37:18 wopr kernel: lkpi_iic4: <LinuxKPI I2C> on drmn0
Dec 26 00:37:18 wopr kernel: lkpi_iic5: <LinuxKPI I2C> on drmn0
Dec 26 00:37:18 wopr kernel: lkpi_iic6: <LinuxKPI I2C> on drmn0
Dec 26 00:37:18 wopr kernel: lkpi_iic7: <LinuxKPI I2C> on drmn0
Dec 26 00:37:18 wopr kernel: lkpi_iic8: <LinuxKPI I2C> on drmn0
Dec 26 00:37:18 wopr kernel: drmn0: successfully loaded firmware image 'i915/adls_dmc_ver2_01.bin'
Dec 26 00:37:18 wopr kernel: drmn0: [drm] Finished loading DMC firmware i915/adls_dmc_ver2_01.bin (v2.1)
Dec 26 00:37:18 wopr kernel: lkpi_iic9: <LinuxKPI I2C> on drm1
Dec 26 00:37:18 wopr kernel: [drm] Initialized i915 1.6.0 20201103 for drmn0 on minor 0

I tested loading DMC driver manually.

# sysrc kld_list+="i915_adls_dmc_ver2_01_bin"
kld_list: i915kms -> i915kms i915_adls_dmc_ver2_01_bin
# echo 'drm_load="YES"' >> /boot/loader.conf
# echo 'i915kms_load="YES"' >> /boot/loader.conf
# cat /boot/loader.conf | sort
aesni_load="YES"
cryptodev_load="YES"
drm_load="YES"
geom_eli_load="YES"
i915kms_load="YES"
if_re_load="YES"
if_re_name="/boot/modules/if_re.ko"
kern.geom.label.disk_ident.enable="0"
kern.geom.label.gptid.enable="0"
vboxdrv_load="YES"
zfs_load="YES"

Any tips?

Realtek RTL8125 network interface driver for FreeBSD

Realtek RTL8125 is a network interface card, that is integrated on modern mid to high range motherboards, such as Asus Prime with 12th and 13th generation Intel CPUs. RTL8125 is not supported by the default Realtek driver, that is loaded by the FreeBSD base system during boot. This presents several issues on a host with no Internet access. With a tip from a brilliant user on Discord, I was able to discover this work-around.

RTL8125 is supported by a non-default Realtek driver, that was written by Realtek for FreeBSD, but not adopted into the main driver, because of differences in coding standards. The driver is in ports as “realtek-re-kmod”. If the host does not have a connection to Internet, then this port has to be fetched on another host and then transferred via USB storage or alternative medium.

# fetch http://pkg.freebsd.org/FreeBSD:14:amd64/release_2/All/realtek-re-kmod-1100.00_1.pkg

The package can not be installed, because the host would still need Internet for that, because of package management. The driver can be extracted and manually placed in the directory for boot kernel modules.

# tar zxf realtek-re-kmod1100.00_1.pkg /boot/modules/if_re.ko
# mv boot/modules/if_re.ko /boot/modules/

The driver needs to load during boot, while it is key to succes, that the “name” paramenter is set to the new driver. If this is omitted, then it will not work, because the old driver is loaded from “/boot/kernel”.

# vi /boot/loader.conf
if_re_load="YES"
if_re_name="/boot/modules/if_re.ko"

This will initialize the Realtek network interface card and DHCP for the network interface can be configured.

# vi /etc/rc.conf
ifconfig_re0="DHCP"

Reboot.

# reboot

The complete procedure:

# dmesg
# gpart show
# gpart destroy -F /dev/da0
# gpart create -s mbr /dev/da0
# gpart add -t fat32 /dev/da0
# gpart show
# newfs_msdos -L REALTEK -F 32 /dev/da0s1
# mount -t msdos /dev/da0s1 /mnt
# cd /mnt
# fetch http://pkg.freebsd.org/FreeBSD:14:amd64/release_2/All/realtek-re-kmod-1100.00_1.pkg
# sync
# cd
# umount /mnt
# mount -t msdos /dev/da0s1 /mnt
# cp /mnt/*.pkg .
# umount /mnt
# tar zxf realtek-re-kmod1100.00_1.pkg /boot/modules/if_re.ko
# mv boot/modules/if_re.ko /boot/modules/
# vi /boot/loader.conf
if_re_load="YES"
if_re_name="/boot/modules/if_re.ko"
# vi /etc/rc.conf
ifconfig_re0="DHCP"
# reboot

References.

Reject Failed SPF with spfmilter for Sendmail on FreeBSD

What is spfmilter?

spfmilter is a Sender Policy Framework (SPF) mail filter module for Sendmail on FreeBSD. spfmilter makes Sendmail reject mail, that has been sent from a mail server, that is not allowed by the policy for the domain. spfmilter s the other half of the framework.

Example of an CEO scam email, that is being contructed with false sender header fields in an Alpine mail client. DKIM authentication and SPF authorization prevents this kind of domain abuse. This can be implemented with an SPF TXT record for BIND DNS and OpenDKIM milter for Sendmail with a DKIM TXT record for BIND DNS on FreeBSD
Example of an CEO scam email, that is being contructed with false sender header fields in an Alpine mail client. Sender Policy Framework (SPF) prevents this kind of domain abuse.

Install spfmilter for Sendmail on FreeBSD

Search for spfmilter in the package repository.

# pkg search spf
spfmilter-2.001_2 SPF milter for sendmail

Install the spfmilter.

# pkg install spfmilter
The following 2 package(s) will be affected (of 0 checked):
New packages to be INSTALLED:
libspf2: 1.2.11_2
spfmilter: 2.001_2
144 KiB to be downloaded.

Configure spfmilter.

# nano /etc/mail/

Configure FreeBSD to run spfmilter as a service

Configure spfmilter as a service in FreeBSD. This is documented on the official website of spfmilter.

# service -l | grep spf
spfmilter
# nano /etc/rc.conf
spfmilter_enable="YES"
spfmilter_socket="unix:/var/run/spfmilter.sock"
# service spfmilter start
Starting spfmilter.
# grep spfmilter /var/log/maillog
Nov 17 13:37:55 foo spfmilter[34074]: spfmilter 2.001 with libspf2-1.2.11 starting

Configure Sendmail to use spfmilter

Configure Sendmail to use spfmilter. This is documented on the official website of spfmilter.

# cd /etc/mail
# cp `hostname`.mc `date -I`-`hostname`.mc
# nano `hostname`.mc
INPUT_MAIL_FILTER(`spfmilter',`S=unix:/var/run/spfmilter.sock')
# make
/usr/bin/m4 -D_CF_DIR_=/usr/share/sendmail/cf/ /usr/share/sendmail/cf/m4/cf.m4 foo.micski.dk.mc > foo.micski.dk.cf
# make install
install -m 444 foo.micski.dk.cf /etc/mail/sendmail.cf
install -m 444 foo.micski.dk.submit.cf /etc/mail/submit.cf
# service sendmail restart
Stopping sendmail.
Starting sendmail.
# tail /var/log/maillog | grep sm-m
Nov 17 13:37:08 foobar sm-mta[37327]: starting daemon (8.18.1): SMTP+queueing@00:30:00
Nov 17 13:37:08 foobar sm-msp-queue[37341]: starting daemon (8.18.1): queueing@00:30:00

Test spfmilter

If regular email works after the installation, it is time to test the spfmilter.

In this example, I will try to forge an email to appear being from Microsoft, that has an SPF. It gets rejected by the spfmilter.

$ telnet foo.micski.dk 587
220 foo.micski.dk ESMTP Sendmail 8.18.1/8.18.1; Sun, 17 Nov 2024 13:37:51 +0100 (CET)
HELO microsoft.com
250 foo.micski.dk Hello [13.37.13.37], pleased to meet you
MAIL From:<>
550 5.7.1 <>... fail

The rejection by spfmilter can be confirmed by the mail log.

# tail /var/log/maillog
Nov 17 13:37:56 foo sm-mta[38143]: 4AGNbZ2E038143: milter=spfmilter, action=helo, continue
Nov 17 13:38:28 foo sm-mta[38143]: 4AGNbZ2E038143: Milter: sender:
Nov 17 13:38:28 foo spfmilter[34074]: rejecting mail from [13.37.13.37] - fail
Nov 17 13:38:28 foo sm-mta[38143]: 4AGNbZ2E038143: milter=spfmilter, action=mail, reject=550 5.7.1 fail
Nov 17 13:38:28 foo sm-mta[38143]: 4AGNbZ2E038143: Milter: from=, reject=550 5.7.1 fail

In generel, future rejects can be listed from the mail log.

# grep spfmilter /var/log/maillog | grep rejecting

References

Attribution and sharing.

Feel free to link to this guide, if you find it useful. Contributions and feedback is always appreciated.

Virtual Private Network (VPN) Server with OpenVPN and Easy-RSA on FreeBSD

In this example, I will create a virtual private network (VPN) server with OpenVPN and Easy-RSA on a FreeBSD VPS. OpenVPN clients, that connect to this VPN server, will then be able to connect to other OpenVPN clients, that are also connected to this VPN server. This is true, even though, the host of each client might not be able to do so otherwise due to carrier-grade network address translation (CG-NAT) or other network limitations. A client, can be any kind of host, such as a server, network device, personal computer workstation or smartphone mobile device. I will be using OpenVPN 2.6.12 and Easy-RSA 3.2.1 on FreeBSD 13.4. I will use the name “foobar” for the VPN server and “foobar-client” for OpenVPN clients.

       +------------------------------+
| VPN Server (Germany) |
|------------------------------|
| Internet IP: 123.123.123.123 |
| VPN IP: 10.8.0.1 |
+------------------------------+
|
|
| VPN Subnet: 10.8.0.0/24
----------------------------------------------
| |
| // Internet // |
DSL FW | | 5G CGNAT
+-----------------------+ +-----------------------+
| Client 1 (Copenhagen) | | Client 2 (Sweden) |
|-----------------------| |-----------------------|
| Local IP: 172.213.1.9 | | Local IP: 192.168.1.5 |
| VPN IP: 10.8.0.11 | | VPN IP: 10.8.0.12 |
+-----------------------+ +-----------------------+

Install OpenVPN and Easy-RSA on the VPN server.

OpenVPN is an open source virtual private network (VPN) solution, that provides secure connections and virtual networks on Internet. It uses SSL and TLS protocols for encryption, which ensures secure data transmission between clients and servers on the virtual network. OpenVPN supports a range of configurations, such as point-to-point and site-to-site setups. It can transmit over UDP or TCP through custom ports, which makes it flexible and capable of bypassing firewalls, network translations and other network limitations.

Easy-RSA is a command-line utility, that simplifies the process of creating and managing a public key infrastructure (PKI) for VPNs and other services, that rely on SSL and TLS certificates. It automates certificate generation, sets up a certificate authority (CA) and issues server and client certificates and does key management.

# pkg install -y openvpn easy-rsa

Create a CA and certificates for the VPN server with Easy-RSA.

Create the PKI with Easy-RSA. The PKI will consist of the CA, digital certificates, keys and revocation lists. The passphrase, that will be set for the CA, will be required, when creating certificates for clients.

# easyrsa init-pki
# easyrsa build-ca
# easyrsa build-server-full foobar nopass
# easyrsa gen-dh

Create a TLS-Auth key with OpenVPN.

A TLS-Auth key is a static, pre-shared key, that is used by OpenVPN to add an extra layer of security to the TLS handshake process. It authenticates the initial packets of the connection, which prevent unauthorized and malicious connections, such as denial-of-service attacks, from reaching the OpenVPN server.

# openvpn --genkey secret pki/ta.key

Copy certificates and TLS-Auth key to OpenVPN directory.

# mkdir /usr/local/etc/openvpn
# cp -pR pki/{ca.crt,dh.pem,issued,private,ta.key} /usr/local/etc/openvpn/
# chown -R openvpn:openvpn /usr/local/etc/openvpn
# chmod 600 /usr/local/etc/openvpn/*

Configure OpenVPN.

Configure basic settings, cryptographic settings and VPN network with client-to-client, IP address persistance and secure authentication.

# nano /usr/local/etc/openvpn/openvpn.conf
port 1194
proto udp
dev tun
ca ca.crt
cert issued/foobar.crt
key private/foobar.key
dh dh.pem
topology subnet
server 10.8.0.0 255.255.255.0
ifconfig-pool-persist ipp.txt
push "route 10.8.0.0 255.255.255.0"
client-config-dir ccd
client-to-client
keepalive 10 120
tls-auth ta.key 0
data-ciphers AES-256-GCM:AES-256-CBC:AES-128-GCM:CHACHA20-POLY1305
cipher AES-256-CBC
auth SHA256
max-clients 5
user openvpn
group openvpn
persist-key
persist-tun
status openvpn-status.log
log openvpn.log
verb 3
mute 20
explicit-exit-notify 1

If you prefer, you can also begin with a copy of the sample configuration, that has comments.

# cp /usr/local/share/examples/openvpn/sample-config-files/server.conf /usr/local/etc/openvpn/openvpn.conf

Configure client configuration directory (CCD).

OpenVPN identifies each client by its common name (CN) from the client certificate. OpenVPN looks for a file with the same name in the client configuration directory (CCD). In this example, 2 OpenVPN client hosts, 1 and 2, will be assigned static IP addresses in the VPN.

# mkdir -m 700 /usr/local/etc/openvpn/ccd
# nano /usr/local/etc/openvpn/ccd/client-1
ifconfig-push 10.8.0.11 255.255.255.0
# nano /usr/local/etc/openvpn/ccd/client-2
ifconfig-push 10.8.0.12 255.255.255.0

Start OpenVPN server.

Configure FreeBSD to start OpenVPN at boot. Start OpenVPN. Confirm, that OpenVPN runs as the less privileged user openvpn. Confirm, that there are errors.

# service openvpn enable
# service openvpn start
# ps aux | grep openvpn
# tail /var/log/messages
# tail /usr/local/etc/openvpn/openvpn.log

Enable routing in FreeBSD.

# sysctl net.inet.ip.forwarding=1
# echo 'net.inet.ip.forwarding=1' >> /etc/sysctl.conf

Configure and start PF.

# ifconfig
# nano /etc/pf.conf
interface = "vtnet0"
network = "10.8.0.0/24"
set skip on lo
scrub in on $interface all fragment reassemble
nat on $interface from $network to any -> ($interface)
pass in on $interface proto udp from any to ($interface) port 1194
# service pflog enable
# service pf enable
# service pf start
# service pflog start

Disable TCP checksum offloading.

If TCP checksum offloading is not disabled, then the network interface card (NIC) may offload the calculation of checksums to the hardware, which can lead to a mismatch in checksums. This can lead to dropped connections and corrupt data.

# ifconfig vtnet0 -rxcsum -txcsum -rxcsum6 -txcsum6 -tso -lro 
# nano /etc/rc.conf
ifconfig_vtnet0="DHCP -rxcsum -txcsum -rxcsum6 -txcsum6 -tso -lro"

Create certificate for OpenVPN FreeBSD client with Easy-RSA.

# easyrsa build-client-full client-1 nopass
Private-Key and Public-Certificate-Request files created.
Your files are:
* req: /root/pki/reqs/client-1.req
* key: /root/pki/private/client-1.key
Certificate created at:
* /root/pki/issued/client-1.crt

Update certificates and keys in the OpenVPN directory.

# cp -pR pki/{issued,private} /usr/local/etc/openvpn/
# chown -R openvpn:openvpn /usr/local/etc/openvpn

Transfer certificates and keys to OpenVPN client.

In this example, I create a zip file, that contain the CA certificate, the TLS-Auth key, the client key and the client certificate. These certificates and keys will be needed for OpenVPN configuration on the OpenVPN client side.

# mkdir foobar-client
# cp pki/ca.crt foobar-client/foobar-ca.crt
# cp pki/ta.key foobar-client/foobar-ta.key
# cp pki/private/client-1.key foobar-client/
# cp pki/issued/client-1.crt foobar-client/
# zip -j foobar-client foobar-client/*

I recommend automizing this with a script, that takes the CN as an argument.

Install OpenVPN on FreeBSD client.

If not already installed, then install OpenVPN on the FreeBSD client.

# pkg install openvpn

Install certificates and keys on OpenVPN FreeBSD client.

Extract the zip file, that contains the certificates and keys, that is needed for the OpenVPN configuration.

# unzip foobar-client.zip -d /usr/local/etc/openvpn
Archive: /root/foobar-client.zip
extracting: /usr/local/etc/openvpn/foobar-ca.crt
extracting: /usr/local/etc/openvpn/foobar-client-1.crt
extracting: /usr/local/etc/openvpn/foobar-client-1.key
extracting: /usr/local/etc/openvpn/foobar-ta.key
# chown openvpn:openvpn /usr/local/etc/openvpn/*

Create configuration file on OpenVPN FreeBSD client.

If you have another VPN running, you can use another device, such as tun1.

# nano /usr/local/etc/openvpn/foobar-client-1.ovpn
client
dev tun0
proto udp
remote 123.123.123.123 1194
resolv-retry infinite
nobind
persist-key
persist-tun
ca foobar-ca.crt
cert foobar-client-1.crt
key foobar-client-1.key
tls-auth foobar-ta.key 1
cipher AES-256-CBC
auth SHA256
remote-cert-tls server
verb 3
mute-replay-warnings

Start OpenVPN on the FreeBSD client.

# nano /etc/rc.conf
openvpn_enable="YES"
openvpn_configfile="/usr/local/etc/openvpn/foobar-client-1.ovpn"
openvpn_dir="/usr/local/etc/openvpn"
# service openvpn start
# tail /var/log/messages

Confirm, that the tun interface is up.

# ifconfig tun0
tun0: flags=8043<UP,BROADCAST,RUNNING,MULTICAST> metric 0 mtu 1500
options=80000<LINKSTATE>
inet 10.8.0.11 netmask 0xffffff00 broadcast 10.8.0.255
groups: tun
nd6 options=29<PERFORMNUD,IFDISABLED,AUTO_LINKLOCAL>
Opened by PID 4885

Confirm, that the VPN server and other connected clients can be reached.

# ping 10.8.0.1
PING 10.8.0.1 (10.8.0.1): 56 data bytes
64 bytes from 10.8.0.1: icmp_seq=0 ttl=64 time=40.965 ms

Check date of expire for certificates.

OpenSSL can be used to check the date of expire for the certificate authority certificate and the client.

# openssl x509 -in pki/ca.crt -noout -enddate
# openssl x509 -in pki/issued/foobar-client-1.crt -noout -enddate
# openssl x509 -in /usr/local/etc/openvpn/foobar-client-1.crt -noout -enddate

Securing SSH.

Now, that the VPN server can be reached via the VPN, SSH can be configured to only listen on the VPN.

# nano /etc/ssh/sshd_config
ListenAddress 10.8.0.1

Running multiple instances of OpenVPN on FreeBSD.

FreeBSD can run multiple instances of OpenVPN. This is done by assigning each one to a unique tun interface. An example of this could be one OpenVPN client for a privacy related VPN, such as Mullvad, and a business related VPN. Make sure, that each OpenVPN configuration will use different tun devices, different networks and different start scripts. The default start script in FreeBSD support multiple instances by linking the script to names, that can be applied in rc.conf.

In this example, I will run this OpenVPN client as an additional client.

# ln -s /usr/local/etc/rc.d/openvpn /usr/local/etc/rc.d/openvpn_foobar
# nano /etc/rc.conf
openvpn_foobar_enable="YES"
openvpn_foobar_configfile="/usr/local/etc/openvpn/foobar-client-1.ovpn"
openvpn_foobar_dir="/usr/local/etc/openvpn"
# service openvpn_foobar start

There will now be multiple tun devices with active networks.

# ifconfig

The routing table on a FreeBSD host with multiple OpenVPN clients.

In the following example, I used the built-in network analysis utility netstat to inspect the routing table on a FreeBSD host with multiple OpenVPN clients. The output is shown without resolving numeric addresses and ports to names. Traffic will be routed through the most direct and specific route. This is also known as the one with the longest prefix match. In the example, a connection to a client on 10.8.0.0/24 will be routed through tun1, and not tun0, even though Internet traffic on 0.0.0.0/1 will be routed through tun0. The same is true for connection to hosts on the LAN, because the 192.168.1.0/24 prefix is longer than the 128.0.0.0/1. Because the VPN subnet is directly reachable through the tun1 interface, there is no need to add an extra hop IP address as a gateway. The flags are used to indicate, if the link is up (U), and, if the link is a VPN gateway or default gateway (G).

# netstat -rn
Destination Gateway Flags Netif
127.0.0.1 link#2 UH lo0
192.168.1.0/24 link#1 U em0
10.8.0.0/24 link#4 U tun1
0.0.0.0/1 10.10.0.1 UGS tun0
128.0.0.0/1 10.10.0.1 UGS tun0
default 192.168.1.1 UGS em0

Make sure that PF script does not depend on VPN interface.

When FreeBSD is rebooted, it will start PF and load the firewall rules before it will start OpenVPN and create the tun interface. This is a problem, if access to network or Internet depends on firewall rules, such as NAT. The current dependency ordering of the start scripts can be listed with rcorder.

# rcorder /etc/rc.d/* /usr/local/etc/rc.d/* | more
/etc/rc.d/netif
/etc/rc.d/pf
/usr/local/etc/rc.d/openvpn

A solution to this is to either change the order or avoid refences to the tun interface in the PF firewall rules script. Instead of the interface, the IP address range can be used.

pass in proto tcp from 10.8.0.0/24 to any port ssh keep state
pass out proto { tcp, udp } to 10.8.0.0/24 keep state

Issue: Static IP address for OpenVPN clients not working.

I noticed, that the assignment of static IP address for the OpenVPN clients did not work. The CCD had been defined. The client certificate CN matched the CCD filename in which the assigned IP address is defined. The syntax for IP address and subnet mask matched the documentation. I found, that the CCD directory had mode 600 and not 700. I fixed it and restarted OpenVPN. It worked.

# grep ccd /usr/local/etc/openvpn/openvpn.conf
client-config-dir ccd
# grep client-1 /usr/local/etc/openvpn/issued/client-1.crt
Subject: CN=client-1
# cat /usr/local/etc/openvpn/ccd/
ifconfig-push 10.8.0.11 255.255.255.0
# ls -ld /usr/local/etc/openvpn/ccd
drw------- 2 openvpn openvpn 6 Nov 12 13:37 /usr/local/etc/openvpn/ccd
# chmod 700 /usr/local/etc/openvpn/ccd
# service openvpn restart

Issue: FreeBSD route add command for OpenVPN failed.

I noticed, that the output from a secondary OpenVPN had an error message about a route add command, that failed. The OpenVPN service worked fine though. I will look into this issue.

# service openvpn_foobar start
Dec 16 13:37:13 foo openvpn_foobar[1756]: ERROR: FreeBSD route add command failed: external program exited with error status: 1

Issue: AEAD decrypt error for OpenVPN.

I noticed, that OpenVPN writes messages about AEAD decrypt errors in the system log, when I initiate traffic on the VPN. I will look into this issue.

Dec 19 13:37:13 foobar openvpn[1337]: AEAD Decrypt error: bad packet ID (may b
e a replay): [ #697 ] -- see the man page entry for --no-replay and --replay-win
dow for more info or silence this warning with --mute-replay-warnings

More abount OpenVPN.

Attribution and sharing.

Feel free to link to this guide, if you find it useful. Contributions and feedback is always appreciated.