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.
       
 (HTM) 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.
       
 (HTM) 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.
       
 (HTM) 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.
       
 (HTM) 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.
       
 (HTM) 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.
       
 (HTM) 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.