4343from plainbox .i18n import gettext as _
4444from plainbox .impl .color import Colorizer
4545from 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+ )
4751from 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
0 commit comments