|
| 1 | +// -*- Mode: Go; indent-tabs-mode: t -*- |
| 2 | + |
| 3 | +/* |
| 4 | + * Copyright (C) 2022 Canonical Ltd |
| 5 | + * |
| 6 | + * This program is free software: you can redistribute it and/or modify |
| 7 | + * it under the terms of the GNU General Public License version 3 as |
| 8 | + * published by the Free Software Foundation. |
| 9 | + * |
| 10 | + * This program is distributed in the hope that it will be useful, |
| 11 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 12 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 13 | + * GNU General Public License for more details. |
| 14 | + * |
| 15 | + * You should have received a copy of the GNU General Public License |
| 16 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
| 17 | + * |
| 18 | + */ |
| 19 | + |
| 20 | +package builtin |
| 21 | + |
| 22 | +const steamSupportSummary = `allow Steam to configure pressure-vessel containers` |
| 23 | + |
| 24 | +const steamSupportBaseDeclarationPlugs = ` |
| 25 | + steam-support: |
| 26 | + allow-installation: false |
| 27 | + deny-auto-connection: true |
| 28 | +` |
| 29 | + |
| 30 | +const steamSupportBaseDeclarationSlots = ` |
| 31 | + steam-support: |
| 32 | + allow-installation: |
| 33 | + slot-snap-type: |
| 34 | + - core |
| 35 | + deny-auto-connection: true |
| 36 | +` |
| 37 | + |
| 38 | +const steamSupportConnectedPlugAppArmor = ` |
| 39 | +# Allow pressure-vessel to set up its Bubblewrap sandbox. |
| 40 | +/sys/kernel/ r, |
| 41 | +@{PROC}/sys/kernel/overflowuid r, |
| 42 | +@{PROC}/sys/kernel/overflowgid r, |
| 43 | +@{PROC}/sys/kernel/sched_autogroup_enabled r, |
| 44 | +@{PROC}/pressure/io r, |
| 45 | +owner @{PROC}/@{pid}/uid_map rw, |
| 46 | +owner @{PROC}/@{pid}/gid_map rw, |
| 47 | +owner @{PROC}/@{pid}/setgroups rw, |
| 48 | +owner @{PROC}/@{pid}/mounts r, |
| 49 | +owner @{PROC}/@{pid}/mountinfo r, |
| 50 | +
|
| 51 | +# Create and pivot to the intermediate root |
| 52 | +mount options=(rw, rslave) -> /, |
| 53 | +mount options=(rw, silent, rslave) -> /, |
| 54 | +mount fstype=tmpfs options=(rw, nosuid, nodev) tmpfs -> /tmp/, |
| 55 | +mount options=(rw, rbind) /tmp/newroot/ -> /tmp/newroot/, |
| 56 | +pivot_root oldroot=/tmp/oldroot/ /tmp/, |
| 57 | +
|
| 58 | +# Set up sandbox in /newroot |
| 59 | +mount options=(rw, rbind) /oldroot/ -> /newroot/, |
| 60 | +mount options=(rw, rbind) /oldroot/dev/ -> /newroot/dev/, |
| 61 | +mount options=(rw, rbind) /oldroot/etc/ -> /newroot/etc/, |
| 62 | +mount options=(rw, rbind) /oldroot/proc/ -> /newroot/proc/, |
| 63 | +mount options=(rw, rbind) /oldroot/sys/ -> /newroot/sys/, |
| 64 | +mount options=(rw, rbind) /oldroot/tmp/ -> /newroot/tmp/, |
| 65 | +mount options=(rw, rbind) /oldroot/var/ -> /newroot/var/, |
| 66 | +mount options=(rw, rbind) /oldroot/var/tmp/ -> /newroot/var/tmp/, |
| 67 | +mount options=(rw, rbind) /oldroot/usr/ -> /newroot/run/host/usr/, |
| 68 | +mount options=(rw, rbind) /oldroot/etc/ -> /newroot/run/host/etc/, |
| 69 | +mount options=(rw, rbind) /oldroot/usr/lib/os-release -> /newroot/run/host/os-release, |
| 70 | +
|
| 71 | +# Bubblewrap performs remounts on directories it binds under /newroot |
| 72 | +# to fix up the options (since options other than MS_REC are ignored |
| 73 | +# when performing a bind mount). Ideally we could do something like: |
| 74 | +# remount options=(bind, silent, nosuid, *) /newroot/{,**}, |
| 75 | +# |
| 76 | +# But that is not supported by AppArmor. So we enumerate the possible |
| 77 | +# combinations of options Bubblewrap might use. |
| 78 | +remount options=(bind, silent, nosuid, rw) /newroot/{,**}, |
| 79 | +remount options=(bind, silent, nosuid, rw, nodev) /newroot/{,**}, |
| 80 | +remount options=(bind, silent, nosuid, rw, noexec) /newroot/{,**}, |
| 81 | +remount options=(bind, silent, nosuid, rw, nodev, noexec) /newroot/{,**}, |
| 82 | +remount options=(bind, silent, nosuid, rw, noatime) /newroot/{,**}, |
| 83 | +remount options=(bind, silent, nosuid, rw, nodev, noatime) /newroot/{,**}, |
| 84 | +remount options=(bind, silent, nosuid, rw, noexec, noatime) /newroot/{,**}, |
| 85 | +remount options=(bind, silent, nosuid, rw, nodev, noexec, noatime) /newroot/{,**}, |
| 86 | +remount options=(bind, silent, nosuid, rw, relatime) /newroot/{,**}, |
| 87 | +remount options=(bind, silent, nosuid, rw, nodev, relatime) /newroot/{,**}, |
| 88 | +remount options=(bind, silent, nosuid, rw, noexec, relatime) /newroot/{,**}, |
| 89 | +remount options=(bind, silent, nosuid, rw, nodev, noexec, relatime) /newroot/{,**}, |
| 90 | +remount options=(bind, silent, nosuid, rw, nodiratime) /newroot/{,**}, |
| 91 | +remount options=(bind, silent, nosuid, rw, nodev, nodiratime) /newroot/{,**}, |
| 92 | +remount options=(bind, silent, nosuid, rw, noexec, nodiratime) /newroot/{,**}, |
| 93 | +remount options=(bind, silent, nosuid, rw, nodev, noexec, nodiratime) /newroot/{,**}, |
| 94 | +remount options=(bind, silent, nosuid, rw, noatime, nodiratime) /newroot/{,**}, |
| 95 | +remount options=(bind, silent, nosuid, rw, nodev, noatime, nodiratime) /newroot/{,**}, |
| 96 | +remount options=(bind, silent, nosuid, rw, noexec, noatime, nodiratime) /newroot/{,**}, |
| 97 | +remount options=(bind, silent, nosuid, rw, nodev, noexec, noatime, nodiratime) /newroot/{,**}, |
| 98 | +remount options=(bind, silent, nosuid, rw, relatime, nodiratime) /newroot/{,**}, |
| 99 | +remount options=(bind, silent, nosuid, rw, nodev, relatime, nodiratime) /newroot/{,**}, |
| 100 | +remount options=(bind, silent, nosuid, rw, noexec, relatime, nodiratime) /newroot/{,**}, |
| 101 | +remount options=(bind, silent, nosuid, rw, nodev, noexec, relatime, nodiratime) /newroot/{,**}, |
| 102 | +remount options=(bind, silent, nosuid, ro) /newroot/{,**}, |
| 103 | +remount options=(bind, silent, nosuid, ro, nodev) /newroot/{,**}, |
| 104 | +remount options=(bind, silent, nosuid, ro, noexec) /newroot/{,**}, |
| 105 | +remount options=(bind, silent, nosuid, ro, nodev, noexec) /newroot/{,**}, |
| 106 | +remount options=(bind, silent, nosuid, ro, noatime) /newroot/{,**}, |
| 107 | +remount options=(bind, silent, nosuid, ro, nodev, noatime) /newroot/{,**}, |
| 108 | +remount options=(bind, silent, nosuid, ro, noexec, noatime) /newroot/{,**}, |
| 109 | +remount options=(bind, silent, nosuid, ro, nodev, noexec, noatime) /newroot/{,**}, |
| 110 | +remount options=(bind, silent, nosuid, ro, relatime) /newroot/{,**}, |
| 111 | +remount options=(bind, silent, nosuid, ro, nodev, relatime) /newroot/{,**}, |
| 112 | +remount options=(bind, silent, nosuid, ro, noexec, relatime) /newroot/{,**}, |
| 113 | +remount options=(bind, silent, nosuid, ro, nodev, noexec, relatime) /newroot/{,**}, |
| 114 | +remount options=(bind, silent, nosuid, ro, nodiratime) /newroot/{,**}, |
| 115 | +remount options=(bind, silent, nosuid, ro, nodev, nodiratime) /newroot/{,**}, |
| 116 | +remount options=(bind, silent, nosuid, ro, noexec, nodiratime) /newroot/{,**}, |
| 117 | +remount options=(bind, silent, nosuid, ro, nodev, noexec, nodiratime) /newroot/{,**}, |
| 118 | +remount options=(bind, silent, nosuid, ro, noatime, nodiratime) /newroot/{,**}, |
| 119 | +remount options=(bind, silent, nosuid, ro, nodev, noatime, nodiratime) /newroot/{,**}, |
| 120 | +remount options=(bind, silent, nosuid, ro, noexec, noatime, nodiratime) /newroot/{,**}, |
| 121 | +remount options=(bind, silent, nosuid, ro, nodev, noexec, noatime, nodiratime) /newroot/{,**}, |
| 122 | +remount options=(bind, silent, nosuid, ro, relatime, nodiratime) /newroot/{,**}, |
| 123 | +remount options=(bind, silent, nosuid, ro, nodev, relatime, nodiratime) /newroot/{,**}, |
| 124 | +remount options=(bind, silent, nosuid, ro, noexec, relatime, nodiratime) /newroot/{,**}, |
| 125 | +remount options=(bind, silent, nosuid, ro, nodev, noexec, relatime, nodiratime) /newroot/{,**}, |
| 126 | +
|
| 127 | +/newroot/** rwkl, |
| 128 | +/bindfile* rw, |
| 129 | +mount options=(rw, rbind) /oldroot/home/** -> /newroot/home/**, |
| 130 | +mount options=(rw, rbind) /oldroot/snap/** -> /newroot/snap/**, |
| 131 | +mount options=(rw, rbind) /oldroot/home/**/usr/ -> /newroot/usr/, |
| 132 | +mount options=(rw, rbind) /oldroot/home/**/usr/etc/** -> /newroot/etc/**, |
| 133 | +mount options=(rw, rbind) /oldroot/home/**/usr/etc/ld.so.cache -> /newroot/run/pressure-vessel/ldso/runtime-ld.so.cache, |
| 134 | +mount options=(rw, rbind) /oldroot/home/**/usr/etc/ld.so.conf -> /newroot/run/pressure-vessel/ldso/runtime-ld.so.conf, |
| 135 | +
|
| 136 | +mount options=(rw, rbind) /oldroot/etc/machine-id -> /newroot/etc/machine-id, |
| 137 | +mount options=(rw, rbind) /oldroot/etc/group -> /newroot/etc/group, |
| 138 | +mount options=(rw, rbind) /oldroot/etc/passwd -> /newroot/etc/passwd, |
| 139 | +mount options=(rw, rbind) /oldroot/etc/host.conf -> /newroot/etc/host.conf, |
| 140 | +mount options=(rw, rbind) /oldroot/etc/hosts -> /newroot/etc/hosts, |
| 141 | +mount options=(rw, rbind) /oldroot/**/*resolv.conf -> /newroot/etc/resolv.conf, |
| 142 | +mount options=(rw, rbind) /bindfile* -> /newroot/etc/timezone, |
| 143 | +
|
| 144 | +mount options=(rw, rbind) /oldroot/run/systemd/journal/socket -> /newroot/run/systemd/journal/socket, |
| 145 | +mount options=(rw, rbind) /oldroot/run/systemd/journal/stdout -> /newroot/run/systemd/journal/stdout, |
| 146 | +
|
| 147 | +mount options=(rw, rbind) /oldroot/usr/share/fonts/ -> /newroot/run/host/fonts/, |
| 148 | +mount options=(rw, rbind) /oldroot/usr/local/share/fonts/ -> /newroot/run/host/local-fonts/, |
| 149 | +mount options=(rw, rbind) /oldroot/{var/cache/fontconfig,usr/lib/fontconfig/cache}/ -> /newroot/run/host/fonts-cache/, |
| 150 | +mount options=(rw, rbind) /oldroot/home/**/.cache/fontconfig/ -> /newroot/run/host/user-fonts-cache/, |
| 151 | +mount options=(rw, rbind) /bindfile* -> /newroot/run/host/font-dirs.xml, |
| 152 | +
|
| 153 | +mount options=(rw, rbind) /oldroot/usr/share/icons/ -> /newroot/run/host/share/icons/, |
| 154 | +mount options=(rw, rbind) /oldroot/home/**/.local/share/icons/ -> /newroot/run/host/user-share/icons/, |
| 155 | +
|
| 156 | +mount options=(rw, rbind) /oldroot/run/user/[0-9]*/wayland-* -> /newroot/run/pressure-vessel/wayland-*, |
| 157 | +mount options=(rw, rbind) /oldroot/tmp/.X11-unix/X* -> /newroot/tmp/.X11-unix/X99, |
| 158 | +mount options=(rw, rbind) /bindfile* -> /newroot/run/pressure-vessel/Xauthority, |
| 159 | +
|
| 160 | +mount options=(rw, rbind) /bindfile* -> /newroot/run/pressure-vessel/pulse/config, |
| 161 | +mount options=(rw, rbind) /oldroot/run/user/[0-9]*/pulse/native -> /newroot/run/pressure-vessel/pulse/native, |
| 162 | +mount options=(rw, rbind) /oldroot/dev/snd/ -> /newroot/dev/snd/, |
| 163 | +mount options=(rw, rbind) /bindfile* -> /newroot/etc/asound.conf, |
| 164 | +mount options=(rw, rbind) /oldroot/run/user/[0-9]*/bus -> /newroot/run/pressure-vessel/bus, |
| 165 | +
|
| 166 | +mount options=(rw, rbind) /oldroot/run/dbus/system_bus_socket -> /newroot/run/dbus/system_bus_socket, |
| 167 | +mount options=(rw, rbind) /oldroot/run/systemd/resolve/io.systemd.Resolve -> /newroot/run/systemd/resolve/io.systemd.Resolve, |
| 168 | +mount options=(rw, rbind) /bindfile* -> /newroot/run/host/container-manager, |
| 169 | +
|
| 170 | +# Allow masking of certain directories in the sandbox |
| 171 | +mount fstype=tmpfs options=(rw, nosuid, nodev) tmpfs -> /newroot/home/*/snap/steam/common/.local/share/vulkan/implicit_layer.d/, |
| 172 | +mount fstype=tmpfs options=(rw, nosuid, nodev) tmpfs -> /newroot/run/pressure-vessel/ldso/, |
| 173 | +mount fstype=tmpfs options=(rw, nosuid, nodev) tmpfs -> /newroot/tmp/.X11-unix/, |
| 174 | +
|
| 175 | +# Pivot from the intermediate root to sandbox root |
| 176 | +mount options in (rw, silent, rprivate) -> /oldroot/, |
| 177 | +umount /oldroot/, |
| 178 | +pivot_root oldroot=/newroot/ /newroot/, |
| 179 | +umount /, |
| 180 | +
|
| 181 | +# Permissions needed within sandbox root |
| 182 | +/usr/lib/pressure-vessel/** ixr, |
| 183 | +/run/host/** mr, |
| 184 | +/run/pressure-vessel/** mrw, |
| 185 | +/run/host/usr/sbin/ldconfig* ixr, |
| 186 | +/run/host/usr/bin/localedef ixr, |
| 187 | +/var/cache/ldconfig/** rw, |
| 188 | +
|
| 189 | +capability sys_admin, |
| 190 | +capability sys_ptrace, |
| 191 | +capability setpcap, |
| 192 | +` |
| 193 | + |
| 194 | +const steamSupportConnectedPlugSecComp = ` |
| 195 | +# Description: additional permissions needed by Steam |
| 196 | +
|
| 197 | +# Allow Steam to set up "pressure-vessel" containers to run games in. |
| 198 | +mount |
| 199 | +umount2 |
| 200 | +pivot_root |
| 201 | +` |
| 202 | + |
| 203 | +func init() { |
| 204 | + registerIface(&commonInterface{ |
| 205 | + name: "steam-support", |
| 206 | + summary: steamSupportSummary, |
| 207 | + implicitOnCore: false, |
| 208 | + implicitOnClassic: true, |
| 209 | + baseDeclarationSlots: steamSupportBaseDeclarationSlots, |
| 210 | + baseDeclarationPlugs: steamSupportBaseDeclarationPlugs, |
| 211 | + connectedPlugAppArmor: steamSupportConnectedPlugAppArmor, |
| 212 | + connectedPlugSecComp: steamSupportConnectedPlugSecComp, |
| 213 | + }) |
| 214 | +} |
0 commit comments