mirror of
https://github.com/systemd/systemd.git
synced 2024-12-22 17:35:35 +03:00
pid1: add mechanism for conditionalizing units/network/netdev/link based on credentials passed in
This is useful when provisioning systems via nspawn/qemu and running specific services only if specific data is passed into the system.
This commit is contained in:
parent
5eab88a569
commit
4f80cfca5e
@ -395,3 +395,9 @@ in `/etc/credstore/`, `/run/credstore/`,
|
||||
`/usr/lib/credstore/`. `LoadCredentialEncrypted=` will also search
|
||||
`/etc/credstore.encrypted/` and similar directories. These directories are
|
||||
hence a great place to store credentials to load on the system.
|
||||
|
||||
## Conditionalizing Services
|
||||
|
||||
Sometimes it makes sense to conditionalize system services and invoke them only
|
||||
if the right system credential is passed to the system. use the
|
||||
`ConditionCredential=` and `AssertCredential=` unit file settings for that.
|
||||
|
@ -269,6 +269,18 @@
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry id='credential'>
|
||||
<term><varname>Credential=</varname></term>
|
||||
<listitem>
|
||||
<para>Checks whether the specified credential was passed to the
|
||||
<filename>systemd-networkd.service</filename> service. See <ulink
|
||||
url="https://systemd.io/CREDENTIALS">System and Service Credentials</ulink> for details. When
|
||||
prefixed with an exclamation mark (<literal>!</literal>), the result is negated. If an empty
|
||||
string is assigned, the previously assigned value is cleared.
|
||||
</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry id='architecture'>
|
||||
<term><varname>Architecture=</varname></term>
|
||||
<listitem>
|
||||
|
@ -217,6 +217,7 @@
|
||||
<xi:include href="systemd.link.xml" xpointer="virtualization" />
|
||||
<xi:include href="systemd.link.xml" xpointer="kernel-command-line" />
|
||||
<xi:include href="systemd.link.xml" xpointer="kernel-version" />
|
||||
<xi:include href="systemd.link.xml" xpointer="credential" />
|
||||
<xi:include href="systemd.link.xml" xpointer="architecture" />
|
||||
<xi:include href="systemd.link.xml" xpointer="firmware" />
|
||||
</variablelist>
|
||||
|
@ -135,6 +135,7 @@
|
||||
<xi:include href="systemd.link.xml" xpointer="virtualization" />
|
||||
<xi:include href="systemd.link.xml" xpointer="kernel-command-line" />
|
||||
<xi:include href="systemd.link.xml" xpointer="kernel-version" />
|
||||
<xi:include href="systemd.link.xml" xpointer="credential" />
|
||||
<xi:include href="systemd.link.xml" xpointer="architecture" />
|
||||
<xi:include href="systemd.link.xml" xpointer="firmware" />
|
||||
</variablelist>
|
||||
|
@ -1330,6 +1330,19 @@
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><varname>ConditionCredential=</varname></term>
|
||||
|
||||
<listitem><para><varname>ConditionCredential=</varname> may be used to check whether a credential
|
||||
by the specified name was passed into the service manager. See <ulink
|
||||
url="https://systemd.io/CREDENTIALS">System and Service Credentials</ulink> for details about
|
||||
credentials. If used in services for the system service manager this may be used to conditionalize
|
||||
services based on system credentials passed in. If used in services for the per-user service
|
||||
manager this may be used to conditionalize services based on credentials passed into the
|
||||
<filename>unit@.service</filename> service instance belonging to the user. The argument must be a
|
||||
valid credential name.</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><varname>ConditionEnvironment=</varname></term>
|
||||
|
||||
|
@ -332,6 +332,7 @@ Unit.ConditionVirtualization, config_parse_unit_condition_string,
|
||||
Unit.ConditionHost, config_parse_unit_condition_string, CONDITION_HOST, offsetof(Unit, conditions)
|
||||
Unit.ConditionKernelCommandLine, config_parse_unit_condition_string, CONDITION_KERNEL_COMMAND_LINE, offsetof(Unit, conditions)
|
||||
Unit.ConditionKernelVersion, config_parse_unit_condition_string, CONDITION_KERNEL_VERSION, offsetof(Unit, conditions)
|
||||
Unit.ConditionCredential, config_parse_unit_condition_string, CONDITION_CREDENTIAL, offsetof(Unit, conditions)
|
||||
Unit.ConditionSecurity, config_parse_unit_condition_string, CONDITION_SECURITY, offsetof(Unit, conditions)
|
||||
Unit.ConditionCapability, config_parse_unit_condition_string, CONDITION_CAPABILITY, offsetof(Unit, conditions)
|
||||
Unit.ConditionACPower, config_parse_unit_condition_string, CONDITION_AC_POWER, offsetof(Unit, conditions)
|
||||
@ -363,6 +364,7 @@ Unit.AssertVirtualization, config_parse_unit_condition_string,
|
||||
Unit.AssertHost, config_parse_unit_condition_string, CONDITION_HOST, offsetof(Unit, asserts)
|
||||
Unit.AssertKernelCommandLine, config_parse_unit_condition_string, CONDITION_KERNEL_COMMAND_LINE, offsetof(Unit, asserts)
|
||||
Unit.AssertKernelVersion, config_parse_unit_condition_string, CONDITION_KERNEL_VERSION, offsetof(Unit, asserts)
|
||||
Unit.AssertCredential, config_parse_unit_condition_string, CONDITION_CREDENTIAL, offsetof(Unit, asserts)
|
||||
Unit.AssertSecurity, config_parse_unit_condition_string, CONDITION_SECURITY, offsetof(Unit, asserts)
|
||||
Unit.AssertCapability, config_parse_unit_condition_string, CONDITION_CAPABILITY, offsetof(Unit, asserts)
|
||||
Unit.AssertACPower, config_parse_unit_condition_string, CONDITION_AC_POWER, offsetof(Unit, asserts)
|
||||
|
@ -45,6 +45,7 @@ Match.Host, config_parse_net_condition,
|
||||
Match.Virtualization, config_parse_net_condition, CONDITION_VIRTUALIZATION, offsetof(NetDev, conditions)
|
||||
Match.KernelCommandLine, config_parse_net_condition, CONDITION_KERNEL_COMMAND_LINE, offsetof(NetDev, conditions)
|
||||
Match.KernelVersion, config_parse_net_condition, CONDITION_KERNEL_VERSION, offsetof(NetDev, conditions)
|
||||
Match.Credential, config_parse_net_condition, CONDITION_CREDENTIAL, offsetof(NetDev, conditions)
|
||||
Match.Architecture, config_parse_net_condition, CONDITION_ARCHITECTURE, offsetof(NetDev, conditions)
|
||||
Match.Firmware, config_parse_net_condition, CONDITION_FIRMWARE, offsetof(NetDev, conditions)
|
||||
NetDev.Description, config_parse_string, 0, offsetof(NetDev, description)
|
||||
|
@ -62,6 +62,7 @@ Match.Host, config_parse_net_condition,
|
||||
Match.Virtualization, config_parse_net_condition, CONDITION_VIRTUALIZATION, offsetof(Network, conditions)
|
||||
Match.KernelCommandLine, config_parse_net_condition, CONDITION_KERNEL_COMMAND_LINE, offsetof(Network, conditions)
|
||||
Match.KernelVersion, config_parse_net_condition, CONDITION_KERNEL_VERSION, offsetof(Network, conditions)
|
||||
Match.Credential, config_parse_net_condition, CONDITION_CREDENTIAL, offsetof(Network, conditions)
|
||||
Match.Architecture, config_parse_net_condition, CONDITION_ARCHITECTURE, offsetof(Network, conditions)
|
||||
Match.Firmware, config_parse_net_condition, CONDITION_FIRMWARE, offsetof(Network, conditions)
|
||||
Link.MACAddress, config_parse_hw_addr, 0, offsetof(Network, hw_addr)
|
||||
|
@ -22,6 +22,7 @@
|
||||
#include "cgroup-util.h"
|
||||
#include "condition.h"
|
||||
#include "cpu-set-util.h"
|
||||
#include "creds-util.h"
|
||||
#include "efi-api.h"
|
||||
#include "env-file.h"
|
||||
#include "env-util.h"
|
||||
@ -140,6 +141,46 @@ static int condition_test_kernel_command_line(Condition *c, char **env) {
|
||||
return false;
|
||||
}
|
||||
|
||||
static int condition_test_credential(Condition *c, char **env) {
|
||||
int (*gd)(const char **ret);
|
||||
int r;
|
||||
|
||||
assert(c);
|
||||
assert(c->parameter);
|
||||
assert(c->type == CONDITION_CREDENTIAL);
|
||||
|
||||
/* For now we'll do a very simple existance check and are happy with either a regular or an encrypted
|
||||
* credential. Given that we check the syntax of the argument we have the option to later maybe allow
|
||||
* contents checks too without breaking compatibility, but for now let's be minimalistic. */
|
||||
|
||||
if (!credential_name_valid(c->parameter)) /* credentials with invalid names do not exist */
|
||||
return false;
|
||||
|
||||
FOREACH_POINTER(gd, get_credentials_dir, get_encrypted_credentials_dir) {
|
||||
_cleanup_free_ char *j = NULL;
|
||||
const char *cd;
|
||||
|
||||
r = gd(&cd);
|
||||
if (r == -ENXIO) /* no env var set */
|
||||
continue;
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
j = path_join(cd, c->parameter);
|
||||
if (!j)
|
||||
return -ENOMEM;
|
||||
|
||||
if (laccess(j, F_OK) >= 0)
|
||||
return true; /* yay! */
|
||||
if (errno != ENOENT)
|
||||
return -errno;
|
||||
|
||||
/* not found in this dir */
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
typedef enum {
|
||||
/* Listed in order of checking. Note that some comparators are prefixes of others, hence the longest
|
||||
* should be listed first. */
|
||||
@ -1099,6 +1140,7 @@ int condition_test(Condition *c, char **env) {
|
||||
[CONDITION_FILE_IS_EXECUTABLE] = condition_test_file_is_executable,
|
||||
[CONDITION_KERNEL_COMMAND_LINE] = condition_test_kernel_command_line,
|
||||
[CONDITION_KERNEL_VERSION] = condition_test_kernel_version,
|
||||
[CONDITION_CREDENTIAL] = condition_test_credential,
|
||||
[CONDITION_VIRTUALIZATION] = condition_test_virtualization,
|
||||
[CONDITION_SECURITY] = condition_test_security,
|
||||
[CONDITION_CAPABILITY] = condition_test_capability,
|
||||
@ -1218,6 +1260,7 @@ static const char* const condition_type_table[_CONDITION_TYPE_MAX] = {
|
||||
[CONDITION_HOST] = "ConditionHost",
|
||||
[CONDITION_KERNEL_COMMAND_LINE] = "ConditionKernelCommandLine",
|
||||
[CONDITION_KERNEL_VERSION] = "ConditionKernelVersion",
|
||||
[CONDITION_CREDENTIAL] = "ConditionCredential",
|
||||
[CONDITION_SECURITY] = "ConditionSecurity",
|
||||
[CONDITION_CAPABILITY] = "ConditionCapability",
|
||||
[CONDITION_AC_POWER] = "ConditionACPower",
|
||||
@ -1255,6 +1298,7 @@ static const char* const assert_type_table[_CONDITION_TYPE_MAX] = {
|
||||
[CONDITION_HOST] = "AssertHost",
|
||||
[CONDITION_KERNEL_COMMAND_LINE] = "AssertKernelCommandLine",
|
||||
[CONDITION_KERNEL_VERSION] = "AssertKernelVersion",
|
||||
[CONDITION_CREDENTIAL] = "AssertCredential",
|
||||
[CONDITION_SECURITY] = "AssertSecurity",
|
||||
[CONDITION_CAPABILITY] = "AssertCapability",
|
||||
[CONDITION_AC_POWER] = "AssertACPower",
|
||||
|
@ -14,6 +14,7 @@ typedef enum ConditionType {
|
||||
CONDITION_HOST,
|
||||
CONDITION_KERNEL_COMMAND_LINE,
|
||||
CONDITION_KERNEL_VERSION,
|
||||
CONDITION_CREDENTIAL,
|
||||
CONDITION_SECURITY,
|
||||
CONDITION_CAPABILITY,
|
||||
CONDITION_AC_POWER,
|
||||
|
@ -15,7 +15,9 @@
|
||||
#include "condition.h"
|
||||
#include "cpu-set-util.h"
|
||||
#include "efi-loader.h"
|
||||
#include "env-util.h"
|
||||
#include "errno-util.h"
|
||||
#include "fs-util.h"
|
||||
#include "hostname-util.h"
|
||||
#include "id128-util.h"
|
||||
#include "ima-util.h"
|
||||
@ -24,14 +26,17 @@
|
||||
#include "macro.h"
|
||||
#include "nulstr-util.h"
|
||||
#include "os-util.h"
|
||||
#include "path-util.h"
|
||||
#include "process-util.h"
|
||||
#include "psi-util.h"
|
||||
#include "rm-rf.h"
|
||||
#include "selinux-util.h"
|
||||
#include "set.h"
|
||||
#include "smack-util.h"
|
||||
#include "string-util.h"
|
||||
#include "strv.h"
|
||||
#include "tests.h"
|
||||
#include "tmpfile-util.h"
|
||||
#include "tomoyo-util.h"
|
||||
#include "udev-util.h"
|
||||
#include "uid-alloc-range.h"
|
||||
@ -460,6 +465,60 @@ TEST(condition_test_kernel_version) {
|
||||
condition_free(condition);
|
||||
}
|
||||
|
||||
TEST(condition_test_credential) {
|
||||
_cleanup_(rm_rf_physical_and_freep) char *n1 = NULL, *n2 = NULL;
|
||||
_cleanup_free_ char *d1 = NULL, *d2 = NULL, *j = NULL;
|
||||
Condition *condition;
|
||||
|
||||
assert_se(free_and_strdup(&d1, getenv("CREDENTIALS_DIRECTORY")) >= 0);
|
||||
assert_se(free_and_strdup(&d2, getenv("ENCRYPTED_CREDENTIALS_DIRECTORY")) >= 0);
|
||||
|
||||
assert_se(unsetenv("CREDENTIALS_DIRECTORY") >= 0);
|
||||
assert_se(unsetenv("ENCRYPTED_CREDENTIALS_DIRECTORY") >= 0);
|
||||
|
||||
condition = condition_new(CONDITION_CREDENTIAL, "definitelymissing", /* trigger= */ false, /* negate= */ false);
|
||||
assert_se(condition);
|
||||
assert_se(condition_test(condition, environ) == 0);
|
||||
condition_free(condition);
|
||||
|
||||
/* invalid */
|
||||
condition = condition_new(CONDITION_CREDENTIAL, "..", /* trigger= */ false, /* negate= */ false);
|
||||
assert_se(condition);
|
||||
assert_se(condition_test(condition, environ) == 0);
|
||||
condition_free(condition);
|
||||
|
||||
assert_se(mkdtemp_malloc(NULL, &n1) >= 0);
|
||||
assert_se(mkdtemp_malloc(NULL, &n2) >= 0);
|
||||
|
||||
assert_se(setenv("CREDENTIALS_DIRECTORY", n1, /* overwrite= */ true) >= 0);
|
||||
assert_se(setenv("ENCRYPTED_CREDENTIALS_DIRECTORY", n2, /* overwrite= */ true) >= 0);
|
||||
|
||||
condition = condition_new(CONDITION_CREDENTIAL, "stillmissing", /* trigger= */ false, /* negate= */ false);
|
||||
assert_se(condition);
|
||||
assert_se(condition_test(condition, environ) == 0);
|
||||
condition_free(condition);
|
||||
|
||||
assert_se(j = path_join(n1, "existing"));
|
||||
assert_se(touch(j) >= 0);
|
||||
assert_se(j);
|
||||
condition = condition_new(CONDITION_CREDENTIAL, "existing", /* trigger= */ false, /* negate= */ false);
|
||||
assert_se(condition);
|
||||
assert_se(condition_test(condition, environ) > 0);
|
||||
condition_free(condition);
|
||||
free(j);
|
||||
|
||||
assert_se(j = path_join(n2, "existing-encrypted"));
|
||||
assert_se(touch(j) >= 0);
|
||||
assert_se(j);
|
||||
condition = condition_new(CONDITION_CREDENTIAL, "existing-encrypted", /* trigger= */ false, /* negate= */ false);
|
||||
assert_se(condition);
|
||||
assert_se(condition_test(condition, environ) > 0);
|
||||
condition_free(condition);
|
||||
|
||||
assert_se(set_unset_env("CREDENTIALS_DIRECTORY", d1, /* overwrite= */ true) >= 0);
|
||||
assert_se(set_unset_env("ENCRYPTED_CREDENTIALS_DIRECTORY", d2, /* overwrite= */ true) >= 0);
|
||||
}
|
||||
|
||||
#if defined(__i386__) || defined(__x86_64__)
|
||||
TEST(condition_test_cpufeature) {
|
||||
Condition *condition;
|
||||
|
@ -34,6 +34,7 @@ Match.Host, config_parse_net_condition,
|
||||
Match.Virtualization, config_parse_net_condition, CONDITION_VIRTUALIZATION, offsetof(LinkConfig, conditions)
|
||||
Match.KernelCommandLine, config_parse_net_condition, CONDITION_KERNEL_COMMAND_LINE, offsetof(LinkConfig, conditions)
|
||||
Match.KernelVersion, config_parse_net_condition, CONDITION_KERNEL_VERSION, offsetof(LinkConfig, conditions)
|
||||
Match.Credential, config_parse_net_condition, CONDITION_CREDENTIAL, offsetof(LinkConfig, conditions)
|
||||
Match.Architecture, config_parse_net_condition, CONDITION_ARCHITECTURE, offsetof(LinkConfig, conditions)
|
||||
Match.Firmware, config_parse_net_condition, CONDITION_FIRMWARE, offsetof(LinkConfig, conditions)
|
||||
Link.Description, config_parse_string, 0, offsetof(LinkConfig, description)
|
||||
|
@ -58,6 +58,12 @@ if [ "$expected_credential" != "" ] ; then
|
||||
|
||||
# Combine it with a fallback (which should have no effect, given the cred should be passed down)
|
||||
[ "$(systemd-run -p LoadCredential="$expected_credential" -p SetCredential="$expected_credential":zzz --pipe --wait systemd-creds cat "$expected_credential")" = "$expected_value" ]
|
||||
|
||||
# This should succeed
|
||||
systemd-run -p AssertCredential="$expected_credential" -p Type=oneshot true
|
||||
|
||||
# And this should fail
|
||||
systemd-run -p AssertCredential="undefinedcredential" -p Type=oneshot true && { echo 'unexpected success'; exit 1; }
|
||||
fi
|
||||
|
||||
# Verify that the creds are immutable
|
||||
|
Loading…
Reference in New Issue
Block a user