core: add User,Group,SupplementaryGroups,Nice to varlink Unit.StartTransient
This commit adds more writable fields to the io.systemd.Unit.StartTransient varlink method. With this its possible to set: User,Group,SupplementaryGroups,Nice values. Plus tests for them.
This commit is contained in:
+68
-5
@@ -18,12 +18,14 @@
|
||||
#include "manager.h"
|
||||
#include "path-util.h"
|
||||
#include "pidref.h"
|
||||
#include "process-util.h"
|
||||
#include "selinux-access.h"
|
||||
#include "service.h"
|
||||
#include "set.h"
|
||||
#include "strv.h"
|
||||
#include "unit-name.h"
|
||||
#include "unit.h"
|
||||
#include "user-util.h"
|
||||
#include "varlink-automount.h"
|
||||
#include "varlink-cgroup.h"
|
||||
#include "varlink-common.h"
|
||||
@@ -685,6 +687,11 @@ typedef struct TransientExecContextParameters {
|
||||
bool set_credentials_encrypted_set;
|
||||
TransientSetCredential *set_credentials_encrypted;
|
||||
size_t n_set_credentials_encrypted;
|
||||
|
||||
const char *user;
|
||||
const char *group;
|
||||
char **supplementary_groups;
|
||||
int nice; /* INT_MAX means unset */
|
||||
} TransientExecContextParameters;
|
||||
|
||||
static void transient_set_credential_array_free(TransientSetCredential *items, size_t n) {
|
||||
@@ -696,6 +703,7 @@ static void transient_set_credential_array_free(TransientSetCredential *items, s
|
||||
static void transient_exec_context_parameters_done(TransientExecContextParameters *p) {
|
||||
assert(p);
|
||||
strv_free(p->environment);
|
||||
strv_free(p->supplementary_groups);
|
||||
transient_set_credential_array_free(p->set_credentials, p->n_set_credentials);
|
||||
transient_set_credential_array_free(p->set_credentials_encrypted, p->n_set_credentials_encrypted);
|
||||
}
|
||||
@@ -862,12 +870,16 @@ static int dispatch_transient_set_credential_encrypted(const char *name, sd_json
|
||||
}
|
||||
|
||||
static int dispatch_transient_exec_context(const char *name, sd_json_variant *variant, sd_json_dispatch_flags_t flags, void *userdata) {
|
||||
/* Key names compatible with D-Bus property names */
|
||||
/* Key names compatible with D-Bus property names. */
|
||||
static const sd_json_dispatch_field exec_dispatch[] = {
|
||||
{ "WorkingDirectory", SD_JSON_VARIANT_OBJECT, dispatch_transient_working_directory, 0, 0 },
|
||||
{ "Environment", _SD_JSON_VARIANT_TYPE_INVALID, dispatch_transient_environment, 0, 0 },
|
||||
{ "SetCredential", SD_JSON_VARIANT_ARRAY, dispatch_transient_set_credential, 0, 0 },
|
||||
{ "SetCredentialEncrypted", SD_JSON_VARIANT_ARRAY, dispatch_transient_set_credential_encrypted, 0, 0 },
|
||||
{ "Environment", _SD_JSON_VARIANT_TYPE_INVALID, dispatch_transient_environment, 0, 0 },
|
||||
{ "Group", SD_JSON_VARIANT_STRING, json_dispatch_const_user_group_name, offsetof(TransientExecContextParameters, group), SD_JSON_RELAX },
|
||||
{ "Nice", SD_JSON_VARIANT_INTEGER, sd_json_dispatch_int32, offsetof(TransientExecContextParameters, nice), 0 },
|
||||
{ "SetCredential", SD_JSON_VARIANT_ARRAY, dispatch_transient_set_credential, 0, 0 },
|
||||
{ "SetCredentialEncrypted", SD_JSON_VARIANT_ARRAY, dispatch_transient_set_credential_encrypted, 0, 0 },
|
||||
{ "SupplementaryGroups", SD_JSON_VARIANT_ARRAY, sd_json_dispatch_strv, offsetof(TransientExecContextParameters, supplementary_groups), 0 },
|
||||
{ "User", SD_JSON_VARIANT_STRING, json_dispatch_const_user_group_name, offsetof(TransientExecContextParameters, user), SD_JSON_RELAX },
|
||||
{ "WorkingDirectory", SD_JSON_VARIANT_OBJECT, dispatch_transient_working_directory, 0, 0 },
|
||||
{}
|
||||
};
|
||||
|
||||
@@ -1026,6 +1038,56 @@ static int transient_exec_context_apply_properties(Unit *u, ExecContext *c, Tran
|
||||
return r;
|
||||
}
|
||||
|
||||
/* User/Group names already validated by json_dispatch_const_user_group_name() in the dispatch table:
|
||||
* either NULL or a non-empty validated name. */
|
||||
if (p->user) {
|
||||
r = free_and_strdup(&c->user, p->user);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
unit_write_settingf(u, UNIT_RUNTIME|UNIT_PRIVATE|UNIT_ESCAPE_SPECIFIERS, "User",
|
||||
"User=%s", p->user);
|
||||
}
|
||||
|
||||
if (p->group) {
|
||||
r = free_and_strdup(&c->group, p->group);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
unit_write_settingf(u, UNIT_RUNTIME|UNIT_PRIVATE|UNIT_ESCAPE_SPECIFIERS, "Group",
|
||||
"Group=%s", p->group);
|
||||
}
|
||||
|
||||
if (p->supplementary_groups) {
|
||||
_cleanup_free_ char *joined = NULL;
|
||||
|
||||
STRV_FOREACH(g, p->supplementary_groups)
|
||||
if (!isempty(*g) && !valid_user_group_name(*g, VALID_USER_ALLOW_NUMERIC|VALID_USER_RELAX))
|
||||
return log_debug_errno(SYNTHETIC_ERRNO(EINVAL),
|
||||
"Invalid supplementary group name: %s", *g);
|
||||
|
||||
r = strv_extend_strv(&c->supplementary_groups, p->supplementary_groups, /* filter_duplicates= */ true);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
joined = strv_join(p->supplementary_groups, " ");
|
||||
if (!joined)
|
||||
return -ENOMEM;
|
||||
|
||||
unit_write_settingf(u, UNIT_RUNTIME|UNIT_PRIVATE|UNIT_ESCAPE_SPECIFIERS, "SupplementaryGroups",
|
||||
"SupplementaryGroups=%s", joined);
|
||||
}
|
||||
|
||||
if (p->nice != INT_MAX) {
|
||||
if (!nice_is_valid(p->nice))
|
||||
return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid Nice= value: %i", p->nice);
|
||||
|
||||
c->nice = p->nice;
|
||||
c->nice_set = true;
|
||||
|
||||
unit_write_settingf(u, UNIT_RUNTIME|UNIT_PRIVATE, "Nice", "Nice=%i", p->nice);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -1125,6 +1187,7 @@ int vl_method_start_transient_unit(sd_varlink *link, sd_json_variant *parameters
|
||||
.notify_unit_changes = -1,
|
||||
.context.service.type = _SERVICE_TYPE_INVALID,
|
||||
.context.service.remain_after_exit = -1,
|
||||
.context.exec.nice = INT_MAX,
|
||||
};
|
||||
Manager *manager = ASSERT_PTR(userdata);
|
||||
const char *bad_field = NULL;
|
||||
|
||||
@@ -656,6 +656,26 @@ timeout 30 bash -c "until systemctl is-active varlink-transient-cred.service; do
|
||||
grep '^secret-value$' "$CRED_OUTPUT" >/dev/null
|
||||
rm -f "$CRED_OUTPUT"
|
||||
|
||||
# Exec.User, Exec.Group, Exec.SupplementaryGroups, Exec.Nice
|
||||
# The nobody group is different on different distros so resolve here.
|
||||
NOBODY_GROUP=$(id -gn nobody)
|
||||
defer_transient_cleanup varlink-transient-ids.service
|
||||
ids_payload=$(jq -cn --arg g "$NOBODY_GROUP" \
|
||||
'{context:{ID:"varlink-transient-ids.service",
|
||||
Exec:{User:"nobody",Group:$g,SupplementaryGroups:[$g],Nice:5},
|
||||
Service:{Type:"oneshot",RemainAfterExit:true,
|
||||
ExecStart:[{path:"/bin/true"}]}}}')
|
||||
result=$(varlinkctl call "$MANAGER_SOCKET" io.systemd.Unit.StartTransient "$ids_payload")
|
||||
echo "$result" | jq -e '.context.Exec.User == "nobody"'
|
||||
echo "$result" | jq --arg g "$NOBODY_GROUP" -e '.context.Exec.Group == $g'
|
||||
echo "$result" | jq --arg g "$NOBODY_GROUP" -e '.context.Exec.SupplementaryGroups == [$g]'
|
||||
echo "$result" | jq -e '.context.Exec.Nice == 5'
|
||||
timeout 30 bash -c 'until systemctl is-active varlink-transient-ids.service; do sleep 0.5; done'
|
||||
systemctl show -P User varlink-transient-ids.service | grep '^nobody$' >/dev/null
|
||||
systemctl show -P Group varlink-transient-ids.service | grep "^${NOBODY_GROUP}$" >/dev/null
|
||||
systemctl show -P SupplementaryGroups varlink-transient-ids.service | grep "${NOBODY_GROUP}" >/dev/null
|
||||
systemctl show -P Nice varlink-transient-ids.service | grep '^5$' >/dev/null
|
||||
|
||||
# Error cases: verify specific varlink error types
|
||||
set +o pipefail
|
||||
varlinkctl call "$MANAGER_SOCKET" io.systemd.Unit.StartTransient \
|
||||
@@ -677,6 +697,14 @@ varlinkctl call "$MANAGER_SOCKET" io.systemd.Unit.StartTransient \
|
||||
defer_transient_cleanup varlink-transient-bad-env.service
|
||||
varlinkctl call "$MANAGER_SOCKET" io.systemd.Unit.StartTransient \
|
||||
'{"context":{"ID":"varlink-transient-bad-env.service","Exec":{"Environment":["not_an_env_var"]},"Service":{"Type":"oneshot","ExecStart":[{"path":"/bin/true"}]}}}' |& grep "io.systemd.Unit.BadUnitSetting"
|
||||
# Invalid User= name is rejected at JSON dispatch time as a parameter error
|
||||
defer_transient_cleanup varlink-transient-bad-user.service
|
||||
varlinkctl call "$MANAGER_SOCKET" io.systemd.Unit.StartTransient \
|
||||
'{"context":{"ID":"varlink-transient-bad-user.service","Exec":{"User":"bad/user"},"Service":{"Type":"oneshot","ExecStart":[{"path":"/bin/true"}]}}}' |& grep "Invalid argument"
|
||||
# Out-of-range Nice= value is rejected
|
||||
defer_transient_cleanup varlink-transient-bad-nice.service
|
||||
varlinkctl call "$MANAGER_SOCKET" io.systemd.Unit.StartTransient \
|
||||
'{"context":{"ID":"varlink-transient-bad-nice.service","Exec":{"Nice":100},"Service":{"Type":"oneshot","ExecStart":[{"path":"/bin/true"}]}}}' |& grep "io.systemd.Unit.BadUnitSetting"
|
||||
# Invalid credential ID
|
||||
defer_transient_cleanup varlink-transient-bad-cred-id.service
|
||||
varlinkctl call "$MANAGER_SOCKET" io.systemd.Unit.StartTransient \
|
||||
|
||||
Reference in New Issue
Block a user