Skip to content

Commit 975b6a5

Browse files
committed
Use AmbientCapabilities on non-core16 systems
fs caps dont work on newer bases because all mounted fs are no setuid, which also means no setcap, so the current implementation doesnt work. This also add setpriv to the dependencies, which is already staged on core!=18, but still putting it there so that if it gets removed we have it
1 parent 6adbd50 commit 975b6a5

File tree

6 files changed

+84
-18
lines changed

6 files changed

+84
-18
lines changed

checkbox-core-snap/series18/snap/snapcraft.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -198,6 +198,7 @@ parts:
198198
- zlib1g-dev
199199
- build-essential
200200
stage-packages:
201+
- setpriv # used to downgrade AmbientCapabilities of systemd-based runner
201202
- python3-markupsafe
202203
- python3-jinja2
203204
- python3-packaging

checkbox-core-snap/series20/snap/snapcraft.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -206,6 +206,7 @@ parts:
206206
- zlib1g-dev
207207
- build-essential
208208
stage-packages:
209+
- setpriv # used to downgrade AmbientCapabilities of systemd-based runner
209210
- python3-markupsafe
210211
- python3-jinja2
211212
- python3-packaging

checkbox-core-snap/series22/snap/snapcraft.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -213,6 +213,7 @@ parts:
213213
- zlib1g-dev
214214
- build-essential
215215
stage-packages:
216+
- setpriv # used to downgrade AmbientCapabilities of systemd-based runner
216217
- python3-markupsafe
217218
- python3-jinja2
218219
- python3-packaging

checkbox-core-snap/series24/snap/snapcraft.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,7 @@ parts:
154154
source: checkbox-support
155155
source-type: local
156156
stage-packages:
157+
- setpriv # used to downgrade AmbientCapabilities of systemd-based runner
157158
# actual requirements
158159
- python3-bluez
159160
- python3-pyparsing

checkbox-ng/plainbox/impl/execution.py

Lines changed: 54 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,11 @@
4343
from plainbox.i18n import gettext as _
4444
from plainbox.impl.color import Colorizer
4545
from plainbox.impl.unit.job import supported_plugins
46-
from plainbox.impl.unit.unit import on_ubuntucore
46+
from plainbox.impl.unit.unit import (
47+
on_ubuntucore,
48+
get_snap_base,
49+
get_checkbox_runtime_path,
50+
)
4751
from plainbox.impl.result import (
4852
IOLogRecordWriter,
4953
JobResultBuilder,
@@ -777,14 +781,14 @@ def dangerous_nsenter(path):
777781
return
778782
try:
779783
# here recover setcap and nsenter binaries path outside the sandbox
780-
setcap_path = check_output(
781-
get_plz_run(["bash", "-c", "which setcap"]),
782-
universal_newlines=True,
783-
).strip()
784-
nsenter_path = check_output(
785-
get_plz_run(["bash", "-c", "which nsenter"]),
786-
universal_newlines=True,
787-
).strip()
784+
runtime_path = get_checkbox_runtime_path()
785+
runtime_nsenter = runtime_path / "usr" / "bin" / "nsenter"
786+
# Note: this path only works on core<24, on core24+ this is in
787+
# /usr/sbin but this code should only work on core16, so this is
788+
# fine
789+
runtime_setcap = runtime_path / "sbin" / "setcap"
790+
setcap_path = str(runtime_setcap)
791+
nsenter_path = str(runtime_nsenter)
788792
check_call(get_plz_run(["cp", nsenter_path, path]))
789793
check_call(
790794
get_plz_run(
@@ -840,30 +844,60 @@ def get_execution_command_systemd_unit(
840844
# DONT use SNAP_COMMON (not writable by normal user)
841845
# DONT use SNAP_USER_COMMON (not writable by normal user if running as root)
842846
shared_location = "/var/tmp"
843-
if on_ubuntucore():
844-
if target_user != "root":
847+
core_snap = on_ubuntucore()
848+
# when in a core snap, we need the snap mount namespace to use anything
849+
# that was shared via a content interface
850+
if core_snap:
851+
snap_base = get_snap_base()
852+
if target_user != "root" and snap_base == "core16":
845853
# here we need a dangerous copy of nsenter that works as "normal"
846854
# user because the unit will be normal user and else it wont be
847-
# able to mount the snap namespace
855+
# able to mount the snap namespace. This only on core16 because
856+
# other distros support setting AmbientCapabilities via dbus API
857+
# making that a better, more secure alternative
848858
with tempfile.NamedTemporaryFile(
849859
mode="w", delete=False, prefix="nsenter_", dir=shared_location
850860
) as f:
851861
dangerous_nsenter_path = f.name
862+
elif target_user != "root":
863+
# on core > 16 we can set AmbientCapabilities without using fs caps
864+
# These are the capabilities needed to mount the namespace
865+
# from linux/capability.h
866+
# uint64(1 << 21| 1<<18 | 1<<6 | 1<<7))}
867+
CAP_SETGID = 6 # necessary for setpriv
868+
CAP_SETUID = 7 # necessary for setpriv
869+
CAP_SYS_CHROOT = 18 # necessary for nsenter
870+
CAP_SYS_ADMIN = 21 # necessary for nsenter
871+
ambient_capabilities_bitset = (
872+
1 << CAP_SETGID
873+
| 1 << CAP_SETUID
874+
| 1 << CAP_SYS_CHROOT
875+
| 1 << CAP_SYS_ADMIN
876+
)
877+
wrapper_cmd += [
878+
"-ambient-capabilities",
879+
str(ambient_capabilities_bitset),
880+
]
881+
# these binaries are not reliably shipped / may not be in path
882+
runtime_path = get_checkbox_runtime_path()
883+
runtime_nsenter = runtime_path / "usr" / "bin" / "nsenter"
884+
runtime_setpriv = runtime_path / "usr" / "bin" / "setpriv"
852885

853-
# when in a core snap, we need the snap mount namespace to use anything
854-
# that was shared via a content interface
855886
snap_name = os.getenv("SNAP_NAME", "checkbox")
856887
cmd += [
857-
# Note: don't make this absolute! We must use the system nsenter
858-
# as we have yet to mount the namespace, so the snap one won't
859-
# work
860888
(
861-
"nsenter"
889+
str(runtime_nsenter)
862890
if dangerous_nsenter_path is None
863891
else dangerous_nsenter_path
864892
),
865893
"-m/run/snapd/ns/{}.mnt".format(snap_name),
866894
]
895+
if snap_base != "core16":
896+
# on non-core16 we have given ourselves AmbientCapabilities. After
897+
# using them for what we needed (mounting the namespace) we must
898+
# drop them else the "user" test will have way more priviledges
899+
# than it is supposed to
900+
cmd += [str(runtime_setpriv), "--inh-caps=-all"]
867901
env = get_execution_environment(job, environ, session_id, nest_dir)
868902
if extra_env:
869903
env.update(extra_env())
@@ -888,6 +922,8 @@ def get_execution_command_systemd_unit(
888922
path = f.name
889923

890924
wrapper_cmd.append(path)
925+
# dangerous_nsenter will create the dangerous version only if it is needed
926+
# it is a no-op on non-core16 snaps
891927
with dangerous_nsenter(dangerous_nsenter_path):
892928
try:
893929
yield wrapper_cmd

checkbox-ng/plainbox/impl/unit/unit.py

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
import logging
3030
import os
3131
import string
32+
from pathlib import Path
3233
from functools import lru_cache
3334

3435
from jinja2 import Template
@@ -72,6 +73,31 @@ def on_ubuntucore():
7273
return False
7374

7475

76+
@lru_cache(maxsize=None)
77+
def get_snap_base():
78+
"""
79+
Get the current snap base
80+
"""
81+
snap = os.getenv("SNAP")
82+
if snap:
83+
with open(os.path.join(snap, "meta/snap.yaml")) as f:
84+
for l in f.readlines():
85+
if "base:" in l:
86+
core_version = l.replace("base:", "").strip()
87+
if core_version == "core":
88+
return "core16"
89+
return core_version
90+
return "core16" # core16 may not declare base
91+
raise ValueError("Couldn't detect snap path. Missing SNAP envvar")
92+
93+
94+
def get_checkbox_runtime_path():
95+
snap_base_n = get_snap_base().replace("core", "")
96+
# the bases of the runtime and frontend must always match
97+
runtime_name = "checkbox" + snap_base_n
98+
return Path("/snap") / ("checkbox" + snap_base_n) / "current"
99+
100+
75101
class MissingParam(Exception):
76102
"""
77103
Indicaiton of a missing parameter required for template instantiation.

0 commit comments

Comments
 (0)