core: add SetCredentials{,Encrypted} to varlink Unit.StartTransient
This commit adds support to set `SetCredentials` and `SetCredentialsEncrypted` in the `io.systemd.Unit.StartTransient` varlink call.
This commit is contained in:
+118
-2
@@ -11,6 +11,7 @@
|
||||
#include "execute.h"
|
||||
#include "format-util.h"
|
||||
#include "install.h"
|
||||
#include "iovec-util.h"
|
||||
#include "job.h"
|
||||
#include "json-util.h"
|
||||
#include "locale-util.h"
|
||||
@@ -708,17 +709,38 @@ typedef struct TransientWorkingDirectory {
|
||||
bool missing_ok;
|
||||
} TransientWorkingDirectory;
|
||||
|
||||
typedef struct TransientSetCredential {
|
||||
const char *id;
|
||||
struct iovec value;
|
||||
} TransientSetCredential;
|
||||
|
||||
typedef struct TransientExecContextParameters {
|
||||
bool present;
|
||||
bool working_directory_set;
|
||||
TransientWorkingDirectory working_directory;
|
||||
bool environment_set;
|
||||
char **environment;
|
||||
|
||||
bool set_credentials_set;
|
||||
TransientSetCredential *set_credentials;
|
||||
size_t n_set_credentials;
|
||||
|
||||
bool set_credentials_encrypted_set;
|
||||
TransientSetCredential *set_credentials_encrypted;
|
||||
size_t n_set_credentials_encrypted;
|
||||
} TransientExecContextParameters;
|
||||
|
||||
static void transient_set_credential_array_free(TransientSetCredential *items, size_t n) {
|
||||
FOREACH_ARRAY(item, items, n)
|
||||
iovec_done_erase(&item->value);
|
||||
free(items);
|
||||
}
|
||||
|
||||
static void transient_exec_context_parameters_done(TransientExecContextParameters *p) {
|
||||
assert(p);
|
||||
strv_free(p->environment);
|
||||
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);
|
||||
}
|
||||
|
||||
typedef struct TransientServiceParameters {
|
||||
@@ -827,11 +849,67 @@ static int dispatch_transient_environment(const char *name, sd_json_variant *var
|
||||
return sd_json_dispatch_strv(name, variant, flags, &p->environment);
|
||||
}
|
||||
|
||||
static int dispatch_transient_set_credential_array(
|
||||
sd_json_variant *variant,
|
||||
TransientSetCredential **ret_items,
|
||||
size_t *ret_n) {
|
||||
|
||||
static const sd_json_dispatch_field item_dispatch[] = {
|
||||
{ "id", SD_JSON_VARIANT_STRING, sd_json_dispatch_const_string, offsetof(TransientSetCredential, id), SD_JSON_MANDATORY },
|
||||
{ "value", SD_JSON_VARIANT_STRING, json_dispatch_unbase64_iovec, offsetof(TransientSetCredential, value), SD_JSON_MANDATORY },
|
||||
{}
|
||||
};
|
||||
|
||||
TransientSetCredential *items = NULL;
|
||||
size_t n = 0;
|
||||
int r;
|
||||
|
||||
assert(ret_items);
|
||||
assert(ret_n);
|
||||
|
||||
CLEANUP_ARRAY(items, n, transient_set_credential_array_free);
|
||||
|
||||
size_t n_items = sd_json_variant_elements(variant);
|
||||
if (n_items == 0) {
|
||||
*ret_items = NULL;
|
||||
*ret_n = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
items = new0(TransientSetCredential, n_items);
|
||||
if (!items)
|
||||
return -ENOMEM;
|
||||
|
||||
for (; n < n_items; n++) {
|
||||
r = sd_json_dispatch(sd_json_variant_by_index(variant, n), item_dispatch, /* flags= */ 0, &items[n]);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
*ret_n = n;
|
||||
*ret_items = TAKE_PTR(items);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dispatch_transient_set_credential(const char *name, sd_json_variant *variant, sd_json_dispatch_flags_t flags, void *userdata) {
|
||||
TransientExecContextParameters *p = ASSERT_PTR(userdata);
|
||||
p->set_credentials_set = true;
|
||||
return dispatch_transient_set_credential_array(variant, &p->set_credentials, &p->n_set_credentials);
|
||||
}
|
||||
|
||||
static int dispatch_transient_set_credential_encrypted(const char *name, sd_json_variant *variant, sd_json_dispatch_flags_t flags, void *userdata) {
|
||||
TransientExecContextParameters *p = ASSERT_PTR(userdata);
|
||||
p->set_credentials_encrypted_set = true;
|
||||
return dispatch_transient_set_credential_array(variant, &p->set_credentials_encrypted, &p->n_set_credentials_encrypted);
|
||||
}
|
||||
|
||||
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 */
|
||||
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 },
|
||||
{ "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 },
|
||||
{}
|
||||
};
|
||||
|
||||
@@ -900,6 +978,32 @@ static int transient_unit_apply_properties(Unit *u, StartTransientContextParamet
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int transient_apply_set_credentials(
|
||||
Unit *u,
|
||||
ExecContext *c,
|
||||
const TransientSetCredential *items,
|
||||
size_t n_items,
|
||||
bool encrypted) {
|
||||
|
||||
int r;
|
||||
|
||||
assert(u);
|
||||
assert(c);
|
||||
|
||||
FOREACH_ARRAY(item, items, n_items) {
|
||||
const char *err = NULL;
|
||||
|
||||
r = exec_context_apply_set_credential(u, c, item->id, item->value.iov_base, item->value.iov_len,
|
||||
encrypted, UNIT_RUNTIME|UNIT_PRIVATE, &err);
|
||||
if (r == -EINVAL)
|
||||
return log_debug_errno(r, "%s: %s", err, item->id);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int transient_exec_context_apply_properties(Unit *u, ExecContext *c, TransientExecContextParameters *p) {
|
||||
int r;
|
||||
|
||||
@@ -950,6 +1054,18 @@ static int transient_exec_context_apply_properties(Unit *u, ExecContext *c, Tran
|
||||
return r;
|
||||
}
|
||||
|
||||
if (p->set_credentials_set) {
|
||||
r = transient_apply_set_credentials(u, c, p->set_credentials, p->n_set_credentials, /* encrypted= */ false);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
if (p->set_credentials_encrypted_set) {
|
||||
r = transient_apply_set_credentials(u, c, p->set_credentials_encrypted, p->n_set_credentials_encrypted, /* encrypted= */ true);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -1184,9 +1184,10 @@ static SD_VARLINK_DEFINE_STRUCT_TYPE(
|
||||
SD_VARLINK_DEFINE_FIELD(parameter, SD_VARLINK_STRING, SD_VARLINK_NULLABLE));
|
||||
|
||||
/* UnitContext is used both as input to StartTransient (subset settable at creation time: ID,
|
||||
* Description, Service, and the Exec subset {WorkingDirectory, Environment}) and as output from
|
||||
* List/StartTransient (full unit configuration). Fields that are not settable at creation time are
|
||||
* rejected with PropertyNotSupported when supplied as input. */
|
||||
* Description, Service, and the Exec subset {WorkingDirectory, Environment, SetCredential,
|
||||
* SetCredentialEncrypted}) and as output from List/StartTransient (full unit configuration). Fields
|
||||
* that are not settable at creation time are rejected with PropertyNotSupported when supplied as
|
||||
* input. */
|
||||
static SD_VARLINK_DEFINE_STRUCT_TYPE(
|
||||
UnitContext,
|
||||
SD_VARLINK_FIELD_COMMENT("The unit type"),
|
||||
|
||||
@@ -646,6 +646,16 @@ varlinkctl call "$MANAGER_SOCKET" io.systemd.Unit.StartTransient \
|
||||
timeout 30 bash -c 'until systemctl is-active varlink-transient-wd-home.service; do sleep 0.5; done'
|
||||
systemctl show -P WorkingDirectory varlink-transient-wd-home.service | grep '^~$' >/dev/null
|
||||
|
||||
# Exec.SetCredential: pass a credential and verify the running process can read it
|
||||
defer_transient_cleanup varlink-transient-cred.service
|
||||
CRED_VALUE_B64=$(printf 'secret-value' | base64 -w0)
|
||||
CRED_OUTPUT=$(mktemp)
|
||||
varlinkctl call "$MANAGER_SOCKET" io.systemd.Unit.StartTransient \
|
||||
"{\"context\":{\"ID\":\"varlink-transient-cred.service\",\"Exec\":{\"SetCredential\":[{\"id\":\"mycred\",\"value\":\"${CRED_VALUE_B64}\"}]},\"Service\":{\"Type\":\"oneshot\",\"RemainAfterExit\":true,\"ExecStart\":[{\"path\":\"/bin/sh\",\"arguments\":[\"/bin/sh\",\"-c\",\"cat \$CREDENTIALS_DIRECTORY/mycred > ${CRED_OUTPUT}\"]}]}}}"
|
||||
timeout 30 bash -c "until systemctl is-active varlink-transient-cred.service; do sleep 0.5; done"
|
||||
grep '^secret-value$' "$CRED_OUTPUT" >/dev/null
|
||||
rm -f "$CRED_OUTPUT"
|
||||
|
||||
# Error cases: verify specific varlink error types
|
||||
set +o pipefail
|
||||
varlinkctl call "$MANAGER_SOCKET" io.systemd.Unit.StartTransient \
|
||||
@@ -667,7 +677,15 @@ 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"
|
||||
# Exec on a unit type without an exec context (.target) is rejected
|
||||
# Invalid credential ID
|
||||
defer_transient_cleanup varlink-transient-bad-cred-id.service
|
||||
varlinkctl call "$MANAGER_SOCKET" io.systemd.Unit.StartTransient \
|
||||
'{"context":{"ID":"varlink-transient-bad-cred-id.service","Exec":{"SetCredential":[{"id":"bad/id","value":"YWJj"}]},"Service":{"Type":"oneshot","ExecStart":[{"path":"/bin/true"}]}}}' |& grep "io.systemd.Unit.BadUnitSetting"
|
||||
# Invalid base64 value for credential (rejected at JSON dispatch time as a parameter error)
|
||||
defer_transient_cleanup varlink-transient-bad-cred-value.service
|
||||
varlinkctl call "$MANAGER_SOCKET" io.systemd.Unit.StartTransient \
|
||||
'{"context":{"ID":"varlink-transient-bad-cred-value.service","Exec":{"SetCredential":[{"id":"mycred","value":"!!!not_base64!!!"}]},"Service":{"Type":"oneshot","ExecStart":[{"path":"/bin/true"}]}}}' |& grep "Invalid argument"
|
||||
# Exec on a unit type without an exec context (.slice) is rejected
|
||||
varlinkctl call "$MANAGER_SOCKET" io.systemd.Unit.StartTransient \
|
||||
'{"context":{"ID":"varlink-transient-exec.slice","Exec":{"WorkingDirectory":{"path":"/tmp","missingOK":false}}}}' |& grep "io.systemd.Unit.UnitTypeNotSupported"
|
||||
# Unknown field in Exec is rejected as PropertyNotSupported
|
||||
|
||||
Reference in New Issue
Block a user