Files
systemd/test/units/TEST-46-HOMED.sh
Yu Watanabe 5c60d3011f integration tests: do not adjust log level in the test script
We passes log level through kernel command line. It is not necessary to
set to debug level at the beginning, and set to info at the end.
This is important when a test has several subtests. If a subtest sets
log level to info at the end, then subsequent tests may not generate any
useful logs.
2025-09-26 07:20:04 +09:00

849 lines
34 KiB
Bash
Executable File

#!/usr/bin/env bash
# SPDX-License-Identifier: LGPL-2.1-or-later
# shellcheck disable=SC2016,SC2209
set -eux
set -o pipefail
# Check if homectl is installed, and if it isn't bail out early instead of failing
if ! command -v homectl >/dev/null; then
echo "no homed" >/skipped
exit 77
fi
inspect() {
# As updating disk-size-related attributes can take some time on some
# filesystems, let's drop these fields before comparing the outputs to
# avoid unexpected fails. To see the full outputs of both homectl &
# userdbctl (for debugging purposes) drop the fields just before the
# comparison.
local USERNAME="${1:?}"
homectl inspect "$USERNAME" | tee /tmp/a
userdbctl user "$USERNAME" | tee /tmp/b
# diff uses the grep BREs for pattern matching
diff -I '^\s*Disk \(Size\|Free\|Floor\|Ceiling\|Usage\):' /tmp/{a,b}
rm /tmp/{a,b}
homectl inspect --json=pretty "$USERNAME"
}
wait_for_exist() {
timeout 2m bash -c "until homectl inspect '${1:?}'; do sleep 2; done"
}
wait_for_state() {
timeout 2m bash -c "until homectl inspect '${1:?}' | grep -qF 'State: $2'; do sleep 2; done"
}
FSTYPE="$(stat --file-system --format "%T" /)"
systemctl start systemd-homed.service systemd-userdbd.socket
# Create a tmpfs to use as backing store for the home dir. That way we can enforce a size limit nicely.
mkdir -p /home
mount -t tmpfs tmpfs /home -o size=290M
# Make sure systemd-homed takes notice of the overmounted /home/
systemctl kill -sUSR1 systemd-homed
TMP_SKEL=$(mktemp -d)
echo hogehoge >"$TMP_SKEL"/hoge
# we enable --luks-discard= since we run our tests in a tight VM, hence don't
# needlessly pressure for storage. We also set the cheapest KDF, since we don't
# want to waste CI CPU cycles on it. We also effectively disable rate-limiting on
# the user by allowing 1000 logins per second
NEWPASSWORD=xEhErW0ndafV4s homectl create test-user \
--disk-size=min \
--luks-discard=yes \
--image-path=/home/test-user.home \
--luks-pbkdf-type=pbkdf2 \
--luks-pbkdf-time-cost=1ms \
--rate-limit-interval=1s \
--rate-limit-burst=1000 \
--skel="$TMP_SKEL"
inspect test-user
PASSWORD=xEhErW0ndafV4s homectl authenticate test-user
PASSWORD=xEhErW0ndafV4s homectl activate test-user
inspect test-user
PASSWORD=xEhErW0ndafV4s homectl update test-user --real-name="Inline test"
inspect test-user
homectl deactivate test-user
inspect test-user
PASSWORD=xEhErW0ndafV4s NEWPASSWORD=yPN4N0fYNKUkOq homectl passwd test-user
inspect test-user
PASSWORD=yPN4N0fYNKUkOq homectl activate test-user
inspect test-user
SYSTEMD_LOG_LEVEL=debug PASSWORD=yPN4N0fYNKUkOq NEWPASSWORD=xEhErW0ndafV4s homectl passwd test-user
inspect test-user
homectl deactivate test-user
inspect test-user
homectl update test-user --real-name "Offline test" --offline
inspect test-user
PASSWORD=xEhErW0ndafV4s homectl activate test-user
inspect test-user
# Ensure that the offline changes were propagated in
grep "Offline test" /home/test-user/.identity
homectl deactivate test-user
inspect test-user
PASSWORD=xEhErW0ndafV4s homectl update test-user --real-name="Inactive test"
inspect test-user
PASSWORD=xEhErW0ndafV4s homectl activate test-user
inspect test-user
homectl deactivate test-user
inspect test-user
# Do some keyring tests, but only on real kernels, since keyring access inside of containers will fail
# (See: https://github.com/systemd/systemd/issues/17606)
if ! systemd-detect-virt -cq ; then
PASSWORD=xEhErW0ndafV4s homectl activate test-user
inspect test-user
# Key should now be in the keyring
homectl update test-user --real-name "Keyring Test"
inspect test-user
# These commands shouldn't use the keyring
(! timeout 5s homectl authenticate test-user )
(! NEWPASSWORD="foobar" timeout 5s homectl passwd test-user )
homectl lock test-user
inspect test-user
# Key should be gone from keyring
(! timeout 5s homectl update test-user --real-name "Keyring Test 2" )
PASSWORD=xEhErW0ndafV4s homectl unlock test-user
inspect test-user
# Key should have been re-instantiated into the keyring
homectl update test-user --real-name "Keyring Test 3"
inspect test-user
homectl deactivate test-user
inspect test-user
fi
# Do some resize tests, but only if we run on real kernels and are on btrfs, as quota inside of containers
# will fail and minimizing while active only works on btrfs.
if ! systemd-detect-virt -cq && [[ "$FSTYPE" == "btrfs" ]]; then
# grow while inactive
PASSWORD=xEhErW0ndafV4s homectl resize test-user 300M
inspect test-user
# minimize while inactive
PASSWORD=xEhErW0ndafV4s homectl resize test-user min
inspect test-user
PASSWORD=xEhErW0ndafV4s homectl activate test-user
inspect test-user
# grow while active
PASSWORD=xEhErW0ndafV4s homectl resize test-user max
inspect test-user
# minimize while active
PASSWORD=xEhErW0ndafV4s homectl resize test-user 0
inspect test-user
# grow while active
PASSWORD=xEhErW0ndafV4s homectl resize test-user 300M
inspect test-user
# shrink to original size while active
PASSWORD=xEhErW0ndafV4s homectl resize test-user 256M
inspect test-user
# minimize again
PASSWORD=xEhErW0ndafV4s homectl resize test-user min
inspect test-user
# Increase space, so that we can reasonably rebalance free space between to home dirs
mount /home -o remount,size=800M
# create second user
NEWPASSWORD=uuXoo8ei homectl create test-user2 \
--disk-size=min \
--luks-discard=yes \
--image-path=/home/test-user2.home \
--luks-pbkdf-type=pbkdf2 \
--luks-pbkdf-time-cost=1ms \
--rate-limit-interval=1s \
--rate-limit-burst=1000
inspect test-user2
# activate second user
PASSWORD=uuXoo8ei homectl activate test-user2
inspect test-user2
# set second user's rebalance weight to 100
PASSWORD=uuXoo8ei homectl update test-user2 --rebalance-weight=100
inspect test-user2
# set first user's rebalance weight to quarter of that of the second
PASSWORD=xEhErW0ndafV4s homectl update test-user --rebalance-weight=25
inspect test-user
# synchronously rebalance
homectl rebalance
inspect test-user
inspect test-user2
wait_for_state test-user2 active
homectl deactivate test-user2
wait_for_state test-user2 inactive
homectl remove test-user2
fi
PASSWORD=xEhErW0ndafV4s homectl with test-user -- test ! -f /home/test-user/xyz
(! PASSWORD=xEhErW0ndafV4s homectl with test-user -- test -f /home/test-user/xyz)
PASSWORD=xEhErW0ndafV4s homectl with test-user -- touch /home/test-user/xyz
PASSWORD=xEhErW0ndafV4s homectl with test-user -- test -f /home/test-user/xyz
PASSWORD=xEhErW0ndafV4s homectl with test-user -- rm /home/test-user/xyz
PASSWORD=xEhErW0ndafV4s homectl with test-user -- test ! -f /home/test-user/xyz
(! PASSWORD=xEhErW0ndafV4s homectl with test-user -- test -f /home/test-user/xyz)
[[ $(PASSWORD=xEhErW0ndafV4s homectl with test-user -- stat -c %U /home/test-user/hoge) == "test-user" ]]
[[ $(PASSWORD=xEhErW0ndafV4s homectl with test-user -- cat /home/test-user/hoge) == "$(cat "$TMP_SKEL"/hoge)" ]]
# Regression tests
wait_for_state test-user inactive
/usr/lib/systemd/tests/unit-tests/manual/test-homed-regression-31896 test-user
wait_for_state test-user inactive
homectl remove test-user
# blob directory tests
# See docs/USER_RECORD_BLOB_DIRS.md
checkblob() {
test -f "/var/cache/systemd/home/blob-user/$1"
stat -c "%u %#a" "/var/cache/systemd/home/blob-user/$1" | grep "^0 0644"
test -f "/home/blob-user/.identity-blob/$1"
stat -c "%u %#a" "/home/blob-user/.identity-blob/$1" | grep "^12345 0644"
diff "/var/cache/systemd/home/blob-user/$1" "$2"
diff "/var/cache/systemd/home/blob-user/$1" "/home/blob-user/.identity-blob/$1"
}
mkdir /tmp/blob1 /tmp/blob2
echo data1 blob1 >/tmp/blob1/test1
echo data1 blob2 >/tmp/blob2/test1
echo data2 blob1 >/tmp/blob1/test2
echo data2 blob2 >/tmp/blob2/test2
echo invalid filename >/tmp/blob1/файл
echo data3 >/tmp/external-test3
echo avatardata >/tmp/external-avatar
ln -s /tmp/external-avatar /tmp/external-avatar-lnk
dd if=/dev/urandom of=/tmp/external-barely-fits bs=1M count=64
dd if=/dev/urandom of=/tmp/external-toobig bs=1M count=65
# create w/ prepopulated blob dir
NEWPASSWORD=EMJuc3zQaMibJo homectl create blob-user \
--disk-size=min --luks-discard=yes \
--luks-pbkdf-type=pbkdf2 --luks-pbkdf-time-cost=1ms \
--rate-limit-interval=1s --rate-limit-burst=1000 \
--uid=12345 \
--blob=/tmp/blob1
inspect blob-user
PASSWORD=EMJuc3zQaMibJo homectl activate blob-user
inspect blob-user
test -d /var/cache/systemd/home/blob-user
stat -c "%u %#a" /var/cache/systemd/home/blob-user | grep "^0 0755"
test -d /home/blob-user/.identity-blob
stat -c "%u %#a" /home/blob-user/.identity-blob | grep "^12345 0700"
checkblob test1 /tmp/blob1/test1
(! checkblob test1 /tmp/blob2/test1 )
checkblob test2 /tmp/blob1/test2
(! checkblob test2 /tmp/blob2/test2 )
(! checkblob фаил /tmp/blob1/фаил )
(! checkblob test3 /tmp/external-test3 )
(! checkblob avatar /tmp/external-avatar )
# append files to existing blob, both well-known and other
PASSWORD=EMJuc3zQaMibJo homectl update blob-user \
-b test3=/tmp/external-test3 --avatar=/tmp/external-avatar
inspect blob-user
checkblob test1 /tmp/blob1/test1
(! checkblob test1 /tmp/blob2/test1 )
checkblob test2 /tmp/blob1/test2
(! checkblob test2 /tmp/blob2/test2 )
(! checkblob фаил /tmp/blob1/фаил )
checkblob test3 /tmp/external-test3
checkblob avatar /tmp/external-avatar
# delete files from existing blob, both well-known and other
PASSWORD=EMJuc3zQaMibJo homectl update blob-user \
-b test3= --avatar=
inspect blob-user
checkblob test1 /tmp/blob1/test1
(! checkblob test1 /tmp/blob2/test1 )
checkblob test2 /tmp/blob1/test2
(! checkblob test2 /tmp/blob2/test2 )
(! checkblob фаил /tmp/blob1/фаил )
(! checkblob test3 /tmp/external-test3 )
(! checkblob avatar /tmp/external-avatar )
# swap entire blob directory
PASSWORD=EMJuc3zQaMibJo homectl update blob-user \
-b /tmp/blob2
inspect blob-user
(! checkblob test1 /tmp/blob1/test1 )
checkblob test1 /tmp/blob2/test1
(! checkblob test2 /tmp/blob1/test2 )
checkblob test2 /tmp/blob2/test2
(! checkblob фаил /tmp/blob1/фаил )
(! checkblob test3 /tmp/external-test3 )
(! checkblob avatar /tmp/external-avatar )
# create and delete files while swapping blob directory. Also symlinks.
PASSWORD=EMJuc3zQaMibJo homectl update blob-user \
-b /tmp/blob1 -b test2= -b test3=/tmp/external-test3 --avatar=/tmp/external-avatar-lnk
inspect blob-user
checkblob test1 /tmp/blob1/test1
(! checkblob test1 /tmp/blob2/test1 )
(! checkblob test2 /tmp/blob1/test2 )
(! checkblob test2 /tmp/blob2/test2 )
(! checkblob фаил /tmp/blob1/фаил )
checkblob test3 /tmp/external-test3
checkblob avatar /tmp/external-avatar # target of the link
# clear the blob directory
PASSWORD=EMJuc3zQaMibJo homectl update blob-user \
-b /tmp/blob2 -b test3=/tmp/external-test3 --blob=
inspect blob-user
(! checkblob test1 /tmp/blob1/test1 )
(! checkblob test1 /tmp/blob2/test1 )
(! checkblob test2 /tmp/blob1/test2 )
(! checkblob test2 /tmp/blob2/test2 )
(! checkblob фаил /tmp/blob1/фаил )
(! checkblob test3 /tmp/external-test3 )
(! checkblob avatar /tmp/external-avatar )
# file that's exactly 64M still fits
# FIXME: Figure out why this fails on ext4.
if [[ "$FSTYPE" != "ext2/ext3" ]]; then
PASSWORD=EMJuc3zQaMibJo homectl update blob-user \
-b barely-fits=/tmp/external-barely-fits
(! checkblob test1 /tmp/blob1/test1 )
(! checkblob test1 /tmp/blob2/test1 )
(! checkblob test2 /tmp/blob1/test2 )
(! checkblob test2 /tmp/blob2/test2 )
(! checkblob фаил /tmp/blob1/фаил )
(! checkblob test3 /tmp/external-test3 )
(! checkblob avatar /tmp/external-avatar )
checkblob barely-fits /tmp/external-barely-fits
fi
# error out if the file is too big
(! PASSWORD=EMJuc3zQaMibJo homectl update blob-user -b huge=/tmp/external-toobig )
# error out if filenames are invalid
(! PASSWORD=EMJuc3zQaMibJo homectl update blob-user -b .hidden=/tmp/external-test3 )
(! PASSWORD=EMJuc3zQaMibJo homectl update blob-user -b "with spaces=/tmp/external-test3" )
(! PASSWORD=EMJuc3zQaMibJo homectl update blob-user -b with=equals=/tmp/external-test3 )
(! PASSWORD=EMJuc3zQaMibJo homectl update blob-user -b файл=/tmp/external-test3 )
(! PASSWORD=EMJuc3zQaMibJo homectl update blob-user -b special@chars=/tmp/external-test3 )
# Make sure offline updates to blobs get propagated in
homectl deactivate blob-user
inspect blob-user
homectl update blob-user --offline -b barely-fits= -b propagated=/tmp/external-test3
inspect blob-user
PASSWORD=EMJuc3zQaMibJo homectl activate blob-user
inspect blob-user
(! checkblob barely-fits /tmp/external-barely-fits )
checkblob propagated /tmp/external-test3
homectl deactivate blob-user
wait_for_state blob-user inactive
homectl remove blob-user
# userdbctl tests
export PAGER=
# Create a couple of user/group records to test io.systemd.DropIn
# See docs/USER_RECORD.md and docs/GROUP_RECORD.md
mkdir -p /run/userdb/
cat >"/run/userdb/dropingroup.group" <<\EOF
{
"groupName" : "dropingroup",
"gid" : 1000000
}
EOF
cat >"/run/userdb/dropinuser.user" <<\EOF
{
"userName" : "dropinuser",
"uid" : 2000000,
"realName" : "🐱",
"memberOf" : [
"dropingroup"
]
}
EOF
cat >"/run/userdb/dropinuser.user-privileged" <<\EOF
{
"privileged" : {
"hashedPassword" : [
"$6$WHBKvAFFT9jKPA4k$OPY4D4TczKN/jOnJzy54DDuOOagCcvxxybrwMbe1SVdm.Bbr.zOmBdATp.QrwZmvqyr8/SafbbQu.QZ2rRvDs/"
],
"sshAuthorizedKeys" : [
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIA//dxI2xLg4MgxIKKZv1nqwTEIlE/fdakii2Fb75pG+ foo@bar.tld",
"ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBMlaqG2rTMje5CQnfjXJKmoSpEVJ2gWtx4jBvsQbmee2XbU/Qdq5+SRisssR9zVuxgg5NA5fv08MgjwJQMm+csc= hello@world.tld"
]
}
}
EOF
# Set permissions and create necessary symlinks as described in nss-systemd(8)
chmod 0600 "/run/userdb/dropinuser.user-privileged"
ln -svrf "/run/userdb/dropingroup.group" "/run/userdb/1000000.group"
ln -svrf "/run/userdb/dropinuser.user" "/run/userdb/2000000.user"
ln -svrf "/run/userdb/dropinuser.user-privileged" "/run/userdb/2000000.user-privileged"
userdbctl
userdbctl --version
userdbctl --help --no-pager
userdbctl --no-legend
userdbctl --output=classic
userdbctl --output=friendly
userdbctl --output=table
userdbctl --output=json | jq
userdbctl -j --json=pretty | jq
userdbctl -j --json=short | jq
userdbctl --with-varlink=no
userdbctl user
userdbctl user -S
userdbctl user -IS
userdbctl user -R
userdbctl user --disposition=regular --disposition=intrinsic
userdbctl user kkkk -z
userdbctl user --uid-min=100 --uid-max=100
userdbctl user -B
userdbctl user testuser
userdbctl user root
userdbctl user testuser root
userdbctl user -j testuser root | jq
# Check only UID for the nobody user, since the name is build-configurable
userdbctl user --with-nss=no --synthesize=yes
userdbctl user --with-nss=no --synthesize=yes 0 root 65534
userdbctl user dropinuser
userdbctl user 2000000
userdbctl user --with-nss=no --with-varlink=no --synthesize=no --multiplexer=no dropinuser
userdbctl user --with-nss=no 2000000
(! userdbctl user '')
(! userdbctl user 🐱)
(! userdbctl user 🐱 '' bar)
(! userdbctl user i-do-not-exist)
(! userdbctl user root i-do-not-exist testuser)
(! userdbctl user --with-nss=no --synthesize=no 0 root 65534)
(! userdbctl user -N root nobody)
(! userdbctl user --with-dropin=no dropinuser)
(! userdbctl user --with-dropin=no 2000000)
userdbctl group
userdbctl group -S
userdbctl group -IS
userdbctl group -R
userdbctl group --disposition=regular --disposition=intrinsic
userdbctl group kkkk -z
userdbctl group --uid-min=100 --uid-max=100
userdbctl group -B
userdbctl group testuser
userdbctl group root
userdbctl group testuser root
userdbctl group -j testuser root | jq
# Check only GID for the nobody group, since the name is build-configurable
userdbctl group --with-nss=no --synthesize=yes
userdbctl group --with-nss=no --synthesize=yes 0 root 65534
userdbctl group dropingroup
userdbctl group 1000000
userdbctl group --with-nss=no --with-varlink=no --synthesize=no --multiplexer=no dropingroup
userdbctl group --with-nss=no 1000000
(! userdbctl group '')
(! userdbctl group 🐱)
(! userdbctl group 🐱 '' bar)
(! userdbctl group i-do-not-exist)
(! userdbctl group root i-do-not-exist testuser)
(! userdbctl group --with-nss=no --synthesize=no 0 root 65534)
(! userdbctl group --with-dropin=no dropingroup)
(! userdbctl group --with-dropin=no 1000000)
userdbctl users-in-group
userdbctl users-in-group testuser
userdbctl users-in-group testuser root
userdbctl users-in-group -j testuser root | jq
userdbctl users-in-group 🐱
(! userdbctl users-in-group '')
(! userdbctl users-in-group foo '' bar)
userdbctl groups-of-user
userdbctl groups-of-user testuser
userdbctl groups-of-user testuser root
userdbctl groups-of-user -j testuser root | jq
userdbctl groups-of-user 🐱
(! userdbctl groups-of-user '')
(! userdbctl groups-of-user foo '' bar)
userdbctl services
userdbctl services -j | jq
varlinkctl call /run/systemd/userdb/io.systemd.Multiplexer io.systemd.UserDatabase.GetUserRecord '{"userName":"testuser","service":"io.systemd.Multiplexer"}'
varlinkctl call /run/systemd/userdb/io.systemd.Multiplexer io.systemd.UserDatabase.GetUserRecord '{"userName":"root","service":"io.systemd.Multiplexer"}'
varlinkctl call /run/systemd/userdb/io.systemd.Multiplexer io.systemd.UserDatabase.GetUserRecord '{"userName":"dropinuser","service":"io.systemd.Multiplexer"}'
varlinkctl call /run/systemd/userdb/io.systemd.Multiplexer io.systemd.UserDatabase.GetUserRecord '{"uid":2000000,"service":"io.systemd.Multiplexer"}'
(! varlinkctl call /run/systemd/userdb/io.systemd.Multiplexer io.systemd.UserDatabase.GetUserRecord '{"userName":"","service":"io.systemd.Multiplexer"}')
(! varlinkctl call /run/systemd/userdb/io.systemd.Multiplexer io.systemd.UserDatabase.GetUserRecord '{"userName":"🐱","service":"io.systemd.Multiplexer"}')
(! varlinkctl call /run/systemd/userdb/io.systemd.Multiplexer io.systemd.UserDatabase.GetUserRecord '{"userName":"i-do-not-exist","service":"io.systemd.Multiplexer"}')
userdbctl ssh-authorized-keys dropinuser | tee /tmp/authorized-keys
grep "ssh-ed25519" /tmp/authorized-keys
grep "ecdsa-sha2-nistp256" /tmp/authorized-keys
echo "my-top-secret-key 🐱" >/tmp/my-top-secret-key
userdbctl ssh-authorized-keys dropinuser --chain /usr/bin/cat /tmp/my-top-secret-key | tee /tmp/authorized-keys
grep "ssh-ed25519" /tmp/authorized-keys
grep "ecdsa-sha2-nistp256" /tmp/authorized-keys
grep "my-top-secret-key 🐱" /tmp/authorized-keys
(! userdbctl ssh-authorized-keys 🐱)
(! userdbctl ssh-authorized-keys dropin-user --chain)
(! userdbctl ssh-authorized-keys dropin-user --chain '')
(! SYSTEMD_LOG_LEVEL=debug userdbctl ssh-authorized-keys dropin-user --chain /usr/bin/false)
(! userdbctl '')
for opt in json multiplexer output synthesize with-dropin with-nss with-varlink; do
(! userdbctl "--$opt=''")
(! userdbctl "--$opt='🐱'")
(! userdbctl "--$opt=foo")
(! userdbctl "--$opt=foo" "--$opt=''" "--$opt=🐱")
done
# FIXME: sshd seems to crash inside asan currently, skip the actual ssh test hence
if command -v ssh >/dev/null && command -v sshd >/dev/null && ! [[ -v ASAN_OPTIONS ]]; then
at_exit() {
set +e
systemctl is-active -q mysshserver.socket && systemctl stop mysshserver.socket
rm -f /tmp/homed.id_ecdsa /run/systemd/system/mysshserver{@.service,.socket}
systemctl daemon-reload
wait_for_state homedsshtest inactive
homectl remove homedsshtest
for dir in /etc /usr/lib; do
if [[ -f "$dir/pam.d/sshd.bak" ]]; then
mv "$dir/pam.d/sshd.bak" "$dir/pam.d/sshd"
fi
done
}
trap at_exit EXIT
# Test that SSH logins work with delayed unlocking
ssh-keygen -N '' -C '' -t ecdsa -f /tmp/homed.id_ecdsa
NEWPASSWORD=hunter4711 homectl create \
--disk-size=min \
--luks-discard=yes \
--luks-pbkdf-type=pbkdf2 \
--luks-pbkdf-time-cost=1ms \
--rate-limit-interval=1s \
--rate-limit-burst=1000 \
--enforce-password-policy=no \
--ssh-authorized-keys=@/tmp/homed.id_ecdsa.pub \
--stop-delay=0 \
homedsshtest
homectl inspect homedsshtest
mkdir -p /etc/ssh
test -f /etc/ssh/ssh_host_ecdsa_key || ssh-keygen -t ecdsa -C '' -N '' -f /etc/ssh/ssh_host_ecdsa_key
# ssh wants this dir around, but distros cannot agree on a common name for it, let's just create all that
# are aware of distros use
mkdir -p /usr/share/empty.sshd /var/empty /var/empty/sshd /run/sshd
for dir in /etc /usr/lib; do
if [[ -f "$dir/pam.d/sshd" ]]; then
mv "$dir/pam.d/sshd" "$dir/pam.d/sshd.bak"
cat >"$dir/pam.d/sshd" <<EOF
auth [success=done authtok_err=bad perm_denied=bad maxtries=bad default=ignore] pam_systemd_home.so
auth sufficient pam_unix.so nullok
auth required pam_deny.so
account [success=done authtok_expired=bad new_authtok_reqd=bad maxtries=bad acct_expired=bad default=ignore] pam_systemd_home.so
account required pam_unix.so
session optional pam_systemd_home.so debug
session optional pam_systemd.so
session required pam_unix.so
EOF
break
fi
done
mkdir -p /etc/sshd/
cat >/etc/ssh/sshd_config <<EOF
AuthorizedKeysCommand /usr/bin/userdbctl ssh-authorized-keys %u
AuthorizedKeysCommandUser root
UsePAM yes
AcceptEnv PASSWORD
LogLevel DEBUG3
EOF
cat >/run/systemd/system/mysshserver.socket <<EOF
[Socket]
ListenStream=4711
Accept=yes
EOF
cat >/run/systemd/system/mysshserver@.service <<EOF
[Service]
ExecStart=-sshd -i -d -e
StandardInput=socket
StandardOutput=socket
StandardError=journal
EOF
systemctl daemon-reload
systemctl start mysshserver.socket
userdbctl user -j homedsshtest
ssh -t -t -4 -p 4711 -i /tmp/homed.id_ecdsa \
-o "SetEnv PASSWORD=hunter4711" -o "StrictHostKeyChecking no" \
homedsshtest@localhost echo zzz | tr -d '\r' | tee /tmp/homedsshtest.out
grep -E "^zzz$" /tmp/homedsshtest.out
rm /tmp/homedsshtest.out
ssh -t -t -4 -p 4711 -i /tmp/homed.id_ecdsa \
-o "SetEnv PASSWORD=hunter4711" -o "StrictHostKeyChecking no" \
homedsshtest@localhost env
trap - EXIT
at_exit
fi
NEWPASSWORD=hunter4711 homectl create aliastest --storage=directory --alias=aliastest2 --alias=aliastest3 --realm=myrealm
homectl inspect aliastest
homectl inspect aliastest2
homectl inspect aliastest3
homectl inspect aliastest@myrealm
homectl inspect aliastest2@myrealm
homectl inspect aliastest3@myrealm
userdbctl user aliastest
userdbctl user aliastest2
userdbctl user aliastest3
userdbctl user aliastest@myrealm
userdbctl user aliastest2@myrealm
userdbctl user aliastest3@myrealm
getent passwd aliastest
getent passwd aliastest2
getent passwd aliastest3
getent passwd aliastest@myrealm
getent passwd aliastest2@myrealm
getent passwd aliastest3@myrealm
homectl remove aliastest
NEWPASSWORD=quux homectl create tmpfsquota --storage=subvolume --dev-shm-limit=50K --tmp-limit=50K -P
for p in /dev/shm /tmp; do
if findmnt -n -o options "$p" | grep -q usrquota; then
# Check if we can display the quotas. If we cannot, than it's likely
# that PID1 was also not able to set the limits and we should not fail
# in the tests below.
/usr/lib/systemd/tests/unit-tests/manual/test-display-quota tmpfsquota "$p" || set +e
run0 --property=SetCredential=pam.authtok.systemd-run0:quux -u tmpfsquota dd if=/dev/zero of="$p/quotatestfile1" bs=1024 count=30
(! run0 --property=SetCredential=pam.authtok.systemd-run0:quux -u tmpfsquota dd if=/dev/zero of="$p/quotatestfile2" bs=1024 count=30)
run0 --property=SetCredential=pam.authtok.systemd-run0:quux -u tmpfsquota rm "$p/quotatestfile1" "$p/quotatestfile2"
run0 --property=SetCredential=pam.authtok.systemd-run0:quux -u tmpfsquota dd if=/dev/zero of="$p/quotatestfile1" bs=1024 count=30
run0 --property=SetCredential=pam.authtok.systemd-run0:quux -u tmpfsquota rm "$p/quotatestfile1"
set -e
fi
done
systemctl stop user@"$(id -u tmpfsquota)".service
wait_for_state tmpfsquota inactive
homectl remove tmpfsquota
NEWPASSWORD=quux homectl create subareatest --storage=subvolume -P
run0 --property=SetCredential=pam.authtok.systemd-run0:quux -u subareatest mkdir Areas
run0 --property=SetCredential=pam.authtok.systemd-run0:quux -u subareatest cp -av /etc/skel Areas/furb
run0 --property=SetCredential=pam.authtok.systemd-run0:quux -u subareatest cp -av /etc/skel Areas/molb
run0 --property=SetCredential=pam.authtok.systemd-run0:quux -u subareatest ln -s /home/srub Areas/srub
run0 --property=SetCredential=pam.authtok.systemd-run0:quux -u subareatest ln -s /root Areas/root
test "$(run0 --property=SetCredential=pam.authtok.systemd-run0:quux -u subareatest sh -c 'echo $HOME')" = "/home/subareatest"
test "$(run0 --property=SetCredential=pam.authtok.systemd-run0:quux -u subareatest sh -c 'echo x$XDG_AREA')" = "x"
test "$(run0 --property=SetCredential=pam.authtok.systemd-run0:quux -u subareatest sh -c 'echo $XDG_RUNTIME_DIR')" = "/run/user/$(id -u subareatest)"
test "$(run0 --property=SetCredential=pam.authtok.systemd-run0:quux -u subareatest --area=furb sh -c 'echo $HOME')" = "/home/subareatest/Areas/furb"
test "$(run0 --property=SetCredential=pam.authtok.systemd-run0:quux -u subareatest --area=furb sh -c 'echo $XDG_AREA')" = "furb"
test "$(run0 --property=SetCredential=pam.authtok.systemd-run0:quux -u subareatest --area=furb sh -c 'echo $XDG_RUNTIME_DIR')" = "/run/user/$(id -u subareatest)/Areas/furb"
PASSWORD=quux homectl update subareatest --default-area=molb
test "$(run0 --property=SetCredential=pam.authtok.systemd-run0:quux -u subareatest sh -c 'echo $HOME')" = "/home/subareatest/Areas/molb"
test "$(run0 --property=SetCredential=pam.authtok.systemd-run0:quux -u subareatest sh -c 'echo $XDG_AREA')" = "molb"
test "$(run0 --property=SetCredential=pam.authtok.systemd-run0:quux -u subareatest sh -c 'echo $XDG_RUNTIME_DIR')" = "/run/user/$(id -u subareatest)/Areas/molb"
test "$(run0 --property=SetCredential=pam.authtok.systemd-run0:quux -u subareatest --area=furb sh -c 'echo $HOME')" = "/home/subareatest/Areas/furb"
test "$(run0 --property=SetCredential=pam.authtok.systemd-run0:quux -u subareatest --area=furb sh -c 'echo $XDG_AREA')" = "furb"
test "$(run0 --property=SetCredential=pam.authtok.systemd-run0:quux -u subareatest --area=furb sh -c 'echo $XDG_RUNTIME_DIR')" = "/run/user/$(id -u subareatest)/Areas/furb"
# Install a PK rule that allows 'subareatest' user to invoke run0 without password, just for testing
cat >/usr/share/polkit-1/rules.d/subareatest.rules <<'EOF'
polkit.addRule(function(action, subject) {
if (action.id == "org.freedesktop.systemd1.manage-units" &&
subject.user == "subareatest") {
return polkit.Result.YES;
}
});
EOF
# Test "recursive" operation
test "$(run0 --property=SetCredential=pam.authtok.systemd-run0:quux -u subareatest --area=furb run0 --property=SetCredential=pam.authtok.systemd-run0:quux -u subareatest --area=molb sh -c 'echo $HOME')" = "/home/subareatest/Areas/molb"
test "$(run0 --property=SetCredential=pam.authtok.systemd-run0:quux -u subareatest --area=furb run0 --property=SetCredential=pam.authtok.systemd-run0:quux -u subareatest --area=molb sh -c 'echo $XDG_AREA')" = "molb"
test "$(run0 --property=SetCredential=pam.authtok.systemd-run0:quux -u subareatest --area=furb run0 --property=SetCredential=pam.authtok.systemd-run0:quux -u subareatest --area=molb sh -c 'echo $XDG_RUNTIME_DIR')" = "/run/user/$(id -u subareatest)/Areas/molb"
test "$(run0 --property=SetCredential=pam.authtok.systemd-run0:quux -u subareatest --area=furb run0 --property=SetCredential=pam.authtok.systemd-run0:quux -u subareatest --area=molb run0 --property=SetCredential=pam.authtok.systemd-run0:quux -u subareatest --area=furb sh -c 'echo $HOME')" = "/home/subareatest/Areas/furb"
test "$(run0 --property=SetCredential=pam.authtok.systemd-run0:quux -u subareatest --area=furb run0 --property=SetCredential=pam.authtok.systemd-run0:quux -u subareatest --area=molb run0 --property=SetCredential=pam.authtok.systemd-run0:quux -u subareatest --area=furb sh -c 'echo $XDG_AREA')" = "furb"
test "$(run0 --property=SetCredential=pam.authtok.systemd-run0:quux -u subareatest --area=furb run0 --property=SetCredential=pam.authtok.systemd-run0:quux -u subareatest --area=molb run0 --property=SetCredential=pam.authtok.systemd-run0:quux -u subareatest --area=furb sh -c 'echo $XDG_RUNTIME_DIR')" = "/run/user/$(id -u subareatest)/Areas/furb"
# Test symlinked area
mkdir -p /home/srub
chown subareatest:subareatest /home/srub
test "$(run0 --property=SetCredential=pam.authtok.systemd-run0:quux -u subareatest --area=srub sh -c 'echo $HOME')" = "/home/srub"
test "$(run0 --property=SetCredential=pam.authtok.systemd-run0:quux -u subareatest --area=srub sh -c 'echo $XDG_AREA')" = "srub"
test "$(run0 --property=SetCredential=pam.authtok.systemd-run0:quux -u subareatest --area=srub sh -c 'echo $XDG_RUNTIME_DIR')" = "/run/user/$(id -u subareatest)/Areas/srub"
# Verify that login into an area not owned by target user will be redirected to main area
test "$(run0 --property=SetCredential=pam.authtok.systemd-run0:quux -u subareatest --area=root sh -c 'echo $HOME')" = "/home/subareatest"
test "$(run0 --property=SetCredential=pam.authtok.systemd-run0:quux -u subareatest --area=root sh -c 'echo x$XDG_AREA')" = "x"
test "$(run0 --property=SetCredential=pam.authtok.systemd-run0:quux -u subareatest --area=root sh -c 'echo $XDG_RUNTIME_DIR')" = "/run/user/$(id -u subareatest)"
systemctl stop user@"$(id -u subareatest)".service
wait_for_state subareatest inactive
homectl remove subareatest
# Test signing key logic
homectl list-signing-keys | grep -q local.public
(! (homectl list-signing-keys | grep -q signtest.public))
IDENTITY='{"userName":"signtest","storage":"directory","disposition":"regular","privileged":{"hashedPassword":["$y$j9T$I5Wxfm.fyg.RRWlgWw.rI1$gnQqGtbpPexqxZJkWMq8FxQi5Swc.CWeKtM8LwvEUB6"]},"enforcePasswordPolicy":false,"lastChangeUSec":1740677608017608,"lastPasswordChangeUSec":1740677608017608,"signature":[{"data":"Gl4wtc0sMjVnsH6FQwG/0M+x0nLI5cvvdtSSCttUu1gNtXqYn0UI4wZi/7zX35ERht6XHWDlP4d6V8HiAst4Dg==","key":"-----BEGIN PUBLIC KEY-----\nMCowBQYDK2VwAyEA6uvVaP1vh7O6nIbiOcvyIHRl4ihYSs0R7ctxtz2Zu7E=\n-----END PUBLIC KEY-----\n"}],"secret":{"password":["test"]}}'
# Try with stripping the foreign signature first, this should just work
echo "$IDENTITY" | homectl create -P --identity=- --seize=yes
wait_for_state signtest inactive
homectl remove signtest
# No try again, and don't strip the signature. It will be refused.
(! (echo "$IDENTITY" | homectl create -P --identity=- --seize=no))
print_public_key() {
cat <<EOF
-----BEGIN PUBLIC KEY-----
MCowBQYDK2VwAyEA6uvVaP1vh7O6nIbiOcvyIHRl4ihYSs0R7ctxtz2Zu7E=
-----END PUBLIC KEY-----
EOF
}
# Let's now add the signing key
print_public_key | homectl add-signing-key --key-name=signtest.public
homectl get-signing-key signtest.public | cmp - <(print_public_key)
homectl list-signing-keys | grep -q local.public
homectl list-signing-keys | grep -q signtest.public
# Now create the account with this, it should work now
echo "$IDENTITY" | homectl create -P --identity=- --seize=no
# Verify we can log in
PASSWORD="test" homectl with signtest true
# Remove the key, and check again ,should fail now
wait_for_state signtest inactive
homectl remove-signing-key signtest.public
wait_for_state signtest inactive
(! PASSWORD="test" homectl with signtest true)
# Verify key is really gone
homectl list-signing-keys | grep -q local.public
(! (homectl list-signing-keys | grep -q signtest.public))
# Test unregister + adopt
mkdir /home/elsewhere
mv /home/signtest.homedir /home/elsewhere/
wait_for_state signtest absent
homectl unregister signtest
print_public_key | homectl add-signing-key --key-name=signtest.public
homectl adopt /home/elsewhere/signtest.homedir
PASSWORD="test" homectl with signtest true
# Test register
wait_for_state signtest inactive
homectl unregister signtest
homectl register /home/elsewhere/signtest.homedir/.identity
wait_for_state signtest absent
homectl unregister signtest
# Test automatic fixation for anything in /home/
mv /home/elsewhere/signtest.homedir /home
rmdir /home/elsewhere
wait_for_exist signtest
PASSWORD="test" homectl with signtest true
# add signing key via credential
wait_for_state signtest inactive
homectl remove-signing-key signtest.public
(! (homectl list-signing-keys | grep -q signtest.public))
systemd-run --wait -p "SetCredential=home.add-signing-key.signtest.public:$(print_public_key)" homectl firstboot
homectl list-signing-keys | grep -q signtest.public
# register user via credential
mkdir /home/elsewhere2
mv /home/signtest.homedir /home/elsewhere2/
wait_for_state signtest absent
homectl unregister signtest
systemd-run --wait -p "LoadCredential=home.register.signtest:/home/elsewhere2/signtest.homedir/.identity" homectl firstboot
homectl inspect signtest
wait_for_state signtest absent
homectl unregister signtest
mv /home/elsewhere2/signtest.homedir /home/
rmdir /home/elsewhere2
# Remove it all again
wait_for_exist signtest
homectl remove-signing-key signtest.public
homectl remove signtest
# Test positive and negative matching
NEWPASSWORD=test homectl create --storage=directory --nice=5 -P matchtest
homectl inspect matchtest
homectl inspect matchtest | grep "Nice: 5"
PASSWORD=test homectl update -N --nice=7 -T --nice=3 matchtest
homectl inspect matchtest
homectl inspect matchtest | grep "Nice: 3"
PASSWORD=test homectl update -A --default-area=quux1 matchtest
homectl inspect matchtest
homectl inspect matchtest | grep "Area: quux1"
PASSWORD=test homectl update -N --default-area=quux2 matchtest
homectl inspect matchtest
homectl inspect matchtest | grep "Area: quux1"
PASSWORD=test homectl update -T --default-area=quux3 matchtest
homectl inspect matchtest
homectl inspect matchtest | grep "Area: quux3"
homectl remove matchtest
touch /testok