| Title: OpenBSD workstation hardening | |
| Author: Solène | |
| Date: 31 December 2023 | |
| Tags: security openbsd unix | |
| Description: In this blog post, you will learn different methods to | |
| harden your OpenBSD system, and in which situation they are useful | |
| # 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: | |
| * toggle the proxy ON with the command: `ssh -N -D 10000 | |
| _proxy@localhost` which is only possible if your SSH private key is | |
| unlocked | |
| * configure a SOCKS5 proxy in the program | |
| ### 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: | |
| * http / https / ftp | |
| * ssh only | |
| * imap / smtp | |
| * etc.... | |
| 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: | |
| * take a dedicated USB memory stick for this user, this will be used as | |
| a "key" to unlock their data directory | |
| * overwrite the memory stick with random data | |
| * create an empty disk file on the system, it will contain the | |
| encrypted virtual disk, use a random part of the USB disk for the | |
| passphrase (you will have to write down the length + offset) | |
| * write a rc file that looks for the USB disk volume if present, if so, | |
| tries to unlock and mount the partition upon boot | |
| 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. |