core: add absolute-path properties to varlink StartTransient
Similar to PR#42360 this commit adds missing properties for
absolute path handling in io.systemd.Unit.StartTransient for
the `Exec` context and a macro helper to share the common code.
The properties added are:
IPCNamespacePath, NetworkNamespacePath, RootDirectory, RootHashPath,
RootHashSignaturePath, RootImage, RootVerity, UserNamespacePath,
RootMStack.
Note that RootHashPath, RootHashSignaturePath need custom apply
functions because the varlink name "RootHashPath" differs from the
name that needs to be written into the unit file ("RootHash=")
and the iovec must be cleared.
This commit is contained in:
committed by
Zbigniew Jędrzejewski-Szmek
parent
ef7b9f679b
commit
ab344eb750
@@ -691,6 +691,16 @@ typedef struct TransientExecContextParameters {
|
||||
const char *user;
|
||||
const char *group;
|
||||
char **supplementary_groups;
|
||||
/* Absolute-path strings: must validate via path_is_absolute(). */
|
||||
const char *ipc_namespace_path;
|
||||
const char *network_namespace_path;
|
||||
const char *root_directory;
|
||||
const char *root_hash_path;
|
||||
const char *root_hash_sig_path;
|
||||
const char *root_image;
|
||||
const char *root_mstack;
|
||||
const char *root_verity;
|
||||
const char *user_namespace_path;
|
||||
bool nice_set;
|
||||
int nice;
|
||||
bool oom_score_adjust_set;
|
||||
@@ -1210,6 +1220,83 @@ DEFINE_APPLY_EXEC_TRISTATE_BOOL(restrict_realtime, "RestrictRealtime");
|
||||
DEFINE_APPLY_EXEC_TRISTATE_BOOL(restrict_suid_sgid, "RestrictSUIDSGID");
|
||||
DEFINE_APPLY_EXEC_TRISTATE_BOOL(root_ephemeral, "RootEphemeral");
|
||||
|
||||
/* Generate an apply fn for an exec-context absolute-path string: validate via path_is_absolute(),
|
||||
* copy into ExecContext, write "Name=<path>". Mirrors D-Bus bus_set_transient_path. */
|
||||
#define DEFINE_APPLY_EXEC_ABSOLUTE_PATH(field, JsonName) \
|
||||
static int apply_exec_##field(Unit *u, ExecContext *c, TransientExecContextParameters *p) { \
|
||||
int r; \
|
||||
assert(c); \
|
||||
assert(p); \
|
||||
if (!p->field) \
|
||||
return 0; \
|
||||
if (!path_is_absolute(p->field)) \
|
||||
return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), \
|
||||
JsonName ": expects an absolute path"); \
|
||||
r = free_and_strdup(&c->field, p->field); \
|
||||
if (r < 0) \
|
||||
return r; \
|
||||
unit_write_settingf(u, UNIT_RUNTIME|UNIT_PRIVATE|UNIT_ESCAPE_SPECIFIERS, JsonName, \
|
||||
JsonName "=%s", p->field); \
|
||||
return 0; \
|
||||
}
|
||||
|
||||
DEFINE_APPLY_EXEC_ABSOLUTE_PATH(ipc_namespace_path, "IPCNamespacePath");
|
||||
DEFINE_APPLY_EXEC_ABSOLUTE_PATH(network_namespace_path, "NetworkNamespacePath");
|
||||
DEFINE_APPLY_EXEC_ABSOLUTE_PATH(root_directory, "RootDirectory");
|
||||
DEFINE_APPLY_EXEC_ABSOLUTE_PATH(root_image, "RootImage");
|
||||
DEFINE_APPLY_EXEC_ABSOLUTE_PATH(root_mstack, "RootMStack");
|
||||
DEFINE_APPLY_EXEC_ABSOLUTE_PATH(root_verity, "RootVerity");
|
||||
DEFINE_APPLY_EXEC_ABSOLUTE_PATH(user_namespace_path, "UserNamespacePath");
|
||||
|
||||
/* RootHashPath and RootHashSignaturePath need custom apply fns: the corresponding unit-file
|
||||
* directive is "RootHash="/"RootHashSignature=" (not the JSON name), and setting the path form
|
||||
* must clear the byte-buffer (iovec) form to match the D-Bus semantics. */
|
||||
static int apply_exec_root_hash_path(Unit *u, ExecContext *c, TransientExecContextParameters *p) {
|
||||
int r;
|
||||
|
||||
assert(c);
|
||||
assert(p);
|
||||
|
||||
if (!p->root_hash_path)
|
||||
return 0;
|
||||
|
||||
if (!path_is_absolute(p->root_hash_path))
|
||||
return log_debug_errno(SYNTHETIC_ERRNO(EINVAL),
|
||||
"RootHashPath: expects an absolute path");
|
||||
|
||||
r = free_and_strdup(&c->root_hash_path, p->root_hash_path);
|
||||
if (r < 0)
|
||||
return r;
|
||||
iovec_done(&c->root_hash);
|
||||
|
||||
unit_write_settingf(u, UNIT_RUNTIME|UNIT_PRIVATE|UNIT_ESCAPE_SPECIFIERS, "RootHash",
|
||||
"RootHash=%s", p->root_hash_path);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int apply_exec_root_hash_sig_path(Unit *u, ExecContext *c, TransientExecContextParameters *p) {
|
||||
int r;
|
||||
|
||||
assert(c);
|
||||
assert(p);
|
||||
|
||||
if (!p->root_hash_sig_path)
|
||||
return 0;
|
||||
|
||||
if (!path_is_absolute(p->root_hash_sig_path))
|
||||
return log_debug_errno(SYNTHETIC_ERRNO(EINVAL),
|
||||
"RootHashSignaturePath: expects an absolute path");
|
||||
|
||||
r = free_and_strdup(&c->root_hash_sig_path, p->root_hash_sig_path);
|
||||
if (r < 0)
|
||||
return r;
|
||||
iovec_done(&c->root_hash_sig);
|
||||
|
||||
unit_write_settingf(u, UNIT_RUNTIME|UNIT_PRIVATE|UNIT_ESCAPE_SPECIFIERS, "RootHashSignature",
|
||||
"RootHashSignature=%s", p->root_hash_sig_path);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Per-property descriptor for fields of the Exec context.
|
||||
*
|
||||
* json_name - JSON key inside the Exec object (e.g. "Nice").
|
||||
@@ -1254,9 +1341,20 @@ typedef struct TransientExecProperty {
|
||||
{ json, "Exec." json, SD_JSON_VARIANT_STRING, dispatch_fn, \
|
||||
offsetof(TransientExecContextParameters, field), json_flags, apply_exec_##field }
|
||||
|
||||
/* Absolute-path string property: parsed via sd_json_dispatch_const_string + path_is_absolute(). */
|
||||
#define EXEC_PROPERTY_ABSOLUTE_PATH(json, field) \
|
||||
{ json, "Exec." json, SD_JSON_VARIANT_STRING, sd_json_dispatch_const_string, \
|
||||
offsetof(TransientExecContextParameters, field), 0, apply_exec_##field }
|
||||
|
||||
static const TransientExecProperty exec_properties[] = {
|
||||
EXEC_PROPERTY ("WorkingDirectory", SD_JSON_VARIANT_OBJECT, dispatch_transient_working_directory, 0, 0, apply_exec_working_directory),
|
||||
EXEC_PROPERTY_ABSOLUTE_PATH("RootDirectory", root_directory),
|
||||
EXEC_PROPERTY_ABSOLUTE_PATH("RootImage", root_image),
|
||||
EXEC_PROPERTY_ABSOLUTE_PATH("RootMStack", root_mstack),
|
||||
EXEC_PROPERTY_TRISTATE_BOOL("RootEphemeral", root_ephemeral),
|
||||
EXEC_PROPERTY ("RootHashPath", SD_JSON_VARIANT_STRING, sd_json_dispatch_const_string, offsetof(TransientExecContextParameters, root_hash_path), 0, apply_exec_root_hash_path),
|
||||
EXEC_PROPERTY ("RootHashSignaturePath", SD_JSON_VARIANT_STRING, sd_json_dispatch_const_string, offsetof(TransientExecContextParameters, root_hash_sig_path), 0, apply_exec_root_hash_sig_path),
|
||||
EXEC_PROPERTY_ABSOLUTE_PATH("RootVerity", root_verity),
|
||||
EXEC_PROPERTY_STRING ("User", user, json_dispatch_const_user_group_name, SD_JSON_RELAX),
|
||||
EXEC_PROPERTY_STRING ("Group", group, json_dispatch_const_user_group_name, SD_JSON_RELAX),
|
||||
EXEC_PROPERTY_TRISTATE_BOOL("DynamicUser", dynamic_user),
|
||||
@@ -1266,6 +1364,9 @@ static const TransientExecProperty exec_properties[] = {
|
||||
EXEC_PROPERTY ("OOMScoreAdjust", SD_JSON_VARIANT_INTEGER, dispatch_transient_oom_score_adjust, 0, 0, apply_exec_oom_score_adjust),
|
||||
EXEC_PROPERTY_TRISTATE_BOOL("IgnoreSIGPIPE", ignore_sigpipe),
|
||||
EXEC_PROPERTY ("Nice", SD_JSON_VARIANT_INTEGER, dispatch_transient_nice, 0, 0, apply_exec_nice),
|
||||
EXEC_PROPERTY_ABSOLUTE_PATH("NetworkNamespacePath", network_namespace_path),
|
||||
EXEC_PROPERTY_ABSOLUTE_PATH("IPCNamespacePath", ipc_namespace_path),
|
||||
EXEC_PROPERTY_ABSOLUTE_PATH("UserNamespacePath", user_namespace_path),
|
||||
EXEC_PROPERTY_TRISTATE_BOOL("LockPersonality", lock_personality),
|
||||
EXEC_PROPERTY_TRISTATE_BOOL("MemoryDenyWriteExecute", memory_deny_write_execute),
|
||||
EXEC_PROPERTY_TRISTATE_BOOL("RestrictRealtime", restrict_realtime),
|
||||
@@ -1278,6 +1379,7 @@ static const TransientExecProperty exec_properties[] = {
|
||||
#undef EXEC_PROPERTY
|
||||
#undef EXEC_PROPERTY_TRISTATE_BOOL
|
||||
#undef EXEC_PROPERTY_STRING
|
||||
#undef EXEC_PROPERTY_ABSOLUTE_PATH
|
||||
|
||||
/* Tristate-bool fields default to -1 (= absent). Default-zeroed slots would look "explicitly false"
|
||||
* to the apply path. Initialize all tristate fields here so adding a new tristate property only
|
||||
|
||||
@@ -253,6 +253,17 @@ systemctl show -P UMask varlink-transient-procctl.service | grep '^0022$' >/dev/
|
||||
systemctl show -P NoNewPrivileges varlink-transient-procctl.service | grep '^yes$' >/dev/null
|
||||
systemctl show -P MemoryDenyWriteExecute varlink-transient-procctl.service | grep '^yes$' >/dev/null
|
||||
|
||||
# Exec.RootHashPath / Exec.RootHashSignaturePath: the unit-file directive must be
|
||||
# "RootHash=" / "RootHashSignature=" (not the JSON name), otherwise the next daemon-reload
|
||||
# would drop the setting as an unknown key.
|
||||
defer_transient_cleanup varlink-transient-roothash.service
|
||||
varlinkctl call "$MANAGER_SOCKET" io.systemd.Unit.StartTransient \
|
||||
'{"context":{"ID":"varlink-transient-roothash.service","Exec":{"RootHashPath":"/etc/hostname","RootHashSignaturePath":"/etc/machine-id"},"Service":{"Type":"oneshot","RemainAfterExit":true,"ExecStart":[{"path":"/bin/true"}]}}}' >/dev/null
|
||||
fragment=$(systemctl show -P FragmentPath varlink-transient-roothash.service)
|
||||
test -n "$fragment"
|
||||
grep '^RootHash=/etc/hostname$' "$fragment" >/dev/null
|
||||
grep '^RootHashSignature=/etc/machine-id$' "$fragment" >/dev/null
|
||||
|
||||
# Error cases: verify specific varlink error types
|
||||
set +o pipefail
|
||||
varlinkctl call "$MANAGER_SOCKET" io.systemd.Unit.StartTransient \
|
||||
@@ -303,6 +314,11 @@ defer_transient_cleanup varlink-transient-bad-oom.service
|
||||
expect_invalid_parameter \
|
||||
'{"context":{"ID":"varlink-transient-bad-oom.service","Exec":{"OOMScoreAdjust":9999},"Service":{"Type":"oneshot","ExecStart":[{"path":"/bin/true"}]}}}' \
|
||||
"Exec.OOMScoreAdjust"
|
||||
# Relative RootDirectory path is rejected
|
||||
defer_transient_cleanup varlink-transient-bad-rd.service
|
||||
expect_invalid_parameter \
|
||||
'{"context":{"ID":"varlink-transient-bad-rd.service","Exec":{"RootDirectory":"relative/path"},"Service":{"Type":"oneshot","ExecStart":[{"path":"/bin/true"}]}}}' \
|
||||
"Exec.RootDirectory"
|
||||
# Invalid credential ID
|
||||
defer_transient_cleanup varlink-transient-bad-cred-id.service
|
||||
expect_invalid_parameter \
|
||||
@@ -319,9 +335,9 @@ varlinkctl call "$MANAGER_SOCKET" io.systemd.Unit.StartTransient \
|
||||
# Unknown field in Exec is rejected as PropertyNotSupported
|
||||
defer_transient_cleanup varlink-transient-unknown-exec.service
|
||||
unsupported_exec=$(varlinkctl call "$MANAGER_SOCKET" io.systemd.Unit.StartTransient \
|
||||
'{"context":{"ID":"varlink-transient-unknown-exec.service","Exec":{"RootDirectory":"/tmp"},"Service":{"Type":"oneshot","ExecStart":[{"path":"/bin/true"}]}}}' 2>&1 || true)
|
||||
'{"context":{"ID":"varlink-transient-unknown-exec.service","Exec":{"AmbientCapabilities":["cap_net_raw"]},"Service":{"Type":"oneshot","ExecStart":[{"path":"/bin/true"}]}}}' 2>&1 || true)
|
||||
echo "$unsupported_exec" | grep "io.systemd.Unit.PropertyNotSupported"
|
||||
echo "$unsupported_exec" | grep "Exec.RootDirectory"
|
||||
echo "$unsupported_exec" | grep "Exec.AmbientCapabilities"
|
||||
# Service field declared in the IDL but not yet settable at creation is rejected as PropertyNotSupported,
|
||||
# and the offending sub-property is identified
|
||||
defer_transient_cleanup varlink-transient-unknown-service.service
|
||||
|
||||
Reference in New Issue
Block a user