Multi-user auto-login on OSX

Motivation

For a large software product, we use separate user-accounts for automatic builds. All build accounts are separate and non-administrator accounts.

The last step of the build-process packages the application and arranges application icons. This requires a Finder Process for the user running the build-process, which in turn requires the user to be logged-in.

Also for running select unit-tests, application windows are needed, which also depends on a local session.

General Setup

The process are launched by a cron-job which launches the build/test processes inside a screen-session. This allows to log the output (via screen) and also to attach to a running process if needed.

All build-users are password-less (empty passphrase) which facilitates fast-user switching and have ssh-key for remote access (by default ssh does not allow login with an empty password, make sure to keep this config)

The Problem

OSX only allows to auto-login a single user. There is no standard way to automatically log-in multiple users at startup.

Every time the machine boots, all build-users need to be logged in. With a password-less setup, it's easy enough to simply VNC into the machine (using a dedicated user-account/passphrase) and then fast-user-switch cycle through all users, but it's not always convenient to do so.

The Solution

A combination of various mechanisms is needed.

The general way to switch users from the commanline is easy enough:

 /System/Library/CoreServices/Menu\ Extras/User.menu/Contents/Resources/CGSession -switchToUserID <UID>

The crux with this command is, that it requires a session in the first place. Calling this command directly from launchd ~/Library/LaunchAgents/ does not work. CGSession sends a signal to the currently active session. In order to chain multiple automatic logins, the command needs be executes in the same Mach bootstrap namespace as the loginwindow which spawned the current session.

To put this all together, the solution is as follows:

  1. Autologin first user (using standard OSX mechanism: System Preferences > Accounts > Login Options)
  2. Login Items (Prefs > Accounts) for this user launches a script to switch user.
  3. Login Items for the newly logged in user switches to the next.. (chain)

which leaves us with some small further complications:

  • Login Items cannot directly launch a shell script.
  • Setting the Mach bootstrap environment (even for a owned process) requires admin privileges

The first is easily remedied. Using the `Script Editor` allows one to compile an AppleScript into an .app bundle. This applescript is intentionally left simple as to re-use it for various accounts:

autologin.app - AppleScript

do shell script "$HOME/bin/autologin.sh"


To work around the admin requirement, all local users are allowed to run a script via sudo:

sudoers – edit via `sudo visudo`

  %localaccounts ALL=(root) NOPASSWD: /usr/local/bin/switchuser.sh


The script that is used for switching users, it takes 2 arguments: current-username and the UID to switch to. Since this script is invoked via sudo, it is not trivial to find the calling user's name so this needs to be passed as argument.

/usr/local/bin/switchuser.sh

#!/bin/bash

LOGINPID=$(ps auxwwww \
      | grep -v grep \
      | fgrep /System/Library/CoreServices/loginwindow.app/Contents/MacOS/loginwindow \
      | egrep "^$1" \
      | awk '//{print $2;}')

launchctl bsexec $LOGINPID \
      "/System/Library/CoreServices/Menu Extras/User.menu/Contents/Resources/CGSession" \
      -switchToUserID $2


and finally a small script to tie it all together:

$HOME/bin/autologin.sh

#!/bin/sh
sudo /usr/local/bin/switchuser.sh `whoami` `id -u nextbuilduser`


This last script is the only one that differs for every user. nextbuilduser needs to be adjusted to chain logins. All user-accounts can share autostart.app as Login Item.

. The actual /usr/local/bin/switchuser.sh is a bit more elaborate to gracefully handle errors and prevent abuse with bogus parameters. There is a if test -z “$LOGINPID”; then case to catch potential PID lookup errors, and also contains a if test -z “$1” -o -z “$2”; then to verify that all parameters are given, as well as two `id` checks to see if the script is running as root and the target user exists. These are not essential and have been left out for brevity.

To sum up, the complete flow is:

  1. System auto-logs in first user
  2. Login-Item runs $HOME/bin/autologin.app after logon.
  3. the $HOME/bin/autologin.sh shell script is called from the .app
  4. the shell script determines current user `whoami` and the UID of the next user to log in `id -u name`.
  5. the shell script launches /usr/local/bin/switchuser.sh with admin privileges. Due to sudoers setup, no passphrase is needed for local-users to run this command.
  6. switchuser.sh triggers the actual user-switch.
  7. Go to Step 2

While this is all pretty straight forward, it's a complex setup for a simple task.

Note: if using empty-passwords is not an option, there are means to inject plaintext passwords, the switchuser.sh can in turn call an applescript (the password would become a 3rd parameter). e.g.

osascript << EOF
tell application "System Events"
  keystroke "yourpasswordhere" & return
end tell
EOF
 
wiki/osxautologin.txt · Last modified: 26.09.2015 22:34 by rgareus