graysky2 / kodi-standalone-service

Use systemd to allow for standalone operation of kodi.
160 stars 35 forks source link

CapabilityBoundingSet= was required when using TTYPath= with flatpak #52

Open tofurky opened 2 weeks ago

tofurky commented 2 weeks ago

I used the kodi-gbm.service unit as a template to create my own unit with the tv.kodi.Kodi flatpak and ran into an issue when TTYPath= was set; bubblewrap sandbox refused to start (even when running bwrap directly in ExecStart to debug, without flatpak).

I'm using flatpak now as that's the only supported way to get the official non-distro builds these days.

ExecStart=/usr/bin/flatpak run -v tv.kodi.Kodi --standalone --audio-backend=alsa --windowing=gbm
ExecStop=/usr/bin/flatpak kill tv.kodi.Kodi

The error from bwrap was Unexpected capabilities but not setuid, old file caps config? (https://github.com/containers/bubblewrap/blob/a253257cd298892da43e15201d83f9a02c9b58b5/bubblewrap.c#L875-L881)

After running systemd-analyze security kodi-gbm.service, I saw that all capabilities were allowed. Not really sure why TTYPath is what does it; maybe an interplay with PAMName=login and actually succeeding in starting a proper login session? Without TTYPath, the following is seen: pam_systemd(login:session): Failed to create session: VT number out of range.

Anyways, the fix was to add CapabilityBoundingSet= under [Service] to prevent any new capabilities from being obtained. I don't understand this fully enough to add a PR or README note; but, hopefully this helps someone running into the same issue. This would also prevent binding to a privileged port, I think.

Some more details - strace showed the following when replacing ExecStart with strace -s1024 bwrap >/tmp/bwrap 2>&1:

mprotect(0x79f993ac8000, 8192, PROT_READ) = 0
prlimit64(0, RLIMIT_STACK, NULL, {rlim_cur=8192*1024, rlim_max=RLIM64_INFINITY}) = 0
munmap(0x79f993a86000, 40859)           = 0
prctl(PR_CAPBSET_READ, CAP_MAC_OVERRIDE) = 1
prctl(PR_CAPBSET_READ, 0x30 /* CAP_??? */) = -1 EINVAL (Invalid argument)
prctl(PR_CAPBSET_READ, CAP_CHECKPOINT_RESTORE) = 1
prctl(PR_CAPBSET_READ, 0x2c /* CAP_??? */) = -1 EINVAL (Invalid argument)
prctl(PR_CAPBSET_READ, 0x2a /* CAP_??? */) = -1 EINVAL (Invalid argument)
prctl(PR_CAPBSET_READ, 0x29 /* CAP_??? */) = -1 EINVAL (Invalid argument)
statfs("/sys/fs/selinux", 0x7ffc9a04eb80) = -1 ENOENT (No such file or directory)
statfs("/selinux", 0x7ffc9a04eb80)      = -1 ENOENT (No such file or directory)
getrandom("\x6f\x67\xa7\x3b\x5e\x54\x9f\xce", 8, GRND_NONBLOCK) = 8
brk(NULL)                               = 0x5c043170b000
brk(0x5c043172c000)                     = 0x5c043172c000
openat(AT_FDCWD, "/proc/filesystems", O_RDONLY|O_CLOEXEC) = 3
fstat(3, {st_mode=S_IFREG|0444, st_size=0, ...}) = 0
read(3, "nodev\tsysfs\nnodev\ttmpfs\nnodev\tbdev\nnodev\tproc\nnodev\tcgroup\nnodev\tcgroup2\nnodev\tcpuset\nnodev\tdevtmpfs\nnodev\tconfigfs\nnodev\tdebugfs\nnodev\ttracefs\nnodev\tsecurityfs\nnodev\tsockfs\nnodev\tbpf\nnodev\tpipefs\nnodev\tramfs\nnodev\thugetlbfs\nnodev\tdevpts\n\text3\n\text2\n\text4\n\tsquashfs\n\tvfat\nnodev\tecryptfs\n\tfuseblk\nnodev\tfuse\nnodev\tfusectl\nnodev\tefivarfs\nnodev\tmqueue\nnodev\tpstore\n\tbtrfs\nnodev\tautofs\nnodev\tbinfmt_misc\nnodev\tcifs\nnodev\tsmb3\n", 1024) = 422
read(3, "", 1024)                       = 0
close(3)                                = 0
access("/etc/selinux/config", F_OK)     = -1 ENOENT (No such file or directory)
getuid()                                = 1001
getgid()                                = 1001
geteuid()                               = 1001
capget({version=_LINUX_CAPABILITY_VERSION_3, pid=0}, {effective=1<<CAP_WAKE_ALARM, permitted=1<<CAP_WAKE_ALARM, inheritable=1<<CAP_WAKE_ALARM}) = 0
write(2, "bwrap: ", 7bwrap: )                  = 7
write(2, "Unexpected capabilities but not setuid, old file caps config?", 61Unexpected capabilities but not setuid, old file caps config?) = 61
write(2, "\n", 1
)                       = 1
exit_group(1)                           = ?
+++ exited with 1 +++
graysky2 commented 2 weeks ago

For clarity, can you post the working service file?

tofurky commented 2 weeks ago
[Unit]
Description=Kodi standalone (GBM)
After=remote-fs.target systemd-user-sessions.service network-online.target nss-lookup.target sound.target bluetooth.target polkit.service upower.service mysqld.service lircd.service mythtv-backend.service sys-devices-pci0000:00-0000:00:1f.3-sound-card0-controlC0.device
Wants=network-online.target polkit.service upower.service mythtv-backend.service sys-devices-pci0000:00-0000:00:1f.3-sound-card0-controlC0.device
Conflicts=getty@tty1.service

[Service]
User=kodi
Group=kodi
EnvironmentFile=-/etc/default/kodi-gbm
PAMName=login
Type=simple
TTYPath=/dev/tty1
TTYReset=yes
ExecStart=/usr/bin/flatpak run -v tv.kodi.Kodi --standalone --audio-backend=alsa --windowing=gbm
ExecStop=/usr/bin/flatpak kill tv.kodi.Kodi
Restart=on-abort
StandardInput=tty
StandardOutput=journal
StandardError=journal
CapabilityBoundingSet=

[Install]
Alias=display-manager.service

It's still got some issues; stderr/stdout from flatpak isn't captured unless I wrap it in something like /bin/sh -c "/usr/bin/flatpak ... >/tmp/out 2>&1", but mostly works as expected.

graysky2 commented 2 weeks ago

I haven't used flatpak before but if you feel like there is some value for other users, perhaps I can put your service into a contrib folder or something that is technically unsupported.