2 # SPDX-License-Identifier: LGPL-2.1-or-later
6 # Check if homectl is installed, and if it isn't bail out early instead of failing
7 if ! test -x /usr
/bin
/homectl
; then
8 echo "no homed" >/skipped
13 # As updating disk-size-related attributes can take some time on some
14 # filesystems, let's drop these fields before comparing the outputs to
15 # avoid unexpected fails. To see the full outputs of both homectl &
16 # userdbctl (for debugging purposes) drop the fields just before the
18 local USERNAME
="${1:?}"
19 homectl inspect
"$USERNAME" |
tee /tmp
/a
20 userdbctl user
"$USERNAME" |
tee /tmp
/b
22 # diff uses the grep BREs for pattern matching
23 diff -I '^\s*Disk \(Size\|Free\|Floor\|Ceiling\):' /tmp
/{a
,b
}
26 homectl inspect
--json=pretty
"$USERNAME"
31 (( i
> 1 )) && sleep 0.5
32 homectl inspect
"$1" |
grep -qF "State: $2" && break
36 systemd-analyze log-level debug
37 systemctl service-log-level systemd-homed debug
39 # Create a tmpfs to use as backing store for the home dir. That way we can enforce a size limit nicely.
41 mount
-t tmpfs tmpfs
/home
-o size
=290M
43 # we enable --luks-discard= since we run our tests in a tight VM, hence don't
44 # needlessly pressure for storage. We also set the cheapest KDF, since we don't
45 # want to waste CI CPU cycles on it.
46 NEWPASSWORD
=xEhErW0ndafV4s homectl create test-user \
49 --image-path=/home
/test-user.home \
50 --luks-pbkdf-type=pbkdf2 \
51 --luks-pbkdf-time-cost=1ms
54 PASSWORD
=xEhErW0ndafV4s homectl authenticate test-user
56 PASSWORD
=xEhErW0ndafV4s homectl activate test-user
59 PASSWORD
=xEhErW0ndafV4s homectl update test-user
--real-name="Inline test"
62 homectl deactivate test-user
65 PASSWORD
=xEhErW0ndafV4s NEWPASSWORD
=yPN4N0fYNKUkOq homectl passwd test-user
68 PASSWORD
=yPN4N0fYNKUkOq homectl activate test-user
71 SYSTEMD_LOG_LEVEL
=debug PASSWORD
=yPN4N0fYNKUkOq NEWPASSWORD
=xEhErW0ndafV4s homectl passwd test-user
74 homectl deactivate test-user
77 PASSWORD
=xEhErW0ndafV4s homectl activate test-user
80 homectl deactivate test-user
83 PASSWORD
=xEhErW0ndafV4s homectl update test-user
--real-name="Offline test"
86 PASSWORD
=xEhErW0ndafV4s homectl activate test-user
89 homectl deactivate test-user
92 # Do some resize tests, but only if we run on real kernels, as quota inside of containers will fail
93 if ! systemd-detect-virt
-cq ; then
95 PASSWORD
=xEhErW0ndafV4s homectl resize test-user
300M
98 # minimize while inactive
99 PASSWORD
=xEhErW0ndafV4s homectl resize test-user min
102 PASSWORD
=xEhErW0ndafV4s homectl activate test-user
106 PASSWORD
=xEhErW0ndafV4s homectl resize test-user max
109 # minimize while active
110 PASSWORD
=xEhErW0ndafV4s homectl resize test-user
0
114 PASSWORD
=xEhErW0ndafV4s homectl resize test-user
300M
117 # shrink to original size while active
118 PASSWORD
=xEhErW0ndafV4s homectl resize test-user
256M
122 PASSWORD
=xEhErW0ndafV4s homectl resize test-user min
125 # Increase space, so that we can reasonably rebalance free space between to home dirs
126 mount
/home
-o remount
,size
=800M
129 NEWPASSWORD
=uuXoo8ei homectl create test-user2 \
132 --image-path=/home
/test-user2.home \
133 --luks-pbkdf-type=pbkdf2 \
134 --luks-pbkdf-time-cost=1ms
137 # activate second user
138 PASSWORD
=uuXoo8ei homectl activate test-user2
141 # set second user's rebalance weight to 100
142 PASSWORD
=uuXoo8ei homectl update test-user2
--rebalance-weight=100
145 # set first user's rebalance weight to quarter of that of the second
146 PASSWORD
=xEhErW0ndafV4s homectl update test-user
--rebalance-weight=25
149 # synchronously rebalance
155 PASSWORD
=xEhErW0ndafV4s homectl with test-user
-- test ! -f /home
/test-user
/xyz
156 (! PASSWORD
=xEhErW0ndafV4s homectl with test-user
-- test -f /home
/test-user
/xyz
)
157 PASSWORD
=xEhErW0ndafV4s homectl with test-user
-- touch /home
/test-user
/xyz
158 PASSWORD
=xEhErW0ndafV4s homectl with test-user
-- test -f /home
/test-user
/xyz
159 # CAREFUL adding more `homectl with` tests here. Auth can get rate-limited and cause the tests to fail.
161 wait_for_state test-user inactive
162 homectl remove test-user
164 if ! systemd-detect-virt
-cq ; then
165 wait_for_state test-user2 active
166 homectl deactivate test-user2
167 wait_for_state test-user2 inactive
168 homectl remove test-user2
171 # blob directory tests
172 # See docs/USER_RECORD_BLOB_DIRS.md
174 test -f "/var/cache/systemd/home/blob-user/$1"
175 stat
-c "%u %#a" "/var/cache/systemd/home/blob-user/$1" |
grep "^0 0644"
176 test -f "/home/blob-user/.identity-blob/$1"
177 stat
-c "%u %#a" "/home/blob-user/.identity-blob/$1" |
grep "^12345 0644"
179 diff "/var/cache/systemd/home/blob-user/$1" "$2"
180 diff "/var/cache/systemd/home/blob-user/$1" "/home/blob-user/.identity-blob/$1"
183 mkdir
/tmp
/blob1
/tmp
/blob2
184 echo data1 blob1
> /tmp
/blob
1/test1
185 echo data1 blob2
> /tmp
/blob
2/test1
186 echo data2 blob1
> /tmp
/blob
1/test2
187 echo data2 blob2
> /tmp
/blob
2/test2
188 echo invalid filename
> /tmp
/blob
1/ΡΠ°ΠΉΠ»
189 echo data3
> /tmp
/external-test3
190 echo avatardata
> /tmp
/external-avatar
191 ln -s /tmp
/external-avatar
/tmp
/external-avatar-lnk
192 dd if=/dev
/urandom of
=/tmp
/external-barely-fits bs
=1M count
=64
193 dd if=/dev
/urandom of
=/tmp
/external-toobig bs
=1M count
=65
195 # create w/ prepopulated blob dir
196 NEWPASSWORD
=EMJuc3zQaMibJo homectl create blob-user \
197 --disk-size=min
--luks-discard=yes \
198 --luks-pbkdf-type=pbkdf2
--luks-pbkdf-time-cost=1ms \
202 PASSWORD
=EMJuc3zQaMibJo homectl activate blob-user
205 test -d /var
/cache
/systemd
/home
/blob-user
206 stat
-c "%u %#a" /var
/cache
/systemd
/home
/blob-user |
grep "^0 0755"
207 test -d /home
/blob-user
/.identity-blob
208 stat
-c "%u %#a" /home
/blob-user
/.identity-blob |
grep "^12345 0700"
210 checkblob test1
/tmp
/blob
1/test1
211 (! checkblob test1
/tmp
/blob
2/test1
)
212 checkblob test2
/tmp
/blob
1/test2
213 (! checkblob test2
/tmp
/blob
2/test2
)
214 (! checkblob ΡΠ°ΠΈΠ»
/tmp
/blob
1/ΡΠ°ΠΈΠ»
)
215 (! checkblob test3
/tmp
/external-test3
)
216 (! checkblob avatar
/tmp
/external-avatar
)
218 # append files to existing blob, both well-known and other
219 PASSWORD
=EMJuc3zQaMibJo homectl update blob-user \
220 -b test3
=/tmp
/external-test3
--avatar=/tmp
/external-avatar
222 checkblob test1
/tmp
/blob
1/test1
223 (! checkblob test1
/tmp
/blob
2/test1
)
224 checkblob test2
/tmp
/blob
1/test2
225 (! checkblob test2
/tmp
/blob
2/test2
)
226 (! checkblob ΡΠ°ΠΈΠ»
/tmp
/blob
1/ΡΠ°ΠΈΠ»
)
227 checkblob test3
/tmp
/external-test3
228 checkblob avatar
/tmp
/external-avatar
230 # delete files from existing blob, both well-known and other
231 PASSWORD
=EMJuc3zQaMibJo homectl update blob-user \
234 checkblob test1
/tmp
/blob
1/test1
235 (! checkblob test1
/tmp
/blob
2/test1
)
236 checkblob test2
/tmp
/blob
1/test2
237 (! checkblob test2
/tmp
/blob
2/test2
)
238 (! checkblob ΡΠ°ΠΈΠ»
/tmp
/blob
1/ΡΠ°ΠΈΠ»
)
239 (! checkblob test3
/tmp
/external-test3
)
240 (! checkblob avatar
/tmp
/external-avatar
)
242 # swap entire blob directory
243 PASSWORD
=EMJuc3zQaMibJo homectl update blob-user \
246 (! checkblob test1
/tmp
/blob
1/test1
)
247 checkblob test1
/tmp
/blob
2/test1
248 (! checkblob test2
/tmp
/blob
1/test2
)
249 checkblob test2
/tmp
/blob
2/test2
250 (! checkblob ΡΠ°ΠΈΠ»
/tmp
/blob
1/ΡΠ°ΠΈΠ»
)
251 (! checkblob test3
/tmp
/external-test3
)
252 (! checkblob avatar
/tmp
/external-avatar
)
254 # create and delete files while swapping blob directory. Also symlinks.
255 PASSWORD
=EMJuc3zQaMibJo homectl update blob-user \
256 -b /tmp
/blob1
-b test2
= -b test3
=/tmp
/external-test3
--avatar=/tmp
/external-avatar-lnk
258 checkblob test1
/tmp
/blob
1/test1
259 (! checkblob test1
/tmp
/blob
2/test1
)
260 (! checkblob test2
/tmp
/blob
1/test2
)
261 (! checkblob test2
/tmp
/blob
2/test2
)
262 (! checkblob ΡΠ°ΠΈΠ»
/tmp
/blob
1/ΡΠ°ΠΈΠ»
)
263 checkblob test3
/tmp
/external-test3
264 checkblob avatar
/tmp
/external-avatar
# target of the link
266 # clear the blob directory
267 PASSWORD
=EMJuc3zQaMibJo homectl update blob-user \
268 -b /tmp
/blob2
-b test3
=/tmp
/external-test3
--blob=
270 (! checkblob test1
/tmp
/blob
1/test1
)
271 (! checkblob test1
/tmp
/blob
2/test1
)
272 (! checkblob test2
/tmp
/blob
1/test2
)
273 (! checkblob test2
/tmp
/blob
2/test2
)
274 (! checkblob ΡΠ°ΠΈΠ»
/tmp
/blob
1/ΡΠ°ΠΈΠ»
)
275 (! checkblob test3
/tmp
/external-test3
)
276 (! checkblob avatar
/tmp
/external-avatar
)
278 # file that's exactly 64M still fits
279 PASSWORD
=EMJuc3zQaMibJo homectl update blob-user \
280 -b barely-fits
=/tmp
/external-barely-fits
281 (! checkblob test1
/tmp
/blob
1/test1
)
282 (! checkblob test1
/tmp
/blob
2/test1
)
283 (! checkblob test2
/tmp
/blob
1/test2
)
284 (! checkblob test2
/tmp
/blob
2/test2
)
285 (! checkblob ΡΠ°ΠΈΠ»
/tmp
/blob
1/ΡΠ°ΠΈΠ»
)
286 (! checkblob test3
/tmp
/external-test3
)
287 (! checkblob avatar
/tmp
/external-avatar
)
288 checkblob barely-fits
/tmp
/external-barely-fits
290 # error out if the file is too big
291 (! PASSWORD
=EMJuc3zQaMibJo homectl update blob-user
-b huge
=/tmp
/external-toobig
)
293 # error out if filenames are invalid
294 (! PASSWORD
=EMJuc3zQaMibJo homectl update blob-user
-b .hidden
=/tmp
/external-test3
)
295 (! PASSWORD
=EMJuc3zQaMibJo homectl update blob-user
-b "with spaces=/tmp/external-test3" )
296 (! PASSWORD
=EMJuc3zQaMibJo homectl update blob-user
-b with
=equals
=/tmp
/external-test3
)
297 (! PASSWORD
=EMJuc3zQaMibJo homectl update blob-user
-b ΡΠ°ΠΉΠ»
=/tmp
/external-test3
)
298 (! PASSWORD
=EMJuc3zQaMibJo homectl update blob-user
-b special@chars
=/tmp
/external-test3
)
300 homectl deactivate blob-user
301 wait_for_state blob-user inactive
302 homectl remove blob-user
307 # Create a couple of user/group records to test io.systemd.DropIn
308 # See docs/_groups/USER_RECORD.md and docs/_groups/GROUP_RECORD.md
309 mkdir
-p /run
/userdb
/
310 cat >"/run/userdb/dropingroup.group" <<\EOF
312 "groupName" : "dropingroup",
316 cat >"/run/userdb/dropinuser.user" <<\EOF
318 "userName" : "dropinuser",
326 cat >"/run/userdb/dropinuser.user-privileged" <<\EOF
330 "$6$WHBKvAFFT9jKPA4k$OPY4D4TczKN/jOnJzy54DDuOOagCcvxxybrwMbe1SVdm.Bbr.zOmBdATp.QrwZmvqyr8/SafbbQu.QZ2rRvDs/"
332 "sshAuthorizedKeys" : [
333 "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIA//dxI2xLg4MgxIKKZv1nqwTEIlE/fdakii2Fb75pG+ foo@bar.tld",
334 "ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBMlaqG2rTMje5CQnfjXJKmoSpEVJ2gWtx4jBvsQbmee2XbU/Qdq5+SRisssR9zVuxgg5NA5fv08MgjwJQMm+csc= hello@world.tld"
339 # Set permissions and create necessary symlinks as described in nss-systemd(8)
340 chmod 0600 "/run/userdb/dropinuser.user-privileged"
341 ln -svrf "/run/userdb/dropingroup.group" "/run/userdb/1000000.group"
342 ln -svrf "/run/userdb/dropinuser.user" "/run/userdb/2000000.user"
343 ln -svrf "/run/userdb/dropinuser.user-privileged" "/run/userdb/2000000.user-privileged"
347 userdbctl
--help --no-pager
348 userdbctl
--no-legend
349 userdbctl
--output=classic
350 userdbctl
--output=friendly
351 userdbctl
--output=table
352 userdbctl
--output=json | jq
353 userdbctl
-j --json=pretty | jq
354 userdbctl
-j --json=short | jq
355 userdbctl
--with-varlink=no
358 userdbctl user testuser
360 userdbctl user testuser root
361 userdbctl user
-j testuser root | jq
362 # Check only UID for the nobody user, since the name is build-configurable
363 userdbctl user
--with-nss=no
--synthesize=yes
364 userdbctl user
--with-nss=no
--synthesize=yes 0 root
65534
365 userdbctl user dropinuser
366 userdbctl user
2000000
367 userdbctl user
--with-nss=no
--with-varlink=no
--synthesize=no
--multiplexer=no dropinuser
368 userdbctl user
--with-nss=no
2000000
369 (! userdbctl user
'')
370 (! userdbctl user π±
)
371 (! userdbctl user π±
'' bar
)
372 (! userdbctl user i-do-not-exist
)
373 (! userdbctl user root i-do-not-exist testuser
)
374 (! userdbctl user
--with-nss=no
--synthesize=no
0 root
65534)
375 (! userdbctl user
-N root nobody
)
376 (! userdbctl user
--with-dropin=no dropinuser
)
377 (! userdbctl user
--with-dropin=no
2000000)
380 userdbctl group testuser
382 userdbctl group testuser root
383 userdbctl group
-j testuser root | jq
384 # Check only GID for the nobody group, since the name is build-configurable
385 userdbctl group
--with-nss=no
--synthesize=yes
386 userdbctl group
--with-nss=no
--synthesize=yes 0 root
65534
387 userdbctl group dropingroup
388 userdbctl group
1000000
389 userdbctl group
--with-nss=no
--with-varlink=no
--synthesize=no
--multiplexer=no dropingroup
390 userdbctl group
--with-nss=no
1000000
391 (! userdbctl group
'')
392 (! userdbctl group π±
)
393 (! userdbctl group π±
'' bar
)
394 (! userdbctl group i-do-not-exist
)
395 (! userdbctl group root i-do-not-exist testuser
)
396 (! userdbctl group
--with-nss=no
--synthesize=no
0 root
65534)
397 (! userdbctl group
--with-dropin=no dropingroup
)
398 (! userdbctl group
--with-dropin=no
1000000)
400 userdbctl users-in-group
401 userdbctl users-in-group testuser
402 userdbctl users-in-group testuser root
403 userdbctl users-in-group
-j testuser root | jq
404 userdbctl users-in-group π±
405 (! userdbctl users-in-group
'')
406 (! userdbctl users-in-group foo
'' bar
)
408 userdbctl groups-of-user
409 userdbctl groups-of-user testuser
410 userdbctl groups-of-user testuser root
411 userdbctl groups-of-user
-j testuser root | jq
412 userdbctl groups-of-user π±
413 (! userdbctl groups-of-user
'')
414 (! userdbctl groups-of-user foo
'' bar
)
417 userdbctl services
-j | jq
419 varlinkctl call
/run
/systemd
/userdb
/io.systemd.Multiplexer io.systemd.UserDatabase.GetUserRecord
'{"userName":"testuser","service":"io.systemd.Multiplexer"}'
420 varlinkctl call
/run
/systemd
/userdb
/io.systemd.Multiplexer io.systemd.UserDatabase.GetUserRecord
'{"userName":"root","service":"io.systemd.Multiplexer"}'
421 varlinkctl call
/run
/systemd
/userdb
/io.systemd.Multiplexer io.systemd.UserDatabase.GetUserRecord
'{"userName":"dropinuser","service":"io.systemd.Multiplexer"}'
422 varlinkctl call
/run
/systemd
/userdb
/io.systemd.Multiplexer io.systemd.UserDatabase.GetUserRecord
'{"uid":2000000,"service":"io.systemd.Multiplexer"}'
423 (! varlinkctl call
/run
/systemd
/userdb
/io.systemd.Multiplexer io.systemd.UserDatabase.GetUserRecord
'{"userName":"","service":"io.systemd.Multiplexer"}')
424 (! varlinkctl call
/run
/systemd
/userdb
/io.systemd.Multiplexer io.systemd.UserDatabase.GetUserRecord
'{"userName":"π±","service":"io.systemd.Multiplexer"}')
425 (! varlinkctl call
/run
/systemd
/userdb
/io.systemd.Multiplexer io.systemd.UserDatabase.GetUserRecord
'{"userName":"i-do-not-exist","service":"io.systemd.Multiplexer"}')
427 userdbctl ssh-authorized-keys dropinuser |
tee /tmp
/authorized-keys
428 grep "ssh-ed25519" /tmp
/authorized-keys
429 grep "ecdsa-sha2-nistp256" /tmp
/authorized-keys
430 echo "my-top-secret-key π±" >/tmp
/my-top-secret-key
431 userdbctl ssh-authorized-keys dropinuser
--chain /bin
/cat /tmp
/my-top-secret-key |
tee /tmp
/authorized-keys
432 grep "ssh-ed25519" /tmp
/authorized-keys
433 grep "ecdsa-sha2-nistp256" /tmp
/authorized-keys
434 grep "my-top-secret-key π±" /tmp
/authorized-keys
435 (! userdbctl ssh-authorized-keys π±
)
436 (! userdbctl ssh-authorized-keys dropin-user
--chain)
437 (! userdbctl ssh-authorized-keys dropin-user
--chain '')
438 (! SYSTEMD_LOG_LEVEL
=debug userdbctl ssh-authorized-keys dropin-user
--chain /bin
/false
)
441 for opt
in json multiplexer output synthesize with-dropin with-nss with-varlink
; do
442 (! userdbctl
"--$opt=''")
443 (! userdbctl
"--$opt='π±'")
444 (! userdbctl
"--$opt=foo")
445 (! userdbctl
"--$opt=foo" "--$opt=''" "--$opt=π±")
448 # FIXME: sshd seems to crash inside asan currently, skip the actual ssh test hence
449 if command -v ssh &> /dev
/null
&& command -v sshd
&> /dev
/null
&& ! [[ -v ASAN_OPTIONS
]]; then
452 systemctl stop mysshserver.socket
453 rm -f /tmp
/homed.id_rsa
/run
/systemd
/system
/mysshserver.socket
/run
/systemd
/system
/mysshserver@.service
454 systemctl daemon-reload
455 homectl remove homedsshtest ||
:
456 mv /etc
/pam.d
/sshd.save46
/etc
/pam.d
/sshd
461 # Test that SSH logins work with delayed unlocking
462 ssh-keygen
-N '' -C '' -t rsa
-f /tmp
/homed.id_rsa
463 NEWPASSWORD
=hunter4711 homectl create \
466 --luks-pbkdf-type=pbkdf2 \
467 --luks-pbkdf-time-cost=1ms \
468 --enforce-password-policy=no \
469 --ssh-authorized-keys=@
/tmp
/homed.id_rsa.pub \
474 test -f /etc
/ssh
/ssh_host_rsa_key || ssh-keygen
-t rsa
-C '' -N '' -f /etc
/ssh
/ssh_host_rsa_key
476 # 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
477 mkdir
-p /usr
/share
/empty.sshd
/var
/empty
/var
/empty
/sshd
479 mv /etc
/pam.d
/sshd
/etc
/pam.d
/sshd.save46
481 cat > /etc
/pam.d
/sshd
<<EOF
482 auth sufficient pam_unix.so nullok
483 auth sufficient pam_systemd_home.so
484 auth required pam_deny.so
485 account sufficient pam_systemd_home.so
486 account sufficient pam_unix.so
487 account required pam_permit.so
488 session optional pam_systemd_home.so
489 session optional pam_systemd.so
490 session required pam_unix.so
493 cat >> /etc
/ssh
/sshd_config
<<EOF
494 AuthorizedKeysCommand /usr/bin/userdbctl ssh-authorized-keys %u
495 AuthorizedKeysCommandUser root
501 cat > /run
/systemd
/system
/mysshserver.socket
<<EOF
507 cat > /run
/systemd
/system
/mysshserver@.service
<<EOF
509 ExecStart=-/usr/sbin/sshd -i -d -e
511 StandardOutput=socket
512 StandardError=journal
515 systemctl daemon-reload
516 systemctl start mysshserver.socket
518 userdbctl user
-j homedsshtest
520 ssh -t -t -4 -p 4711 -i /tmp
/homed.id_rsa
-o "SetEnv PASSWORD=hunter4711" -o "StrictHostKeyChecking no" homedsshtest@localhost
echo zzz |
tail -n 1 |
tr -d '\r' > /tmp
/homedsshtest.out
521 cat /tmp
/homedsshtest.out
522 test "$(cat /tmp/homedsshtest.out)" = "zzz"
523 rm /tmp
/homedsshtest.out
525 ssh -t -t -4 -p 4711 -i /tmp
/homed.id_rsa
-o "SetEnv PASSWORD=hunter4711" -o "StrictHostKeyChecking no" homedsshtest@localhost env
527 wait_for_state homedsshtest inactive
528 homectl remove homedsshtest
531 systemd-analyze log-level info