From 5ec220d0a692022a36a558e1ccd3c12ccc5f7412 Mon Sep 17 00:00:00 2001 From: "Michael N. Lipp" Date: Thu, 27 Feb 2025 17:41:57 +0100 Subject: [PATCH] Use gids for id management and isolate home directories. --- dev-example/vmop-agent/vmop-agent | 43 +++++++++++++++++++++++++++---- 1 file changed, 38 insertions(+), 5 deletions(-) diff --git a/dev-example/vmop-agent/vmop-agent b/dev-example/vmop-agent/vmop-agent index ab4e765..99aa25d 100755 --- a/dev-example/vmop-agent/vmop-agent +++ b/dev-example/vmop-agent/vmop-agent @@ -15,28 +15,45 @@ if [ ! -w "$ttyPath" ]; then exit 1 fi +# Create fd for the tty in variable con if ! exec {con}<>"$ttyPath"; then echo >&2 "Cannot open device $ttyPath" exit 1 fi +# Temporary file for logging error messages, clear tty and signal ready temperr=$(mktemp) clear >/dev/tty1 echo >&${con} "220 Hello" +# This script uses the (shared) home directory as "dictonary" for +# synchronizing the username and the uid between hosts. +# +# Every user has a directory with his username. The directory is +# owned by root to prevent changes of access rights by the user. +# The uid and gid of the directory are equal. Thus the name of the +# directory and the id from the group ownership also provide the +# association between the username and the uid. + +# Add the user with name $1 to the host's "user database". This +# may not be invoked concurrently. createUser() { local missing=$1 local uid local userHome="/home/$missing" local createOpts="" + + # Retrieve or create the uid for the username if [ -d "$userHome" ]; then - uid=$(ls -ldn "$userHome" | head -n 1 | awk '{print $3}') + # If a home directory exists, use the id from the group ownership as uid + uid=$(ls -ldn "$userHome" | head -n 1 | awk '{print $4}') createOpts="--no-create-home" else - uid=$(ls -ln "/home" | tail -n +2 | awk '{print $3}' | sort | tail -1) + # Else get the maximum of all ids from the group ownership +1 + uid=$(ls -ln "/home" | tail -n +2 | awk '{print $4}' | sort | tail -1) uid=$(( $uid + 1 )) - if [ $uid -lt 1000 ]; then - uid=1000 + if [ $uid -lt 1100 ]; then + uid=1100 fi createOpts="--create-home" fi @@ -44,33 +61,45 @@ createUser() { useradd $missing -u $uid -g $uid $createOpts } +# Login the user, i.e. create a desktopn for the user. doLogin() { user=$1 if [ "$user" = "root" ]; then echo >&${con} "504 Won't log in root" return fi + + # Check if this user is already logged in on tty1 curUser=$(loginctl -j | jq -r '.[] | select(.tty=="tty1") | .user') if [ "$curUser" = "$user" ]; then echo >&${con} "201 User already logged in" return fi + + # Terminate a running desktop (fail safe) attemptLogout + + # Check if username is known on this host. If not, create user uid=$(id -u ${user} 2>/dev/null) if [ $? != 0 ]; then ( flock 200 createUser ${user} ) 200>/home/.gen-uid-lock + + # This should now work, else something went wrong uid=$(id -u ${user} 2>/dev/null) if [ $? != 0 ]; then echo >&${con} "451 Cannot determine uid" return fi fi + + # Start the desktop for the user systemd-run 2>$temperr \ --unit vmop-user-desktop --uid=$uid --gid=$uid \ --working-directory="/home/$user" -p TTYPath=/dev/tty1 \ -p PAMName=login -p StandardInput=tty -p StandardOutput=journal \ + -p Conflicts="gdm.service getty@tty1.service" \ -E XDG_RUNTIME_DIR="/run/user/$uid" \ -p ExecStartPre="/usr/bin/chvt 1" \ dbus-run-session -- gnome-shell --display-server --wayland @@ -81,6 +110,8 @@ doLogin() { fi } +# Attempt to log out a user currently using tty1. This is an intermediate +# operation that can be invoked from other operations attemptLogout() { systemctl status vmop-user-desktop > /dev/null 2>&1 if [ $? = 0 ]; then @@ -93,6 +124,8 @@ attemptLogout() { echo >&${con} "102 Desktop stopped" } +# Log out any user currently using tty1. This is invoked when executing +# the logout command and therefore sends back a 2xx return code. doLogout() { attemptLogout echo >&${con} "202 User logged out" @@ -101,7 +134,7 @@ doLogout() { while read line <&${con}; do case $line in "login "*) IFS=' ' read -ra args <<< "$line"; doLogin ${args[1]};; - logout) doLogout;; + "logout") doLogout;; esac done