Apply systemd sandboxing by default to some services

Members of group input are allowed to write to /dev/input devices. So user kloak could be a member of group /dev/input.

ls -la /dev/input/

crw-rw---- 1 root input
crw-rw---- 1 root input

By being a member of the input group no CAP_DAC_OVERRIDE capability would be required?

1 Like

Kloak doesn’t actually write to /dev/input. It writes to /dev/uinput.

At least, that’s what it says when starting it

* Started kloak : Keystroke-level Online Anonymizing Kernel
* Reading from  : /dev/input/event5 (dakai PS/2+USB Keyboard)
* Writing to    : /dev/uinput
* Maximum delay : 100 ms

/dev/uinput isn’t owned by the input group. It’s owned by the root group.

ls -la /dev/uinput

crw------- 1 root root

It seems like we can change what group it’s owned by with a udev rule so we can probably make it owned by the kloak group.

1 Like

Another thing I think we should look into is creating device policies for our sandboxes so the service can only access a limited number of devices.

PrivateDevices=true sets up a new /dev mount and adds a few necessary devices like /dev/null and /dev/random but some services need access to more than just those (e.g. kloak needs access to input devices) so we can either just leave out PrivateDevices (less secure) or create a device policy so only the needed devices are in the sandbox (more secure).

Currently, we just leave out PrivateDevices but that isn’t great.

I haven’t had much luck with creating device policies though. The documentation is here.

Debian doesn’t seem to use this much. Running

grep "DeviceAllow" /lib/systemd/system/*.service

only shows OpenVPN stuff.

1 Like

Great guides for using systemd to sandbox services. There may be features in there beyond what we do:

1 Like

We should look into setting a more restrictive umask per-service (not global like we tried before) and whitelisting IP addresses.


I tried these but it broke onion-grater. Exactly which IP addresses does onion-grater connect to? We can create a whitelist of them.

Also see: Linux Hardening Guide | Madaidan's Insecurities

1 Like

Is disable systemd hardening · Kicksecure/sdwdate@b5f0ea1 · GitHub still a problem? I can’t reproduce it with the sandboxing enabled. I can send a PR to strengthen sdwdate’s sandboxing too if that isn’t an issue anymore.

1 Like

I see no issues on my end. Please test.

I had to remove MemoryDenyWriteExecute from onion-grater and sdwdate because some python thing seems to be using RWX memory for some reason.

1 Like

You may get these errors:

Unknown lvalue 'ProtectKernelLogs' in section 'Service', ignoring
Unknown lvalue 'ProtectHostname' in section 'Service', ignoring
Unknown lvalue 'ProtectProc' in section 'Service', ignoring
Unknown lvalue 'ProcSubset' in section 'Service', ignoring

These are harmless. Those options only exist in later versions of systemd but I added them for forward compatibility. Another option to look into:

1 Like

All merged.

Awesome! I will test this over the next days and report back should there be any issues.

1 Like


sdwdate log will show:

/bin/date: cannot set date: Operation not permitted

During testing might help:

  • sudo sdwdate-clock-jump
  • sudo systemctl restart sdwdate

First sets time using date. The later (after first time after boot setting using date) using sclockadj.

1 Like

sudo systemctl stop sdwdate also broken. Takes too long. Should be (almost) instant. Probably since the signal is no longer received by sdwdate. (Then systemd uses sigkill after timeout but that’s bad.)

1 Like

Try adding kill getsockopt unlink to SystemCallFilter, /var/lib/sdwdate/ to ReadWriteDirectories and commenting PrivateUsers.

## Sandboxing.
ReadWriteDirectories=/run/sdwdate/ /var/lib/sdwdate/
RestrictAddressFamilies=AF_UNIX AF_INET
SystemCallFilter=wait4 select futex read stat close openat fstat lseek mmap rt_sigaction getdents64 mprotect ioctl recvfrom munmap brk rt_sigprocmask fcntl getpid write access socket sendto dup2 clone execve getrandom geteuid getgid madvise getuid getegid readlink pipe rt_sigreturn connect pipe2 prlimit64 set_robust_list dup arch_prctl lstat set_tid_address sysinfo sigaltstack rt_sigsuspend shutdown timer_settime mkdir timer_create statfs getcwd setpgid setsockopt uname bind getpgrp getppid getpeername chdir poll getsockname fadvise64 clock_settime kill getsockopt unlink

Does that fix the issue?

1 Like

That works better. However one issue remains. process.wait(timeout_seconds) is broken. Hangs forever. To reproduce, try this:




timeout_seconds = 120


timeout_seconds = 2

And then restart sdwdate.

Meanwhile I will revert these changes to unbreak sdwdate in Whonix developers repository. Once this is fixed, I’ll look into it ASP.

1 Like

I’m getting this error:

sdwdate - ERROR - General Timeout Error. Internet connection might be down.

However, it appears regardless of whether or not the sandboxing is enabled. It only happens after I change the variable you told me to. Are you sure this is a sandboxing error and not a code error?

1 Like

There was before no such code error. Cannot totally rule out yet but only happened after enabling sandboxing. I’ll see that I can provide instructions for reproduction.

1 Like

I couldn’t reproduce this anymore. Perhaps I made a mistake and tested the wrong version. sdwdate sandboxing is re-enabled in Whonix developers, testers, and stable-proposed-updates repository.
Thank you for another great contribution!

1 Like

sdwdate is getting killed on Debian buster on the ppc64el architecture.

How to debug systemd hardening? All I get:

sdwdate.service: Main process exited, code=killed, status=31/SYS

Any way to see a more specific error message which systemd hardening was violated? Any other way to debug other than trial and error which the offensive option is?

Similar issue: