1
1
mirror of https://github.com/systemd/systemd-stable.git synced 2024-12-23 17:34:00 +03:00

Merge pull request #24014 from poettering/tmpfiles-extra-creds

tmpfiles: accept additional tmpfiles lines via credential
This commit is contained in:
Luca Boccassi 2022-07-21 11:55:43 +01:00 committed by GitHub
commit 8d0c34f367
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 162 additions and 18 deletions

View File

@ -230,6 +230,31 @@
<programlisting>systemd-tmpfiles --remove --create</programlisting>
</refsect1>
<refsect1>
<title>Credentials</title>
<para><command>systemd-tmpfiles</command> supports the service credentials logic as implemented by
<varname>LoadCredential=</varname>/<varname>SetCredential=</varname> (see
<citerefentry><refentrytitle>systemd.exec</refentrytitle><manvolnum>1</manvolnum></citerefentry> for
details). The following credentials are used when passed in:</para>
<variablelist>
<varlistentry>
<term><literal>tmpfiles.extra</literal></term>
<listitem><para> The contents of this credential may contain additional lines to operate on. The
credential contents should follow the same format as any other <filename>tmpfiles.d/</filename>
drop-in configuration file. If this credential is passed it is processed after all of the drop-in
files read from the file system. The lines in the credential can hence augment existing lines of the
OS, but not override them.</para></listitem>
</varlistentry>
</variablelist>
<para>Note that by default the <filename>systemd-tmpfiles-setup.service</filename> unit file (and related
unit files) is set up to inherit the <literal>tmpfiles.extra</literal> credential from the service
manager.</para>
</refsect1>
<refsect1>
<title>Environment</title>

View File

@ -160,9 +160,10 @@ L /tmp/foobar - - - - /dev/null</programlisting>
<refsect2>
<title>Type</title>
<para>The type consists of a single letter and optionally a plus sign (<literal>+</literal>),
exclamation mark (<literal>!</literal>), minus sign (<literal>-</literal>), equals sign
(<literal>=</literal>) and/or tilde character (<literal>~</literal>).</para>
<para>The type consists of a single letter and optionally one or emore modifier characters: a plus sign
(<literal>+</literal>), exclamation mark (<literal>!</literal>), minus sign (<literal>-</literal>),
equals sign (<literal>=</literal>), tilde character (<literal>~</literal>) and/or caret
(<literal>^</literal>).</para>
<para>The following line types are understood:</para>
@ -461,6 +462,10 @@ L /tmp/foobar - - - - /dev/null</programlisting>
symlinks.</para></listitem>
</varlistentry>
</variablelist>
</refsect2>
<refsect2>
<title>Type Modifiers</title>
<para>If the exclamation mark (<literal>!</literal>) is used, this line is only safe to execute during
boot, and can break a running system. Lines without the exclamation mark are presumed to be safe to
@ -493,9 +498,19 @@ w- /proc/sys/vm/swappiness - - - - 10</programlisting></para>
<para>If the tilde character (<literal>~</literal>) is used, the argument (i.e. 6th) column is <ulink
url="https://www.rfc-editor.org/rfc/rfc4648.html">Base64 decoded</ulink> before use. This modifier is
only supported on line types that can write file contents, i.e. <varname>f</varname>,
<varname>f+</varname>, <varname>w</varname>. This is useful for writing arbitrary binary data
(including newlines and NUL bytes) to files. Note that if this switch is used, the argument is not
subject to specifier expansion, neither before nor after Base64 decoding.</para>
<varname>f+</varname>, <varname>w</varname>, <varname>+</varname>. This is useful for writing arbitrary
binary data (including newlines and NUL bytes) to files. Note that if this switch is used, the argument
is not subject to specifier expansion, neither before nor after Base64 decoding.</para>
<para>If the caret character (<literal>^</literal>) is used, the argument (i.e. 6th) column takes a
service credential name to read the argument data from. See <ulink
url="https://systemd.io/CREDENTIALS">System and Service Credentials</ulink> for details about the
credentials concept. This modifier is only supported on line types that can write file contents,
i.e. <varname>f</varname>, <varname>f+</varname>, <varname>w</varname>, <varname>w+</varname>. This is
useful for writing arbitrary files with contents sourced from elsewhere, including from VM or container
managers further up. If the specified credential is not set for the <command>systemd-tmpfiles</command>
service, the line is silently skipped. If <literal>^</literal> and <literal>~</literal> are combined
Base64 decoding is applied to the credential contents.</para>
<para>Note that for all line types that result in creation of any kind of file node
(i.e. <varname>f</varname>/<varname>F</varname>,

View File

@ -25,6 +25,7 @@
#include "chattr-util.h"
#include "conf-files.h"
#include "copy.h"
#include "creds-util.h"
#include "def.h"
#include "devnum-util.h"
#include "dirent-util.h"
@ -2977,7 +2978,7 @@ static int parse_line(
ItemArray *existing;
OrderedHashmap *h;
int r, pos;
bool append_or_force = false, boot = false, allow_failure = false, try_replace = false, unbase64 = false;
bool append_or_force = false, boot = false, allow_failure = false, try_replace = false, unbase64 = false, from_cred = false;
assert(fname);
assert(line >= 1);
@ -3050,6 +3051,8 @@ static int parse_line(
try_replace = true;
else if (action[pos] == '~' && !unbase64)
unbase64 = true;
else if (action[pos] == '^' && !from_cred)
from_cred = true;
else {
*invalid_config = true;
return log_syntax(NULL, LOG_ERR, fname, line, SYNTHETIC_ERRNO(EBADMSG), "Unknown modifiers in command '%s'", action);
@ -3239,13 +3242,8 @@ static int parse_line(
if (!should_include_path(i.path))
return 0;
if (unbase64) {
if (i.argument) {
r = unbase64mem(i.argument, SIZE_MAX, &i.binary_argument, &i.binary_argument_size);
if (r < 0)
return log_syntax(NULL, LOG_ERR, fname, line, r, "Failed to base64 decode specified argument '%s': %m", i.argument);
}
} else {
if (!unbase64) {
/* Do specifier expansion except if base64 mode is enabled */
r = specifier_expansion_from_arg(specifier_table, &i);
if (r == -ENXIO)
return log_unresolvable_specifier(fname, line);
@ -3256,6 +3254,35 @@ static int parse_line(
}
}
if (from_cred) {
if (!i.argument)
return log_syntax(NULL, LOG_ERR, fname, line, SYNTHETIC_ERRNO(EINVAL), "Reading from credential requested, but no credential name specified.");
if (!credential_name_valid(i.argument))
return log_syntax(NULL, LOG_ERR, fname, line, SYNTHETIC_ERRNO(EINVAL), "Credential name not valid: %s", i.argument);
r = read_credential(i.argument, &i.binary_argument, &i.binary_argument_size);
if (IN_SET(r, -ENXIO, -ENOENT)) {
/* Silently skip over lines that have no credentials passed */
log_syntax(NULL, LOG_INFO, fname, line, 0, "Credential '%s' not specified, skipping line.", i.argument);
return 0;
}
if (r < 0)
return log_error_errno(r, "Failed to read credential '%s': %m", i.argument);
}
/* If base64 decoding is requested, do so now */
if (unbase64 && item_binary_argument(&i)) {
_cleanup_free_ void *data = NULL;
size_t data_size = 0;
r = unbase64mem(item_binary_argument(&i), item_binary_argument_size(&i), &data, &data_size);
if (r < 0)
return log_syntax(NULL, LOG_ERR, fname, line, r, "Failed to base64 decode specified argument '%s': %m", i.argument);
free_and_replace(i.binary_argument, data);
i.binary_argument_size = data_size;
}
if (!empty_or_root(arg_root)) {
char *p;
@ -3594,7 +3621,12 @@ static int parse_argv(int argc, char *argv[]) {
return 1;
}
static int read_config_file(char **config_dirs, const char *fn, bool ignore_enoent, bool *invalid_config) {
static int read_config_file(
char **config_dirs,
const char *fn,
bool ignore_enoent,
bool *invalid_config) {
_cleanup_(hashmap_freep) Hashmap *uid_cache = NULL, *gid_cache = NULL;
_cleanup_fclose_ FILE *_f = NULL;
_cleanup_free_ char *pp = NULL;
@ -3736,6 +3768,25 @@ static int read_config_files(char **config_dirs, char **args, bool *invalid_conf
return 0;
}
static int read_credential_lines(bool *invalid_config) {
_cleanup_free_ char *j = NULL;
const char *d;
int r;
r = get_credentials_dir(&d);
if (r == -ENXIO)
return 0;
if (r < 0)
return log_error_errno(r, "Failed to get credentials directory: %m");
j = path_join(d, "tmpfiles.extra");
if (!j)
return log_oom();
(void) read_config_file(/* config_dirs= */ NULL, j, /* ignore_enoent= */ true, invalid_config);
return 0;
}
static int link_parent(ItemArray *a) {
const char *path;
char *prefix;
@ -3892,6 +3943,10 @@ static int run(int argc, char *argv[]) {
if (r < 0)
return r;
r = read_credential_lines(&invalid_config);
if (r < 0)
return r;
/* Let's now link up all child/parent relationships */
ORDERED_HASHMAP_FOREACH(a, items) {
r = link_parent(a);

View File

@ -3,9 +3,29 @@
set -e
TEST_DESCRIPTION="test credentials"
NSPAWN_ARGUMENTS="${NSPAWN_ARGUMENTS:-} --set-credential=mynspawncredential:strangevalue"
QEMU_OPTIONS="${QEMU_OPTIONS:-} -fw_cfg name=opt/io.systemd.credentials/myqemucredential,string=othervalue -smbios type=11,value=io.systemd.credential:smbioscredential=magicdata -smbios type=11,value=io.systemd.credential.binary:binarysmbioscredential=bWFnaWNiaW5hcnlkYXRh -smbios type=11,value=io.systemd.credential.binary:sysusers.extra=dSBjcmVkdGVzdHVzZXIK"
KERNEL_APPEND="${KERNEL_APPEND:-} systemd.set_credential=kernelcmdlinecred:uff systemd.set_credential=sysctl.extra:kernel.domainname=sysctltest rd.systemd.import_credentials=no"
NSPAWN_CREDS=(
"--set-credential=mynspawncredential:strangevalue"
)
NSPAWN_ARGUMENTS="${NSPAWN_ARGUMENTS:-} ${NSPAWN_CREDS[*]}"
QEMU_CREDS=(
"-fw_cfg name=opt/io.systemd.credentials/myqemucredential,string=othervalue"
"-smbios type=11,value=io.systemd.credential:smbioscredential=magicdata"
"-smbios type=11,value=io.systemd.credential.binary:binarysmbioscredential=bWFnaWNiaW5hcnlkYXRh"
"-smbios type=11,value=io.systemd.credential.binary:sysusers.extra=dSBjcmVkdGVzdHVzZXIK"
"-smbios type=11,value=io.systemd.credential.binary:tmpfiles.extra=ZiAvdG1wL3NvdXJjZWRmcm9tY3JlZGVudGlhbCAtIC0gLSAtIHRtcGZpbGVzc2VjcmV0Cg=="
)
QEMU_OPTIONS="${QEMU_OPTIONS:-} ${QEMU_CREDS[*]}"
KERNEL_CREDS=(
"systemd.set_credential=kernelcmdlinecred:uff"
"systemd.set_credential=sysctl.extra:kernel.domainname=sysctltest"
"systemd.set_credential=login.motd:hello"
"systemd.set_credential=login.issue:welcome"
"rd.systemd.import_credentials=no"
)
KERNEL_APPEND="${KERNEL_APPEND:-} ${KERNEL_CREDS[*]}"
# shellcheck source=test/test-functions
. "${TEST_BASE_DIR:?}/test-functions"

View File

@ -43,6 +43,11 @@ elif [ -d /sys/firmware/qemu_fw_cfg/by_name ]; then
# Verify that creating a user via sysusers via the kernel cmdline worked
grep -q ^credtestuser: /etc/passwd
# Verify that writing a file via tmpfiles worked
[ "$(cat /tmp/sourcedfromcredential)" = "tmpfilessecret" ]
[ "$(cat /etc/motd.d/50-provision.conf)" = "hello" ]
[ "$(cat /etc/issue.d/50-provision.conf)" = "welcome" ]
else
echo "qemu_fw_cfg support missing in kernel. Sniff!"
expected_credential=""

View File

@ -14,6 +14,7 @@ files = [['README', ''],
['systemd-tmp.conf', ''],
['tmp.conf', ''],
['x11.conf', ''],
['provision.conf', ''],
]
foreach pair : files

17
tmpfiles.d/provision.conf Normal file
View File

@ -0,0 +1,17 @@
# This file is part of systemd.
#
# systemd is free software; you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation; either version 2.1 of the License, or
# (at your option) any later version.
# See tmpfiles.d(5) for details
# Provision additional loging messages from credentials, if they are set. Note
# that these lines are NOPs if the credentials are not set or if the files
# already exist.
f^ /etc/motd.d/50-provision.conf - - - - login.motd
f^ /etc/issue.d/50-provision.conf - - - - login.issue
# Provision a /etc/hosts file from credentials.
f^ /etc/hosts - - - - network.hosts

View File

@ -20,3 +20,4 @@ Type=oneshot
ExecStart=systemd-tmpfiles --clean
SuccessExitStatus=DATAERR
IOSchedulingClass=idle
LoadCredential=tmpfiles.extra

View File

@ -20,3 +20,4 @@ Type=oneshot
RemainAfterExit=yes
ExecStart=systemd-tmpfiles --prefix=/dev --create --boot
SuccessExitStatus=DATAERR CANTCREAT
LoadCredential=tmpfiles.extra

View File

@ -21,3 +21,7 @@ Type=oneshot
RemainAfterExit=yes
ExecStart=systemd-tmpfiles --create --remove --boot --exclude-prefix=/dev
SuccessExitStatus=DATAERR CANTCREAT
LoadCredential=tmpfiles.extra
LoadCredential=login.motd
LoadCredential=login.issue
LoadCredential=network.hosts