1
0
mirror of https://github.com/systemd/systemd.git synced 2024-12-26 03:22:00 +03:00
systemd/test/units/TEST-74-AUX-UTILS.firstboot.sh
Dan Nicholson 35bc4c3424 firstboot: fix root params with creds and prompting disabled
Remove an early return that prevents --prompt-root-password or
--prompt-root-shell and systemd.firstboot=off using credentials. In that case,
arg_prompt_root_password and arg_prompt_root_shell will be false, but the
prompt helpers still need to be called to read the credentials. Furthermore, if
only the root shell has been set, don't overwrite the root password.
2024-07-31 04:02:43 -06:00

285 lines
12 KiB
Bash
Executable File

#!/usr/bin/env bash
# SPDX-License-Identifier: LGPL-2.1-or-later
set -eux
set -o pipefail
# shellcheck source=test/units/util.sh
. "$(dirname "$0")"/util.sh
if ! command -v systemd-firstboot >/dev/null; then
echo "systemd-firstboot not found, skipping the test"
exit 0
fi
at_exit() {
if [[ -n "${ROOT:-}" ]]; then
ls -lR "$ROOT"
grep -r . "$ROOT/etc" || :
rm -fr "$ROOT"
fi
restore_locale
}
trap at_exit EXIT
# Generated via `mkpasswd -m sha-512 -S foobarsalt password1`
# shellcheck disable=SC2016
ROOT_HASHED_PASSWORD1='$6$foobarsalt$YbwdaATX6IsFxvWbY3QcZj2gB31R/LFRFrjlFrJtTTqFtSfn4dfOAg/km2k4Sl.a2g7LOYDo31wMTaEsCo9j41'
# Generated via `mkpasswd -m sha-512 -S foobarsalt password2`
# shellcheck disable=SC2016
ROOT_HASHED_PASSWORD2='$6$foobarsalt$q.P2932zYMLbKnjFwIxPI8y3iuxeuJ2BgE372LcZMMnj3Gcg/9mJg2LPKUl.ha0TG/.fRNNnRQcLfzM0SNot3.'
if [[ -f /etc/locale.conf ]]; then
cp /etc/locale.conf /tmp/locale.conf.bak
fi
# Debian/Ubuntu specific file
if [[ -f /etc/default/locale ]]; then
cp /etc/default/locale /tmp/default-locale.bak
fi
if [[ -f /etc/locale.gen ]]; then
cp /etc/locale.gen /tmp/locale.gen.bak
fi
# Make sure at least two locales exist (C.UTF-8 and en_US.UTF-8) as systemd-firstboot --prompt-locale will
# skip writing the locale if it detects only one is installed.
generate_locale en_US.UTF-8
# Debian and Ubuntu use /etc/default/locale instead of /etc/locale.conf. Make
# sure we use the appropriate path for locale configuration.
LOCALE_PATH="/etc/locale.conf"
[ -e "$LOCALE_PATH" ] || LOCALE_PATH="/etc/default/locale"
[ -e "$LOCALE_PATH" ] || systemd-firstboot --locale=C.UTF-8
# Create a minimal root so we don't modify the testbed
ROOT=test-root
mkdir -p "$ROOT/bin"
# Dummy shell for --root-shell=
touch "$ROOT/bin/fooshell" "$ROOT/bin/barshell"
systemd-firstboot --root="$ROOT" --locale=foo
grep -q "LANG=foo" "$ROOT$LOCALE_PATH"
rm -fv "$ROOT$LOCALE_PATH"
systemd-firstboot --root="$ROOT" --locale-messages=foo
grep -q "LC_MESSAGES=foo" "$ROOT$LOCALE_PATH"
rm -fv "$ROOT$LOCALE_PATH"
systemd-firstboot --root="$ROOT" --locale=foo --locale-messages=bar
grep -q "LANG=foo" "$ROOT$LOCALE_PATH"
grep -q "LC_MESSAGES=bar" "$ROOT$LOCALE_PATH"
systemd-firstboot --root="$ROOT" --keymap=foo
grep -q "KEYMAP=foo" "$ROOT/etc/vconsole.conf"
systemd-firstboot --root="$ROOT" --timezone=Europe/Berlin
readlink "$ROOT/etc/localtime" | grep -q "Europe/Berlin"
systemd-firstboot --root="$ROOT" --hostname "foobar"
grep -q "foobar" "$ROOT/etc/hostname"
systemd-firstboot --root="$ROOT" --machine-id=aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
grep -q "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" "$ROOT/etc/machine-id"
rm -fv "$ROOT/etc/passwd" "$ROOT/etc/shadow"
systemd-firstboot --root="$ROOT" --root-password=foo
grep -q "^root:x:0:0:" "$ROOT/etc/passwd"
grep -q "^root:[^!*]" "$ROOT/etc/shadow"
rm -fv "$ROOT/etc/passwd" "$ROOT/etc/shadow"
echo "foo" >root.passwd
systemd-firstboot --root="$ROOT" --root-password-file=root.passwd
grep -q "^root:x:0:0:" "$ROOT/etc/passwd"
grep -q "^root:[^!*]" "$ROOT/etc/shadow"
rm -fv "$ROOT/etc/passwd" "$ROOT/etc/shadow" root.passwd
# Make sure the root password is set if /etc/passwd and /etc/shadow exist but
# don't have a root entry.
touch "$ROOT/etc/passwd" "$ROOT/etc/shadow"
systemd-firstboot --root="$ROOT" --root-password=foo
grep -q "^root:x:0:0:" "$ROOT/etc/passwd"
grep -q "^root:[^!*]" "$ROOT/etc/shadow"
rm -fv "$ROOT/etc/passwd" "$ROOT/etc/shadow"
# If /etc/passwd and /etc/shadow exist, they will only be updated if the shadow
# password is !unprovisioned.
echo "root:x:0:0:root:/root:/bin/sh" >"$ROOT/etc/passwd"
echo "root:!test:::::::" >"$ROOT/etc/shadow"
systemd-firstboot --root="$ROOT" --root-password=foo
grep -q "^root:x:0:0:" "$ROOT/etc/passwd"
grep -q "^root:!test:" "$ROOT/etc/shadow"
rm -fv "$ROOT/etc/passwd" "$ROOT/etc/shadow"
echo "root:x:0:0:root:/root:/bin/sh" >"$ROOT/etc/passwd"
echo "root:!unprovisioned:::::::" >"$ROOT/etc/shadow"
systemd-firstboot --root="$ROOT" --root-password=foo
grep -q "^root:x:0:0:" "$ROOT/etc/passwd"
grep -q "^root:[^!*]" "$ROOT/etc/shadow"
rm -fv "$ROOT/etc/passwd" "$ROOT/etc/shadow"
systemd-firstboot --root="$ROOT" --root-password-hashed="$ROOT_HASHED_PASSWORD1"
grep -q "^root:x:0:0:" "$ROOT/etc/passwd"
grep -q "^root:$ROOT_HASHED_PASSWORD1:" "$ROOT/etc/shadow"
rm -fv "$ROOT/etc/passwd" "$ROOT/etc/shadow"
systemd-firstboot --root="$ROOT" --root-shell=/bin/fooshell
grep -q "^root:x:0:0:.*:/bin/fooshell$" "$ROOT/etc/passwd"
grep -q "^root:!\*:" "$ROOT/etc/shadow"
rm -fv "$ROOT/etc/passwd" "$ROOT/etc/shadow"
systemd-firstboot --root="$ROOT" --root-password-hashed="$ROOT_HASHED_PASSWORD1" --root-shell=/bin/fooshell
grep -q "^root:x:0:0:.*:/bin/fooshell$" "$ROOT/etc/passwd"
grep -q "^root:$ROOT_HASHED_PASSWORD1:" "$ROOT/etc/shadow"
systemd-firstboot --root="$ROOT" --kernel-command-line="foo.bar=42"
grep -q "foo.bar=42" "$ROOT/etc/kernel/cmdline"
# Configs should not get overwritten if they exist unless --force is used
systemd-firstboot --root="$ROOT" \
--locale=locale-overwrite \
--locale-messages=messages-overwrite \
--keymap=keymap-overwrite \
--timezone=CET \
--hostname=hostname-overwrite \
--machine-id=bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb \
--root-password-hashed="$ROOT_HASHED_PASSWORD2" \
--root-shell=/bin/barshell \
--kernel-command-line="hello.world=0"
grep -q "LANG=foo" "$ROOT$LOCALE_PATH"
grep -q "LC_MESSAGES=bar" "$ROOT$LOCALE_PATH"
grep -q "KEYMAP=foo" "$ROOT/etc/vconsole.conf"
readlink "$ROOT/etc/localtime" | grep -q "Europe/Berlin$"
grep -q "foobar" "$ROOT/etc/hostname"
grep -q "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" "$ROOT/etc/machine-id"
grep -q "^root:x:0:0:.*:/bin/fooshell$" "$ROOT/etc/passwd"
grep -q "^root:$ROOT_HASHED_PASSWORD1:" "$ROOT/etc/shadow"
grep -q "foo.bar=42" "$ROOT/etc/kernel/cmdline"
# The same thing, but now with --force
systemd-firstboot --root="$ROOT" --force \
--locale=locale-overwrite \
--locale-messages=messages-overwrite \
--keymap=keymap-overwrite \
--timezone=CET \
--hostname=hostname-overwrite \
--machine-id=bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb \
--root-password-hashed="$ROOT_HASHED_PASSWORD2" \
--root-shell=/bin/barshell \
--kernel-command-line="hello.world=0"
grep -q "LANG=locale-overwrite" "$ROOT$LOCALE_PATH"
grep -q "LC_MESSAGES=messages-overwrite" "$ROOT$LOCALE_PATH"
grep -q "KEYMAP=keymap-overwrite" "$ROOT/etc/vconsole.conf"
readlink "$ROOT/etc/localtime" | grep -q "/CET$"
grep -q "hostname-overwrite" "$ROOT/etc/hostname"
grep -q "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb" "$ROOT/etc/machine-id"
grep -q "^root:x:0:0:.*:/bin/barshell$" "$ROOT/etc/passwd"
grep -q "^root:$ROOT_HASHED_PASSWORD2:" "$ROOT/etc/shadow"
grep -q "hello.world=0" "$ROOT/etc/kernel/cmdline"
# Test that --reset removes all files configured by firstboot.
systemd-firstboot --root="$ROOT" --reset
[[ ! -e "$ROOT/etc/locale.conf" ]]
[[ ! -e "$ROOT/etc/vconsole.conf" ]]
[[ ! -e "$ROOT/etc/localtime" ]]
[[ ! -e "$ROOT/etc/hostname" ]]
[[ ! -e "$ROOT/etc/machine-id" ]]
[[ ! -e "$ROOT/etc/kernel/cmdline" ]]
# --copy-* options
rm -fr "$ROOT"
mkdir "$ROOT"
# Copy everything at once (--copy)
systemd-firstboot --root="$ROOT" --copy
diff $LOCALE_PATH "$ROOT$LOCALE_PATH"
diff <(awk -F: '/^root/ { print $7; }' /etc/passwd) <(awk -F: '/^root/ { print $7; }' "$ROOT/etc/passwd")
diff <(awk -F: '/^root/ { print $2; }' /etc/shadow) <(awk -F: '/^root/ { print $2; }' "$ROOT/etc/shadow")
[[ -e /etc/vconsole.conf ]] && diff /etc/vconsole.conf "$ROOT/etc/vconsole.conf"
[[ -e /etc/localtime ]] && diff <(readlink /etc/localtime) <(readlink "$ROOT/etc/localtime")
rm -fr "$ROOT"
mkdir "$ROOT"
# Copy everything at once, but now by using separate switches
systemd-firstboot --root="$ROOT" --copy-locale --copy-keymap --copy-timezone --copy-root-password --copy-root-shell
diff $LOCALE_PATH "$ROOT$LOCALE_PATH"
diff <(awk -F: '/^root/ { print $7; }' /etc/passwd) <(awk -F: '/^root/ { print $7; }' "$ROOT/etc/passwd")
diff <(awk -F: '/^root/ { print $2; }' /etc/shadow) <(awk -F: '/^root/ { print $2; }' "$ROOT/etc/shadow")
[[ -e /etc/vconsole.conf ]] && diff /etc/vconsole.conf "$ROOT/etc/vconsole.conf"
[[ -e /etc/localtime ]] && diff <(readlink /etc/localtime) <(readlink "$ROOT/etc/localtime")
# --prompt-* options
rm -fr "$ROOT"
mkdir -p "$ROOT/bin"
touch "$ROOT/bin/fooshell" "$ROOT/bin/barshell"
# Temporarily disable pipefail to avoid `echo: write error: Broken pipe
set +o pipefail
# We can do only limited testing here, since it's all an interactive stuff, so
# --prompt is skipped on purpose and only limited --prompt-root-password
# testing can be done.
echo -ne "\nfoo\nbar\n" | systemd-firstboot --root="$ROOT" --prompt-locale
grep -q "LANG=foo" "$ROOT$LOCALE_PATH"
grep -q "LC_MESSAGES=bar" "$ROOT$LOCALE_PATH"
# systemd-firstboot in prompt-keymap mode requires keymaps to be installed so
# it can present them as a list to the user. As Debian does not ship/provide
# compatible keymaps (from the kbd package), skip this test if the keymaps are
# missing.
if [ -d "/usr/share/keymaps/" ] || [ -d "/usr/share/kbd/keymaps/" ] || [ -d "/usr/lib/kbd/keymaps/" ] ; then
echo -ne "\nfoo\n" | systemd-firstboot --root="$ROOT" --prompt-keymap
grep -q "KEYMAP=foo" "$ROOT/etc/vconsole.conf"
fi
echo -ne "\nEurope/Berlin\n" | systemd-firstboot --root="$ROOT" --prompt-timezone
readlink "$ROOT/etc/localtime" | grep -q "Europe/Berlin$"
echo -ne "\nfoobar\n" | systemd-firstboot --root="$ROOT" --prompt-hostname
grep -q "foobar" "$ROOT/etc/hostname"
# With no root password provided, a locked account should be created.
systemd-firstboot --root="$ROOT" --prompt-root-password </dev/null
grep -q "^root:x:0:0:" "$ROOT/etc/passwd"
grep -q "^root:!\*:" "$ROOT/etc/shadow"
rm -fv "$ROOT/etc/passwd" "$ROOT/etc/shadow"
echo -ne "\n/bin/fooshell\n" | systemd-firstboot --root="$ROOT" --prompt-root-shell
grep -q "^root:.*:0:0:.*:/bin/fooshell$" "$ROOT/etc/passwd"
# Existing files should not get overwritten
echo -ne "\n/bin/barshell\n" | systemd-firstboot --root="$ROOT" --prompt-root-shell
grep -q "^root:.*:0:0:.*:/bin/fooshell$" "$ROOT/etc/passwd"
# Now without the welcome screen but with force
echo -ne "/bin/barshell\n" | systemd-firstboot --root="$ROOT" --force --prompt-root-shell --welcome=no
grep -q "^root:.*:0:0:.*:/bin/barshell$" "$ROOT/etc/passwd"
# Re-enable pipefail
set -o pipefail
# --prompt-* options with credentials. Unfortunately, with --root the
# --systemd.firstboot kernel command line option is ignored, so that can't be
# --tested.
rm -fr "$ROOT"
mkdir -p "$ROOT/bin"
touch "$ROOT/bin/fooshell" "$ROOT/bin/barshell"
systemd-run --wait --pipe --service-type=exec \
-p SetCredential=firstboot.locale:foo \
-p SetCredential=firstboot.locale-messages:bar \
-p SetCredential=firstboot.keymap:foo \
-p SetCredential=firstboot.timezone:Europe/Berlin \
-p SetCredential=passwd.hashed-password.root:"$ROOT_HASHED_PASSWORD1" \
-p SetCredential=passwd.shell.root:/bin/fooshell \
systemd-firstboot \
--root="$ROOT" \
--prompt-locale \
--prompt-keymap \
--prompt-timezone \
--prompt-root-password \
--prompt-root-shell \
</dev/null
grep -q "LANG=foo" "$ROOT$LOCALE_PATH"
grep -q "LC_MESSAGES=bar" "$ROOT$LOCALE_PATH"
grep -q "KEYMAP=foo" "$ROOT/etc/vconsole.conf"
readlink "$ROOT/etc/localtime" | grep -q "Europe/Berlin$"
grep -q "^root:x:0:0:.*:/bin/fooshell$" "$ROOT/etc/passwd"
grep -q "^root:$ROOT_HASHED_PASSWORD1:" "$ROOT/etc/shadow"
# Assorted tests
rm -fr "$ROOT"
mkdir "$ROOT"
systemd-firstboot --root="$ROOT" --setup-machine-id
grep -E "[a-z0-9]{32}" "$ROOT/etc/machine-id"
rm -fv "$ROOT/etc/machine-id"
systemd-firstboot --root="$ROOT" --delete-root-password
grep -q "^root:x:0:0:" "$ROOT/etc/passwd"
grep -q "^root::" "$ROOT/etc/shadow"
rm -fv "$ROOT/etc/passwd" "$ROOT/etc/shadow"
(! systemd-firstboot --root="$ROOT" --root-shell=/bin/nonexistentshell)
(! systemd-firstboot --root="$ROOT" --machine-id=invalidmachineid)
(! systemd-firstboot --root="$ROOT" --timezone=Foo/Bar)