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:
commit
8d0c34f367
@ -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>
|
||||
|
||||
|
@ -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>,
|
||||
|
@ -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);
|
||||
|
@ -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"
|
||||
|
@ -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=""
|
||||
|
@ -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
17
tmpfiles.d/provision.conf
Normal 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
|
@ -20,3 +20,4 @@ Type=oneshot
|
||||
ExecStart=systemd-tmpfiles --clean
|
||||
SuccessExitStatus=DATAERR
|
||||
IOSchedulingClass=idle
|
||||
LoadCredential=tmpfiles.extra
|
||||
|
@ -20,3 +20,4 @@ Type=oneshot
|
||||
RemainAfterExit=yes
|
||||
ExecStart=systemd-tmpfiles --prefix=/dev --create --boot
|
||||
SuccessExitStatus=DATAERR CANTCREAT
|
||||
LoadCredential=tmpfiles.extra
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user