This commit adds a new `systemd.credentials_boot_policy=` kernel commandline that allows to control if credentials with a `null` key are accepted. The possible options are: * strict: always insist on tpm encryption * tofu: allow null encryption in firstboot mode and when no tpm is available * relaxed: allow null encryption when sb is off, or no tpm is available * off: allow null encryption always The default is currently `relaxed` which is the same behavior as before. This replaces the initial idea of using plaintext credentials at firstboot (thanks to Lennart for this nicer and simpler design). Note that this also moves `in_first_boot()` to `basic/initrd-util` which is a better fit now.
719 lines
30 KiB
Bash
Executable File
719 lines
30 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# SPDX-License-Identifier: LGPL-2.1-or-later
|
|
# shellcheck disable=SC2016
|
|
set -eux
|
|
|
|
# shellcheck source=test/units/util.sh
|
|
. "$(dirname "$0")"/util.sh
|
|
|
|
run_with_cred_compare() (
|
|
local cred="${1:?}"
|
|
local exp="${2?}"
|
|
local log_file
|
|
shift 2
|
|
|
|
log_file="$(mktemp)"
|
|
# shellcheck disable=SC2064
|
|
trap "rm -f '$log_file'" RETURN
|
|
|
|
set -o pipefail
|
|
# This has been observed to get stuck under ASAN, so add a timeout as precaution
|
|
timeout 120 systemd-run -p SetCredential="$cred" --wait --pipe -- systemd-creds "$@" | tee "$log_file"
|
|
diff "$log_file" <(echo -ne "$exp")
|
|
)
|
|
|
|
test_mount_with_credential() {
|
|
local credfile tmpdir unit mount_path mount_test
|
|
credfile="/tmp/mount-cred"
|
|
tmpdir="/tmp/test-54-mount"
|
|
unit=$(systemd-escape --suffix mount --path "$tmpdir")
|
|
|
|
echo foo >"$credfile"
|
|
mkdir -p "$tmpdir"
|
|
|
|
# Set up test mount unit
|
|
cat >/run/systemd/system/"$unit" <<EOF
|
|
[Mount]
|
|
What=tmpfs
|
|
Where=$tmpdir
|
|
Type=thisisatest
|
|
LoadCredential=loadcred:$credfile
|
|
EOF
|
|
|
|
# Set up test mount type
|
|
mount_path="$(command -v mount 2>/dev/null)"
|
|
mount_test="${mount_path/\/bin/\/sbin}.thisisatest"
|
|
cat >"$mount_test" <<EOF
|
|
#!/usr/bin/env bash
|
|
# Mount after verifying credential file content
|
|
if [ \$(cat \${CREDENTIALS_DIRECTORY}/loadcred) = "foo" ]; then
|
|
mount -t tmpfs \$1 \$2
|
|
fi
|
|
EOF
|
|
chmod +x "$mount_test"
|
|
|
|
# Verify mount succeeds
|
|
systemctl daemon-reload
|
|
systemctl start "$unit"
|
|
systemctl --no-pager show -p SubState --value "$unit" | grep mounted >/dev/null
|
|
|
|
# Verify mount fails with different credential file content
|
|
echo bar >"$credfile"
|
|
(! systemctl restart "$unit")
|
|
|
|
# Stop unit and delete files
|
|
systemctl stop "$unit"
|
|
rm -f "$credfile" /run/systemd/system/"$unit" "$mount_test"
|
|
rm -rf "$tmpdir"
|
|
}
|
|
|
|
# Sanity checks
|
|
#
|
|
# Create a dummy "full" disk (similar to /dev/full) to check out-of-space
|
|
# scenarios
|
|
mkdir /tmp/full
|
|
mount -t tmpfs -o size=1,nr_inodes=1 tmpfs /tmp/full
|
|
|
|
# verb: setup
|
|
# Run this first, otherwise any encrypted credentials wouldn't be decryptable
|
|
# as we regenerate the host key
|
|
rm -fv /var/lib/systemd/credential.secret
|
|
systemd-creds setup
|
|
test -e /var/lib/systemd/credential.secret
|
|
rm -fv /var/lib/systemd/credential.secret
|
|
|
|
# Prepare a couple of dummy credentials for the cat/list verbs
|
|
CRED_DIR="$(mktemp -d)"
|
|
ENC_CRED_DIR="$(mktemp -d)"
|
|
echo foo >"$CRED_DIR/secure-or-weak"
|
|
echo foo >"$CRED_DIR/insecure"
|
|
echo foo | systemd-creds --name="encrypted" encrypt - "$ENC_CRED_DIR/encrypted"
|
|
echo foo | systemd-creds encrypt - "$ENC_CRED_DIR/encrypted-unnamed"
|
|
chmod -R 0400 "$CRED_DIR" "$ENC_CRED_DIR"
|
|
chmod -R 0444 "$CRED_DIR/insecure"
|
|
mkdir /tmp/empty/
|
|
|
|
systemd-creds --system
|
|
systemd-creds --no-pager --help
|
|
systemd-creds --version
|
|
systemd-creds has-tpm2 || :
|
|
systemd-creds has-tpm2 -q || :
|
|
|
|
# verb: list
|
|
systemd-creds list --system
|
|
ENCRYPTED_CREDENTIALS_DIRECTORY="$ENC_CRED_DIR" CREDENTIALS_DIRECTORY="$CRED_DIR" systemd-creds list --no-legend
|
|
ENCRYPTED_CREDENTIALS_DIRECTORY="$ENC_CRED_DIR" CREDENTIALS_DIRECTORY="$CRED_DIR" systemd-creds list --json=pretty | jq
|
|
ENCRYPTED_CREDENTIALS_DIRECTORY="$ENC_CRED_DIR" CREDENTIALS_DIRECTORY="$CRED_DIR" systemd-creds list --json=short | jq
|
|
ENCRYPTED_CREDENTIALS_DIRECTORY="$ENC_CRED_DIR" CREDENTIALS_DIRECTORY="$CRED_DIR" systemd-creds list --json=off
|
|
ENCRYPTED_CREDENTIALS_DIRECTORY="/tmp/empty/" CREDENTIALS_DIRECTORY="/tmp/empty/" systemd-creds list
|
|
|
|
# verb: cat
|
|
for cred in secure-or-weak insecure encrypted encrypted-unnamed; do
|
|
ENCRYPTED_CREDENTIALS_DIRECTORY="$ENC_CRED_DIR" CREDENTIALS_DIRECTORY="$CRED_DIR" systemd-creds cat "$cred"
|
|
done
|
|
run_with_cred_compare "mycred:" "" cat mycred
|
|
run_with_cred_compare "mycred:\n" "\n" cat mycred
|
|
run_with_cred_compare "mycred:foo" "foo" cat mycred
|
|
run_with_cred_compare "mycred:foo" "foofoofoo" cat mycred mycred mycred
|
|
run_with_cred_compare "mycred:foo" "foo" --newline=auto cat mycred
|
|
run_with_cred_compare "mycred:foo" "foo" --newline=no cat mycred
|
|
run_with_cred_compare "mycred:foo" "foo\n" --newline=yes cat mycred
|
|
run_with_cred_compare "mycred:foo" "foo" --transcode=no cat mycred
|
|
run_with_cred_compare "mycred:foo" "foo" --transcode=0 cat mycred
|
|
run_with_cred_compare "mycred:foo" "foo" --transcode=false cat mycred
|
|
run_with_cred_compare "mycred:foo" "Zm9v" --transcode=base64 cat mycred
|
|
run_with_cred_compare "mycred:Zm9v" "foo" --transcode=unbase64 cat mycred
|
|
run_with_cred_compare "mycred:Zm9v" "foofoofoo" --transcode=unbase64 cat mycred mycred mycred
|
|
run_with_cred_compare "mycred:Zm9vCg==" "foo\n" --transcode=unbase64 cat mycred
|
|
run_with_cred_compare "mycred:hello world" "68656c6c6f20776f726c64" --transcode=hex cat mycred
|
|
run_with_cred_compare "mycred:68656c6c6f20776f726c64" "hello world" --transcode=unhex cat mycred
|
|
run_with_cred_compare "mycred:68656c6c6f20776f726c64" "hello worldhello world" --transcode=unhex cat mycred mycred
|
|
run_with_cred_compare "mycred:68656c6c6f0a776f726c64" "hello\nworld" --transcode=unhex cat mycred
|
|
run_with_cred_compare 'mycred:{ "foo" : "bar", "baz" : [ 3, 4 ] }' '{"foo":"bar","baz":[3,4]}\n' --json=short cat mycred
|
|
systemd-run -p SetCredential='mycred:{ "foo" : "bar", "baz" : [ 3, 4 ] }' --wait --pipe -- systemd-creds --json=pretty cat mycred | jq
|
|
|
|
# verb: encrypt/decrypt
|
|
echo "According to all known laws of aviation..." >/tmp/cred.orig
|
|
systemd-creds --with-key=host encrypt /tmp/cred.orig /tmp/cred.enc
|
|
systemd-creds decrypt /tmp/cred.enc /tmp/cred.dec
|
|
diff /tmp/cred.orig /tmp/cred.dec
|
|
rm -f /tmp/cred.{enc,dec}
|
|
# --pretty
|
|
cred_name="fo'''o''bar"
|
|
cred_option="$(systemd-creds --pretty --name="$cred_name" encrypt /tmp/cred.orig -)"
|
|
mkdir -p /run/systemd/system
|
|
cat >/run/systemd/system/test-54-pretty-cred.service <<EOF
|
|
[Service]
|
|
Type=oneshot
|
|
${cred_option:?}
|
|
ExecStart=bash -c "diff <(systemd-creds cat \"$cred_name\") /tmp/cred.orig"
|
|
EOF
|
|
systemctl daemon-reload
|
|
systemctl start test-54-pretty-cred
|
|
rm /run/systemd/system/test-54-pretty-cred.service
|
|
# Credential validation: name
|
|
systemd-creds --name="foo" -H encrypt /tmp/cred.orig /tmp/cred.enc
|
|
(! systemd-creds decrypt /tmp/cred.enc /tmp/cred.dec)
|
|
(! systemd-creds --name="bar" decrypt /tmp/cred.enc /tmp/cred.dec)
|
|
systemd-creds --name="" decrypt /tmp/cred.enc /tmp/cred.dec
|
|
diff /tmp/cred.orig /tmp/cred.dec
|
|
rm -f /tmp/cred.dec
|
|
systemd-creds --name="foo" decrypt /tmp/cred.enc /tmp/cred.dec
|
|
diff /tmp/cred.orig /tmp/cred.dec
|
|
rm -f /tmp/cred.{enc,dec}
|
|
# Credential validation: time
|
|
systemd-creds --not-after="+1d" encrypt /tmp/cred.orig /tmp/cred.enc
|
|
(! systemd-creds --timestamp="+2d" decrypt /tmp/cred.enc /tmp/cred.dec)
|
|
systemd-creds decrypt /tmp/cred.enc /tmp/cred.dec
|
|
diff /tmp/cred.orig /tmp/cred.dec
|
|
rm -f /tmp/cred.{enc,dec}
|
|
|
|
# Null-key credentials and the systemd.credentials_boot_policy= setting.
|
|
#
|
|
# A null-key credential ("--with-key=null") is wrapped in the normal systemd-creds
|
|
# envelope but offers neither confidentiality nor authenticity, so whether it is
|
|
# accepted at decrypt time is governed by systemd.credentials_boot_policy= and the system
|
|
# state. We only assert the branches that are deterministic regardless of the test
|
|
# bed's TPM2/SecureBoot state (which is fixed and has no test override here); the
|
|
# full accepts_null() truth table for all states is covered by test-creds.c.
|
|
systemd-creds --name=nullcred --with-key=null encrypt /tmp/cred.orig /tmp/cred.enc
|
|
|
|
# --allow-null bypasses the policy, even under the strictest setting.
|
|
SYSTEMD_PROC_CMDLINE="systemd.credentials_boot_policy=strict" \
|
|
systemd-creds --name=nullcred --allow-null decrypt /tmp/cred.enc /tmp/cred.dec
|
|
diff /tmp/cred.orig /tmp/cred.dec
|
|
rm -f /tmp/cred.dec
|
|
|
|
# --refuse-null always refuses, even under the most permissive setting.
|
|
(! SYSTEMD_PROC_CMDLINE="systemd.credentials_boot_policy=off" \
|
|
systemd-creds --name=nullcred --refuse-null decrypt /tmp/cred.enc /tmp/cred.dec)
|
|
|
|
# strict never accepts a null key, regardless of system state.
|
|
(! SYSTEMD_PROC_CMDLINE="systemd.credentials_boot_policy=strict" \
|
|
systemd-creds --name=nullcred decrypt /tmp/cred.enc /tmp/cred.dec)
|
|
|
|
# off always accepts a null key, regardless of system state.
|
|
SYSTEMD_PROC_CMDLINE="systemd.credentials_boot_policy=off" \
|
|
systemd-creds --name=nullcred decrypt /tmp/cred.enc /tmp/cred.dec
|
|
diff /tmp/cred.orig /tmp/cred.dec
|
|
rm -f /tmp/cred.dec
|
|
|
|
# An invalid policy value falls back to the default (relaxed), which accepts during first boot.
|
|
SYSTEMD_PROC_CMDLINE="systemd.credentials_boot_policy=bogus" SYSTEMD_FIRST_BOOT=1 \
|
|
systemd-creds --name=nullcred decrypt /tmp/cred.enc /tmp/cred.dec
|
|
diff /tmp/cred.orig /tmp/cred.dec
|
|
rm -f /tmp/cred.dec
|
|
|
|
# tofu accepts during first boot, independent of TPM2/SecureBoot.
|
|
SYSTEMD_PROC_CMDLINE="systemd.credentials_boot_policy=tofu" SYSTEMD_FIRST_BOOT=1 \
|
|
systemd-creds --name=nullcred decrypt /tmp/cred.enc /tmp/cred.dec
|
|
diff /tmp/cred.orig /tmp/cred.dec
|
|
rm -f /tmp/cred.{enc,dec}
|
|
|
|
(! unshare -m bash -exc "mount -t tmpfs tmpfs /run/credentials && systemd-creds list")
|
|
(! unshare -m bash -exc "mount -t tmpfs tmpfs /run/credentials && systemd-creds --system list")
|
|
(! CREDENTIALS_DIRECTORY="" systemd-creds list)
|
|
(! systemd-creds --system --foo)
|
|
(! systemd-creds --system -@)
|
|
(! systemd-creds --system --json=)
|
|
(! systemd-creds --system --json="")
|
|
(! systemd-creds --system --json=foo)
|
|
(! systemd-creds --system cat)
|
|
(! systemd-creds --system cat "")
|
|
(! systemd-creds --system cat this-should-not-exist)
|
|
(! systemd-run -p SetCredential=mycred:foo --wait --pipe -- systemd-creds --transcode= cat mycred)
|
|
(! systemd-run -p SetCredential=mycred:foo --wait --pipe -- systemd-creds --transcode="" cat mycred)
|
|
(! systemd-run -p SetCredential=mycred:foo --wait --pipe -- systemd-creds --transcode=foo cat mycred)
|
|
(! systemd-run -p SetCredential=mycred:foo --wait --pipe -- systemd-creds --newline=foo cat mycred)
|
|
(! systemd-run -p SetCredential=mycred:notbase64 --wait --pipe -- systemd-creds --transcode=unbase64 cat mycred)
|
|
(! systemd-run -p SetCredential=mycred:nothex --wait --pipe -- systemd-creds --transcode=unhex cat mycred)
|
|
(! systemd-run -p SetCredential=mycred:a --wait --pipe -- systemd-creds --transcode=unhex cat mycred)
|
|
(! systemd-run -p SetCredential=mycred:notjson --wait --pipe -- systemd-creds --json=short cat mycred)
|
|
(! systemd-run -p SetCredential=mycred:notjson --wait --pipe -- systemd-creds --json=pretty cat mycred)
|
|
(! systemd-creds encrypt /foo/bar/baz -)
|
|
(! systemd-creds decrypt /foo/bar/baz -)
|
|
(! systemd-creds decrypt / -)
|
|
(! systemd-creds encrypt / -)
|
|
(! echo foo | systemd-creds --with-key=foo encrypt - -)
|
|
(! echo {0..20} | systemd-creds decrypt - -)
|
|
(! systemd-creds --not-after= encrypt /tmp/cred.orig /tmp/cred.enc)
|
|
(! systemd-creds --not-after="" encrypt /tmp/cred.orig /tmp/cred.enc)
|
|
(! systemd-creds --not-after="-1d" encrypt /tmp/cred.orig /tmp/cred.enc)
|
|
(! systemd-creds --timestamp= encrypt /tmp/cred.orig /tmp/cred.enc)
|
|
(! systemd-creds --timestamp="" encrypt /tmp/cred.orig /tmp/cred.enc)
|
|
(! dd if=/dev/zero count=2M | systemd-creds --with-key=tpm2-absent encrypt - /dev/null)
|
|
(! dd if=/dev/zero count=2M | systemd-creds --with-key=tpm2-absent decrypt - /dev/null)
|
|
(! echo foo | systemd-creds encrypt - /tmp/full/foo)
|
|
(! echo foo | systemd-creds encrypt - - | systemd-creds decrypt - /tmp/full/foo)
|
|
|
|
# Verify that the creds are properly loaded and we can read them from the service's unpriv user
|
|
systemd-run -p LoadCredential=passwd:/etc/passwd \
|
|
-p LoadCredential=shadow:/etc/shadow \
|
|
-p SetCredential=dog:wuff \
|
|
-p DynamicUser=1 \
|
|
--unit=test-54-unpriv.service \
|
|
--wait \
|
|
--pipe \
|
|
cat '${CREDENTIALS_DIRECTORY}/passwd' '${CREDENTIALS_DIRECTORY}/shadow' '${CREDENTIALS_DIRECTORY}/dog' \
|
|
>/tmp/ts54-concat
|
|
(cat /etc/passwd /etc/shadow && echo -n wuff) | cmp /tmp/ts54-concat
|
|
rm /tmp/ts54-concat
|
|
|
|
# Test that SetCredential= acts as fallback for LoadCredential=
|
|
echo piff >/tmp/ts54-fallback
|
|
[ "$(systemd-run -p LoadCredential=paff:/tmp/ts54-fallback -p SetCredential=paff:poff --pipe --wait systemd-creds cat paff)" = "piff" ]
|
|
rm /tmp/ts54-fallback
|
|
[ "$(systemd-run -p LoadCredential=paff:/tmp/ts54-fallback -p SetCredential=paff:poff --pipe --wait systemd-creds cat paff)" = "poff" ]
|
|
|
|
if systemd-detect-virt -q -c ; then
|
|
expected_credential=mynspawncredential
|
|
expected_value=strangevalue
|
|
elif [ -d /sys/firmware/qemu_fw_cfg/by_name ]; then
|
|
# Verify that passing creds through kernel cmdline works
|
|
[ "$(systemd-creds --system cat kernelcmdlinecred)" = "uff" ]
|
|
[ "$(systemd-creds --system cat waldi)" = "woooofffwufffwuff" ]
|
|
|
|
# And that it also works via SMBIOS
|
|
[ "$(systemd-creds --system cat smbioscredential)" = "magicdata" ]
|
|
[ "$(systemd-creds --system cat binarysmbioscredential)" = "magicbinarydata" ]
|
|
|
|
# If we aren't run in nspawn, we are run in qemu
|
|
systemd-detect-virt -q -v
|
|
expected_credential=myqemucredential
|
|
expected_value=othervalue
|
|
|
|
# Verify that writing a sysctl via the kernel cmdline worked
|
|
[ "$(cat /proc/sys/kernel/domainname)" = "sysctltest" ]
|
|
|
|
# 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" ]
|
|
|
|
# Verify that adding a unit and drop-in via credentials worked
|
|
systemctl start my-service
|
|
test -f /tmp/unit-cred
|
|
test -f /tmp/unit-dropin
|
|
test -f /tmp/unit-named-dropin
|
|
else
|
|
echo "qemu_fw_cfg support missing in kernel. Sniff!"
|
|
expected_credential=""
|
|
expected_value=""
|
|
fi
|
|
|
|
if [ "$expected_credential" != "" ] ; then
|
|
# If this test is run in nspawn a credential should have been passed to us. See test/TEST-54-CREDS/test.sh
|
|
[ "$(systemd-creds --system cat "$expected_credential")" = "$expected_value" ]
|
|
|
|
# Test that propagation from system credential to service credential works
|
|
[ "$(systemd-run -p LoadCredential="$expected_credential" --pipe --wait systemd-creds cat "$expected_credential")" = "$expected_value" ]
|
|
|
|
# Check it also works, if we rename it while propagating it
|
|
[ "$(systemd-run -p LoadCredential=miau:"$expected_credential" --pipe --wait systemd-creds cat miau)" = "$expected_value" ]
|
|
|
|
# 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)
|
|
fi
|
|
|
|
# Verify that the creds are immutable
|
|
(! systemd-run -p LoadCredential=passwd:/etc/passwd \
|
|
-p DynamicUser=1 \
|
|
--unit=test-54-immutable-touch.service \
|
|
--wait \
|
|
touch '${CREDENTIALS_DIRECTORY}/passwd')
|
|
(! systemd-run -p LoadCredential=passwd:/etc/passwd \
|
|
-p DynamicUser=1 \
|
|
--unit=test-54-immutable-rm.service \
|
|
--wait \
|
|
rm '${CREDENTIALS_DIRECTORY}/passwd')
|
|
|
|
# Check directory-based loading
|
|
mkdir -p /tmp/ts54-creds/sub
|
|
echo -n a >/tmp/ts54-creds/foo
|
|
echo -n b >/tmp/ts54-creds/bar
|
|
echo -n c >/tmp/ts54-creds/baz
|
|
echo -n d >/tmp/ts54-creds/sub/qux
|
|
systemd-run -p LoadCredential=cred:/tmp/ts54-creds \
|
|
-p DynamicUser=1 \
|
|
--unit=test-54-dir.service \
|
|
--wait \
|
|
--pipe \
|
|
cat '${CREDENTIALS_DIRECTORY}/cred_foo' \
|
|
'${CREDENTIALS_DIRECTORY}/cred_bar' \
|
|
'${CREDENTIALS_DIRECTORY}/cred_baz' \
|
|
'${CREDENTIALS_DIRECTORY}/cred_sub_qux' >/tmp/ts54-concat
|
|
cmp /tmp/ts54-concat <(echo -n abcd)
|
|
rm /tmp/ts54-concat
|
|
rm -rf /tmp/ts54-creds
|
|
|
|
# Check that globs work as expected
|
|
mkdir -p /run/credstore
|
|
echo -n a >/run/credstore/test.creds.first
|
|
# Make sure that when multiple credentials of the same name are found, the first one is used (/etc/credstore
|
|
# is searched before /run/credstore).
|
|
echo -n ignored >/run/credstore/test.creds.second
|
|
mkdir -p /etc/credstore
|
|
echo -n b >/etc/credstore/test.creds.second
|
|
echo -n c >/etc/credstore/test.creds.third
|
|
# Credential name cannot contain ':'
|
|
echo -n hoge >/etc/credstore/test.creds.hoge:invalid
|
|
|
|
# Check if credentials with invalid names are not imported.
|
|
systemd-run -p "ImportCredential=test.creds.*" \
|
|
--unit=test-54-ImportCredential.service \
|
|
-p DynamicUser=1 \
|
|
--wait \
|
|
--pipe \
|
|
test ! -e '${CREDENTIALS_DIRECTORY}/test.creds.hoge:invalid'
|
|
|
|
# Check if credentials with invalid names are not imported (with renaming).
|
|
systemd-run -p "ImportCredential=test.creds.*:renamed.creds." \
|
|
--unit=test-54-ImportCredential.service \
|
|
-p DynamicUser=1 \
|
|
--wait \
|
|
--pipe \
|
|
test ! -e '${CREDENTIALS_DIRECTORY}/renamed.creds.hoge:invalid'
|
|
|
|
# Check that all valid credentials are imported.
|
|
systemd-run -p "ImportCredential=test.creds.*" \
|
|
--unit=test-54-ImportCredential.service \
|
|
-p DynamicUser=1 \
|
|
--wait \
|
|
--pipe \
|
|
cat '${CREDENTIALS_DIRECTORY}/test.creds.first' \
|
|
'${CREDENTIALS_DIRECTORY}/test.creds.second' \
|
|
'${CREDENTIALS_DIRECTORY}/test.creds.third' >/tmp/ts54-concat
|
|
cmp /tmp/ts54-concat <(echo -n abc)
|
|
|
|
# Check that ImportCredential= works without renaming.
|
|
systemd-run -p "ImportCredential=test.creds.*" \
|
|
--unit=test-54-ImportCredential.service \
|
|
-p DynamicUser=1 \
|
|
--wait \
|
|
--pipe \
|
|
cat '${CREDENTIALS_DIRECTORY}/test.creds.first' \
|
|
'${CREDENTIALS_DIRECTORY}/test.creds.second' \
|
|
'${CREDENTIALS_DIRECTORY}/test.creds.third' >/tmp/ts54-concat
|
|
cmp /tmp/ts54-concat <(echo -n abc)
|
|
|
|
# Check that renaming with globs works as expected.
|
|
systemd-run -p "ImportCredential=test.creds.*:renamed.creds." \
|
|
--unit=test-54-ImportCredential.service \
|
|
-p DynamicUser=1 \
|
|
--wait \
|
|
--pipe \
|
|
cat '${CREDENTIALS_DIRECTORY}/renamed.creds.first' \
|
|
'${CREDENTIALS_DIRECTORY}/renamed.creds.second' \
|
|
'${CREDENTIALS_DIRECTORY}/renamed.creds.third' >/tmp/ts54-concat
|
|
cmp /tmp/ts54-concat <(echo -n abc)
|
|
|
|
# Check that renaming without globs works as expected.
|
|
systemd-run -p "ImportCredential=test.creds.first:renamed.creds.first" \
|
|
--unit=test-54-ImportCredential.service \
|
|
-p DynamicUser=1 \
|
|
--wait \
|
|
--pipe \
|
|
cat '${CREDENTIALS_DIRECTORY}/renamed.creds.first' >/tmp/ts54-concat
|
|
cmp /tmp/ts54-concat <(echo -n a)
|
|
|
|
# Test that multiple renames are processed in the correct order.
|
|
systemd-run -p "ImportCredential=test.creds.first:renamed.creds.first" \
|
|
-p "ImportCredential=test.creds.second:renamed.creds.first" \
|
|
--unit=test-54-ImportCredential.service \
|
|
-p DynamicUser=1 \
|
|
--wait \
|
|
--pipe \
|
|
cat '${CREDENTIALS_DIRECTORY}/renamed.creds.first' >/tmp/ts54-concat
|
|
cmp /tmp/ts54-concat <(echo -n a)
|
|
|
|
# Test that a credential can be imported multiple times with different names.
|
|
# We use the deprecated name ImportCredentialEx= on purpose to check that it works.
|
|
systemd-run -p "ImportCredentialEx=test.creds.first" \
|
|
-p "ImportCredentialEx=test.creds.first:renamed.creds.first" \
|
|
-p "ImportCredentialEx=test.creds.first:renamed.creds.second" \
|
|
--unit=test-54-ImportCredential.service \
|
|
-p DynamicUser=1 \
|
|
--wait \
|
|
--pipe \
|
|
cat '${CREDENTIALS_DIRECTORY}/test.creds.first' \
|
|
'${CREDENTIALS_DIRECTORY}/renamed.creds.first' \
|
|
'${CREDENTIALS_DIRECTORY}/renamed.creds.second' >/tmp/ts54-concat
|
|
cmp /tmp/ts54-concat <(echo -n aaa)
|
|
|
|
# Now test encrypted credentials (only supported when built with OpenSSL though)
|
|
if systemctl --version | grep -- +OPENSSL >/dev/null; then
|
|
echo -n $RANDOM >/tmp/test-54-plaintext
|
|
systemd-creds encrypt --name=test-54 /tmp/test-54-plaintext /tmp/test-54-ciphertext
|
|
systemd-creds decrypt --name=test-54 /tmp/test-54-ciphertext | cmp /tmp/test-54-plaintext
|
|
|
|
systemd-run -p LoadCredentialEncrypted=test-54:/tmp/test-54-ciphertext \
|
|
--wait \
|
|
--pipe \
|
|
cat '${CREDENTIALS_DIRECTORY}/test-54' | cmp /tmp/test-54-plaintext
|
|
|
|
echo -n $RANDOM >/tmp/test-54-plaintext
|
|
systemd-creds encrypt --name=test-54 /tmp/test-54-plaintext /tmp/test-54-ciphertext
|
|
systemd-creds decrypt --name=test-54 /tmp/test-54-ciphertext | cmp /tmp/test-54-plaintext
|
|
|
|
systemd-run -p SetCredentialEncrypted=test-54:"$(cat /tmp/test-54-ciphertext)" \
|
|
--wait \
|
|
--pipe \
|
|
cat '${CREDENTIALS_DIRECTORY}/test-54' | cmp /tmp/test-54-plaintext
|
|
|
|
rm /tmp/test-54-plaintext /tmp/test-54-ciphertext
|
|
fi
|
|
|
|
# https://github.com/systemd/systemd/issues/27275
|
|
systemd-run -p DynamicUser=yes -p 'LoadCredential=os:/etc/os-release' \
|
|
-p 'ExecStartPre=true' \
|
|
-p 'ExecStartPre=systemd-creds cat os' \
|
|
--unit=test-54-exec-start-pre.service \
|
|
--wait \
|
|
--pipe \
|
|
true | cmp /etc/os-release
|
|
|
|
# https://github.com/systemd/systemd/issues/31194
|
|
systemd-run -p DynamicUser=yes -p 'LoadCredential=os:/etc/os-release' \
|
|
-p 'ExecStartPost=systemd-creds cat os' \
|
|
--unit=test-54-exec-start-post.service \
|
|
--service-type=oneshot --wait --pipe \
|
|
true | cmp /etc/os-release
|
|
|
|
# https://github.com/systemd/systemd/issues/35788
|
|
systemd-run -p DynamicUser=yes -p 'LoadCredential=os:/etc/os-release' \
|
|
-p 'ExecCondition=systemd-creds cat os' \
|
|
--unit=test-54-exec-condition.service \
|
|
--service-type=oneshot --wait --pipe \
|
|
true | cmp /etc/os-release
|
|
|
|
# https://github.com/systemd/systemd/pull/24734#issuecomment-1925440546
|
|
# Also ExecStartPre= should be able to update creds
|
|
dd if=/dev/urandom of=/tmp/cred-huge bs=600K count=1
|
|
chmod 777 /tmp/cred-huge
|
|
systemd-run -p ProtectSystem=full \
|
|
-p 'LoadCredential=huge:/tmp/cred-huge' \
|
|
-p 'ExecStartPre=true' \
|
|
-p 'ExecStartPre=bash -c "echo fresh >/tmp/cred-huge"' \
|
|
--unit=test-54-huge-cred.service \
|
|
--wait --pipe \
|
|
systemd-creds cat huge | cmp - <(echo "fresh")
|
|
rm /tmp/cred-huge
|
|
|
|
echo stable >/tmp/cred-stable
|
|
systemd-run -p 'LoadCredential=stable:/tmp/cred-stable' \
|
|
-p 'ExecStartPost=systemd-creds cat stable' \
|
|
--unit=test-54-stable.service \
|
|
--service-type=oneshot --wait --pipe \
|
|
bash -c "echo bogus >/tmp/cred-stable" | cmp - <(echo "stable")
|
|
assert_eq "$(cat /tmp/cred-stable)" "bogus"
|
|
rm /tmp/cred-stable
|
|
|
|
if ! systemd-detect-virt -q -c ; then
|
|
# Validate that the credential we inserted via the initrd logic arrived
|
|
test "$(systemd-creds cat --system myinitrdcred)" = "guatemala"
|
|
|
|
# Check that the fstab credential logic worked
|
|
test -d /injected
|
|
grep -q /injected /proc/self/mountinfo
|
|
|
|
# Make sure the getty generator processed the credentials properly
|
|
systemctl -P Wants show getty.target | grep container-getty@idontexist.service >/dev/null
|
|
fi
|
|
|
|
# Decrypt/encrypt via varlink
|
|
|
|
DATA="Zm9vYmFyCg=="
|
|
echo "{\"data\":\"$DATA\"}" >/tmp/vlcredsdata
|
|
|
|
varlinkctl call /run/systemd/io.systemd.Credentials io.systemd.Credentials.Encrypt "$(cat /tmp/vlcredsdata)" | \
|
|
varlinkctl call --json=short /run/systemd/io.systemd.Credentials io.systemd.Credentials.Decrypt >/tmp/vlcredsdata2
|
|
|
|
cmp /tmp/vlcredsdata /tmp/vlcredsdata2
|
|
rm /tmp/vlcredsdata2
|
|
|
|
# Pick a key type explicitly
|
|
varlinkctl call /run/systemd/io.systemd.Credentials io.systemd.Credentials.Encrypt "{\"data\":\"$DATA\",\"withKey\":\"host\"}" | \
|
|
varlinkctl call --json=short /run/systemd/io.systemd.Credentials io.systemd.Credentials.Decrypt >/tmp/vlcredsdata2
|
|
|
|
cmp /tmp/vlcredsdata /tmp/vlcredsdata2
|
|
rm /tmp/vlcredsdata2
|
|
|
|
varlinkctl call /run/systemd/io.systemd.Credentials io.systemd.Credentials.Encrypt "{\"data\":\"$DATA\",\"withKey\":\"null\"}" | \
|
|
jq '.["allowNull"] = true' |
|
|
varlinkctl call --json=short /run/systemd/io.systemd.Credentials io.systemd.Credentials.Decrypt >/tmp/vlcredsdata2
|
|
|
|
cmp /tmp/vlcredsdata /tmp/vlcredsdata2
|
|
rm /tmp/vlcredsdata /tmp/vlcredsdata2
|
|
|
|
# Ensure allowNull works
|
|
(! varlinkctl call /run/systemd/io.systemd.Credentials io.systemd.Credentials.Encrypt "{\"data\":\"$DATA\",\"withKey\":\"null\"}" | \
|
|
jq '.["allowNull"] = false' |
|
|
varlinkctl call --json=short /run/systemd/io.systemd.Credentials io.systemd.Credentials.Decrypt )
|
|
|
|
clean_usertest() {
|
|
rm -f /tmp/usertest.data /tmp/usertest.data /tmp/brummbaer.data
|
|
}
|
|
|
|
trap clean_usertest EXIT
|
|
dd if=/dev/urandom of=/tmp/usertest.data bs=4096 count=1
|
|
|
|
systemd-creds encrypt --user /tmp/usertest.data /tmp/usertest.cred
|
|
|
|
systemd-creds decrypt --user /tmp/usertest.cred - | cmp /tmp/usertest.data
|
|
|
|
# Decryption must fail if it's not done in user context
|
|
(! systemd-creds decrypt /tmp/usertest.cred - )
|
|
|
|
# Decryption must also fail if a different user is used
|
|
(! systemd-creds decrypt --user --uid=65534 /tmp/usertest.cred - )
|
|
|
|
# Try the reverse
|
|
systemd-creds encrypt --user --uid=65534 /tmp/usertest.data /tmp/usertest.cred
|
|
(! systemd-creds decrypt --user /tmp/usertest.cred - )
|
|
systemd-creds decrypt --user --uid=65534 /tmp/usertest.cred - | cmp /tmp/usertest.data
|
|
|
|
systemd-creds encrypt --user /tmp/usertest.data /tmp/usertest.creds --name=mytest
|
|
|
|
# Make sure we actually can decode this in user context
|
|
systemctl start user@0.service
|
|
XDG_RUNTIME_DIR=/run/user/0 systemd-run --pipe --user --unit=waldi.service -p LoadCredentialEncrypted=mytest:/tmp/usertest.creds cat /run/user/0/credentials/waldi.service/mytest | cmp /tmp/usertest.data
|
|
|
|
# Test mount unit with credential
|
|
test_mount_with_credential
|
|
|
|
# Fully unpriv operation
|
|
dd if=/dev/urandom of=/tmp/brummbaer.data bs=4096 count=1
|
|
run0 -u testuser --pipe mkdir -p /home/testuser/.config/credstore.encrypted
|
|
run0 -u testuser --pipe systemd-creds encrypt --user --name=brummbaer - /home/testuser/.config/credstore.encrypted/brummbaer < /tmp/brummbaer.data
|
|
run0 -u testuser --pipe systemd-run --user --pipe -p ImportCredential=brummbaer systemd-creds cat brummbaer | cmp /tmp/brummbaer.data
|
|
|
|
# https://github.com/systemd/systemd/pull/40108
|
|
run0 -u testuser --pipe systemd-run --user --wait -p ImportCredential=brummbaer -p ExecStartPre='ls -l $CREDENTIALS_DIRECTORY' bash -c 'systemd-creds cat brummbaer | cmp /tmp/brummbaer.data'
|
|
|
|
# https://github.com/systemd/systemd/pull/39651
|
|
TESTUSER_CRED_DIR="/run/user/$(id -u testuser)/credentials"
|
|
|
|
PID="$(systemd-notify --fork -- systemd-run -M testuser@ --user --wait --unit=brummbaer.service -p LoadCredential=brummbaer sleep infinity)"
|
|
[[ -d "$TESTUSER_CRED_DIR/brummbaer.service" ]]
|
|
[[ -f "$TESTUSER_CRED_DIR/brummbaer.service/brummbaer" ]]
|
|
|
|
systemd-run -M testuser@ --user --wait -p PrivateMounts=yes -p ImportCredential=brummbaer \
|
|
bash -xec "[[ ! -d '$TESTUSER_CRED_DIR/brummbaer.service' ]] && [[ \$(stat -c %a /run/credentials) -eq 0 ]]"
|
|
systemd-run -M testuser@ --user --wait -p ImportCredential=brummbaer \
|
|
test -d "$TESTUSER_CRED_DIR/brummbaer.service"
|
|
|
|
kill "$PID"
|
|
|
|
# Now test credential refreshing
|
|
|
|
UNIT_NAME="TEST-54-CREDS-refreshing-$RANDOM.service"
|
|
OUTPUT_FILE="/tmp/$UNIT_NAME.out"
|
|
POST_FLAG_FILE="/tmp/$UNIT_NAME.post-flag"
|
|
|
|
cat >/run/systemd/system/"$UNIT_NAME" <<EOF
|
|
[Service]
|
|
Type=notify-reload
|
|
ImportCredential=test.creds.*
|
|
ExecStart=/usr/lib/systemd/tests/testdata/TEST-54-CREDS.units/refresh.sh $OUTPUT_FILE
|
|
EOF
|
|
|
|
systemctl start "$UNIT_NAME"
|
|
ls -l /run/credentials/"$UNIT_NAME"/
|
|
|
|
echo "neu" >/run/credstore/test.creds.new-refresh-1
|
|
[[ ! -e /run/credentials/"$UNIT_NAME"/test.creds.new-refresh-1 ]]
|
|
|
|
systemctl reload "$UNIT_NAME"
|
|
[[ ! -e /run/credentials/"$UNIT_NAME"/test.creds.new-refresh-1 ]]
|
|
(! grep -q "test.creds.new-refresh-1" "$OUTPUT_FILE")
|
|
|
|
echo "RefreshOnReload=credentials" >>/run/systemd/system/"$UNIT_NAME"
|
|
systemctl daemon-reload
|
|
systemctl reload "$UNIT_NAME"
|
|
diff /run/credstore/test.creds.new-refresh-1 /run/credentials/"$UNIT_NAME"/test.creds.new-refresh-1
|
|
diff "$OUTPUT_FILE" <(grep . /run/credentials/"$UNIT_NAME"/*)
|
|
|
|
systemctl stop "$UNIT_NAME"
|
|
cat >>/run/systemd/system/"$UNIT_NAME" <<EOF
|
|
ProtectSystem=strict
|
|
ReadWritePaths=/tmp
|
|
ExecReloadPost=bash -c 'diff $OUTPUT_FILE <(grep . %d/*) || rm -v $POST_FLAG_FILE'
|
|
EOF
|
|
|
|
systemctl daemon-reload
|
|
systemctl start "$UNIT_NAME"
|
|
diff /run/credstore/test.creds.new-refresh-1 /run/credentials/"$UNIT_NAME"/test.creds.new-refresh-1
|
|
diff "$OUTPUT_FILE" <(grep . /run/credentials/"$UNIT_NAME"/*)
|
|
|
|
systemctl reload "$UNIT_NAME"
|
|
diff "$OUTPUT_FILE" <(grep . /run/credentials/"$UNIT_NAME"/*)
|
|
|
|
echo "2" >/run/credstore/test.creds.new-refresh-2
|
|
[[ ! -e /run/credentials/"$UNIT_NAME"/test.creds.new-refresh-2 ]]
|
|
|
|
systemctl reload "$UNIT_NAME"
|
|
diff /run/credstore/test.creds.new-refresh-2 /run/credentials/"$UNIT_NAME"/test.creds.new-refresh-2
|
|
diff "$OUTPUT_FILE" <(grep . /run/credentials/"$UNIT_NAME"/*)
|
|
|
|
echo "3" >/run/credstore/test.creds.new-refresh-3
|
|
[[ ! -e /run/credentials/"$UNIT_NAME"/test.creds.new-refresh-3 ]]
|
|
|
|
rm "$OUTPUT_FILE"
|
|
systemctl edit --runtime --stdin "$UNIT_NAME" <<EOF
|
|
[Service]
|
|
Type=notify
|
|
ExecReloadPost=
|
|
EOF
|
|
systemctl reload "$UNIT_NAME"
|
|
diff /run/credstore/test.creds.new-refresh-3 /run/credentials/"$UNIT_NAME"/test.creds.new-refresh-3
|
|
[[ ! -e "$OUTPUT_FILE" ]]
|
|
|
|
echo "RefreshOnReload=no" >>/run/systemd/system/"$UNIT_NAME"
|
|
systemctl daemon-reload
|
|
assert_eq "$(systemctl show "$UNIT_NAME" -P CanReload)" "no"
|
|
systemctl revert "$UNIT_NAME"
|
|
assert_eq "$(systemctl show "$UNIT_NAME" -P CanReload)" "yes"
|
|
|
|
echo "BOGUS" >/run/credstore/test.creds.refresh-bogus
|
|
touch "$POST_FLAG_FILE"
|
|
systemctl reload "$UNIT_NAME"
|
|
diff /run/credstore/test.creds.new-refresh-3 /run/credentials/"$UNIT_NAME"/test.creds.new-refresh-3
|
|
[[ ! -e /run/credentials/"$UNIT_NAME"/test.creds.refresh-bogus ]]
|
|
diff "$OUTPUT_FILE" <(grep . /run/credentials/"$UNIT_NAME"/*)
|
|
[[ ! -e "$POST_FLAG_FILE" ]]
|
|
|
|
OUTPUT_FILE_USER="/tmp/TEST-54-CREDS-refreshing-user.out"
|
|
|
|
systemd-notify --fork -- \
|
|
systemd-run -M testuser@ --user --wait \
|
|
--unit=brummbaer-refresh.service \
|
|
--service-type=notify-reload \
|
|
-p NotifyAccess=all \
|
|
-p 'ImportCredential=brummbaer*' \
|
|
-p RefreshOnReload=credentials \
|
|
-p ProtectSystem=strict \
|
|
-p ReadWritePaths=/tmp \
|
|
/usr/lib/systemd/tests/testdata/TEST-54-CREDS.units/refresh.sh "$OUTPUT_FILE_USER"
|
|
|
|
[[ -f "$TESTUSER_CRED_DIR/brummbaer-refresh.service/brummbaer" ]]
|
|
diff "$OUTPUT_FILE_USER" <(grep . "$TESTUSER_CRED_DIR"/brummbaer-refresh.service/*)
|
|
|
|
run0 -u testuser --pipe -i \
|
|
--property=EnvironmentFile=-/usr/lib/systemd/systemd-asan-env \
|
|
'mkdir -p .config/credstore && echo "refreshed" >.config/credstore/brummbaer.refreshed'
|
|
|
|
systemctl -M testuser@ --user reload brummbaer-refresh.service
|
|
assert_eq "$(cat "$TESTUSER_CRED_DIR"/brummbaer-refresh.service/brummbaer.refreshed)" "refreshed"
|
|
diff "$OUTPUT_FILE_USER" <(grep . "$TESTUSER_CRED_DIR"/brummbaer-refresh.service/*)
|
|
|
|
touch /testok
|