OpenBSD workstation hardening

Comment on Mastodon

Introduction

I wanted to share a list of hardening you can do on your OpenBSD workstation, and explaining the threat model of each change.

OpenBSD official project website

Feel free to pick any tweak you find useful for your use-case, many are certainly overkill for most people, but depending on the context, these changes could make sense for others.

User configuration

There are some tweaks that could be done in the configuration of a user to improve the security.

The Least privileges

In order to prevent a program to escalate privileges, remove yourself from the wheel group, and don't set any doas or sudo permission.

If you need root privileges, switch to a TTY using the root user.

Multiple-factor authentication

In some cases, it may be desirable to have a multiple factor authentication, this mean that in order to log in your system, you would need a TOTP generator (phone app typically, or a password manager such as KeePassXC) in addition to your regular password.

This would protect against people nearby who may be able to guess your system password.

I already wrote a guide explaining how to add TOTP to an OpenBSD login.

Blog post: Multi-factor authentication on OpenBSD

Home directory permission

The permissions of the user directory should be 700, so only the owner and root could browse it.

Ideally, you should add `umask 077` to your user environment, so every new directory or file permissions will be restricted to your user only.

Firewall

There are some interesting policies to configure with the help of OpenBSD firewall Packet Filter.

Block inbound

By default, it's good practice to disable all incoming traffic except the responses to established sessions (so servers can reply to your requests). This protects against someone on your local network / VPN to access network services that would be listening on the network interfaces.

In `/etc/pf.conf` you would have to replace the default:

block return
pass

By the following:

block all
pass out inet
# allow ICMP because it's useful
pass in proto icmp

Then, reload with `pfctl -f /etc/pf.conf`, if you ever need to allow a port on the network, add the according rule in the file.

Filter outbound

It may be useful and effective to block outbound traffic, but this only work effectively if you know exactly what you need because you will have to allow hosts and remote ports manually.

It would protect against a program trying to exfiltrate data using a non-allowed port/host.

Disabling network for the desktop user

Disabling network by default is an important mitigation in my opinion. This will protect against any program your run and try to act rogue, if they can't figure there is a proxy, they won't be able to connect to the Internet.

This could also save you from mistaken commands that would pull stuff from the network like pip, npm and co. I think it's always great to have a tight control on which program should do networking and which shouldn't. On Linux this is actually easy to do, but on OpenBSD we can't restrict a single program so a proxy is the only solution.

This can be done by creating a new user named `_proxy` (or whatever the name you prefer) using `useradd -s /sbin/nologin -m _proxy` and adding your SSH key to its authorized_keys file.

Add this rule at the end of your file `/etc/pf.conf` and then reload with `pfctl -f /etc/pf.conf`:

block return out proto {tcp udp} user solene

Now, if you want to allow a program to use the network, you need to:

Some network fixes

Most programs will react to a proxy configured in a variable named `http_proxy` or `https_proxy` or `all_proxy`, however it's not a good idea to globally define these variables for your user as it would be a lot easier to a program to use the proxy automatically, which is against the essence of this proxy.

SSH

By default, you won't be able to ssh to anything except on a local user, we need to proxy every remote ssh connection through the local _proxy user.

In `~/.ssh/config`:

Host localhost
User _proxy
ControlMaster auto
ControlPath ~/.ssh/%h%p%r.sock
ControlPersist 60

Host *.*
ProxyJump localhost

Chromium

If you didn't configure GNOME proxy settings, Chromium / Ungoogled Chromium won't use a proxy, except if you add a command line parameter `--proxy-server=socks5://localhost:10000`.

I tried to manually modified the dconf database where the "GNOME" settings are to configure the proxy, but I didn't get it to work (it used to work for me, but I can't succeed anymore).

Syncthing

If you use syncthing, you need to proxy all its traffic through the SSH tunnel. This is done by setting the environment variable `all_proxy=socks5://localhost:10000` in the program environment.

Live in a temporary file-system

It's possible to have most of your home directory be a temporary file system living in memory, with a few directories with persistency.

This change would prevent anyone from using temporary files or cache left-over from previous session.

The most efficient method to achieve this is to use the program home-impermanence that I wrote for this use case, it handles a list of files/directories that should be persistent.

Blog post: Reproducible clean $HOME on OpenBSD using impermanence

If you only want to start fresh using a template (that doesn't evolve on use), you can check the flag `-P` of `mount_mfs` which allows populating the fresh memory based file system using an existing directory.

OpenBSD man page: mount_mfs(8)

Disable webcam and microphone

Good news! I take the opportunity here to remember OpenBSD disables by default the video and audio recording of the various capable devices, instead, they will appear to work but record empty stream of data.

They can be manually enabled by changing the sysctls `kern.audio.record` or `kern.video.record` to 1 when you need to use them.

Some laptop manufacturer offer the option to have a physical switch to disable microphone and webcam, so you can be confident about their state (Framework). Some other manufacturer also allow to not put any webcam and microphone (NovaCustom, Nitropad). Finally, open source firmwares like Coreboot can offer a bios setting to disable these peripherals, it should be trustable in my opinion.

Disabling USB ports

If you need to protect your system from malicious USB devices (usually in an office environment), you should disable them in the BIOS/Firmware if possible.

If it's not possible, then you could still disable the kernel drivers at boot time using this method.

Create the file `/etc/bsd.re-config` and add the content to it:

disable usb
disable xhci

This will disable the support for USB 3 and 2 controllers. On a desktop computer, you may want to use PS/2 peripherals in these conditions.

System-wide services

Clamav antivirus

While this one may make you smile, if there is a chance it saves you once, I think it's still a valuable addition to any kind of hardening. A downloaded attachment from an email, or rogue JPG file could still harm your system.

OpenBSD ships a fully working clamav service, don't forget to enable freshclam, the viral database updater.

Auto-update

I already covered it in a previous article about anacron, but in my opinion, auto-updating the packages and base system daily on a computer is the minimum that should be done everywhere.

Anacron: useful OpenBSD examples

System configuration

Memory allocation hardening

The OpenBSD malloc system allows you to enable some extra checks, like use after free, heap overflow or guard pages, they can be all enabled at once. This is really efficient for security as most security exploits relies on memory management issues, BUT it may break software that have memory management issues (there are many of them). Using this mode will also impact the performance negatively, as the system needs to do more checks for each piece of allocated memory.

In order to enable it, add this to `/etc/sysctl.conf`:

vm.malloc_conf=S

It can be immediately enabled with `sysctl vm.malloc_conf=S`, and disabled by setting no value `sysctl vm.malloc_conf=""`.

The program `ssh` and `sshd` always run with this flag enabled, even if it's disabled system-wide.

Some ideas to go further

Specialized proxies

It could be possible to have different proxy users, with each restriction to the remote ports allowed, we could imagine proxies like:

Of course, this is even more tedious than the multipurpose proxy, but at least, it's harder for a program to guess what proxy to use, especially if you don't connect them all at once.

Run process using dedicated users

I wrote a bit about this in the past, for command line programs, running them in dedicated local users over SSH make sense, as long as it's still practical.

Dedicated users to run processes

But if you need to run graphical programs, this becomes tricky. Using `ssh -Y` gives the remote program a full access to your display server, which has access to everything else running, not great... You could still rely on `ssh -X` which enables X11 Security extensions, but you have to trust the implementation, and it comes with issues like no shared clipboard, poor performance and programs crashing when attempting to access a legit resource that is blocked by the security protocol...

In my opinion, the best way to achieve isolation for graphical programs would be to run a dedicated VNC server in the local user, and connect from your own user. This should be better than running on your own X locally.

Encrypted home with USB unlocking

In a setup where the computer is used by multiple person, the system encryption may be tedious because everyone have to remember the main passphrase, you have no guarantee one won't write it down on a post-it... In that case, it may be better to have a personal volume, encrypted, for each user.

I don't have an implementation yet, but I got a nice idea. Adding a volume for a user would look like the following:

This way, you only need to have your USB memory stick plugged in when the system is booting, and it should automatically unlock and mount your personal encrypted volume. Note that if you want to switch user, you would have to reboot to unlock their drive if you don't want to mess with the command line.

Conclusion

It's always possible to harden a system more and more, but the balance between real world security and actual usability should always be studied.

No one will use a too-much hardened system if they can't work on it efficiently, on the other hand, users expect their system to protect them against most common threats.

Depending on one's environment and threat model, it's important to configure their system accordingly.