TL;DR: It appears you can’t. Strato vServers seem to be containers instead of actual virtualized servers. I’ll update the post (or make a part two) in case I ever get it working. Until then, this post mainly serves to document what I tried.

So, I’ve recently tried utilizing systemd user sessions to autostart some services on a Strato server (running the default “Ubuntu 18.04 + Plesk” image), but it didn’t go well.

Upon trying some basic systemctl commands, I got greeted by the following:

tim@server:~$ systemctl --user status
Failed to connect to bus: No such file or directory

A quick Google search yielded that systemd user sessions appear to require the DBUS_SESSION_BUS_ADDRESS environment variable (and by extension, XDG_RUNTIME_DIR). A quick check of the env command output confirms that we don’t have those set:

tim@server:~$ env
LC_ALL=de_DE.utf8
LS_COLORS=<snip>
SSH_CONNECTION=xxx.xxx.xxx.xxx xxxxx xxx.xxx.xxx.xxx xxxxx
LESSCLOSE=/usr/bin/lesspipe %s %s
LANG=de_DE.utf8
USER=tim
PWD=/home/tim
HOME=/home/tim
SSH_CLIENT=xxx.xxx.xxx.xxx xxxxx xxxxx
SSH_TTY=/dev/pts/0
MAIL=/var/mail/tim
TERM=xterm-256color
SHELL=/bin/bash
SHLVL=1
LANGUAGE=de_DE.utf8
LOGNAME=tim
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin
LESSOPEN=| /usr/bin/lesspipe %s
_=/usr/bin/env

After some additional research, it became clear that systemd’s PAM module is in charge of setting up the latter environment variable, and it appears that it isn’t installed by default (so that’s at least a starting point).

Since we are likely going to mess with the PAM configs, lets make a backup of those (either for restoring later or for reference):

tim@server:~$ sudo cp -a /etc/pam.d /etc/pam.d.bak

Now, we can proceed with installing the pam_systemd module:

tim@server:~$ sudo apt install libpam-systemd
Reading package lists... Done
Building dependency tree       
Reading state information... Done
The following NEW packages will be installed:
  libpam-systemd
0 upgraded, 1 newly installed, 0 to remove and 0 not upgraded.
Need to get 107 kB of archives.
After this operation, 376 kB of additional disk space will be used.
Get:1 ftp://ftp.stratoserver.net/pub/linux/ubuntu bionic-updates/main amd64 libpam-systemd amd64 237-3ubuntu10.42 [107 kB]
Fetched 107 kB in 0s (2239 kB/s)   
Selecting previously unselected package libpam-systemd:amd64.
(Reading database ... 90983 files and directories currently installed.)
Preparing to unpack .../libpam-systemd_237-3ubuntu10.42_amd64.deb ...
Unpacking libpam-systemd:amd64 (237-3ubuntu10.42) ...
Setting up libpam-systemd:amd64 (237-3ubuntu10.42) ...
Processing triggers for man-db (2.8.3-2ubuntu0.1) ...

Now that the module is installed, we will regenerate the PAM configuration (it might already promt for that during the module installation):

tim@server:~$ sudo pam-auth-update

This shows a nice “graphical” dialogue on which modules to activate:

pam-auth-update dialogue with systemd option

“Unix authentication” and “Register user sessions in the systemd control group hierarchy” are enabled by default, and we’re fine with that, so just press Enter to confirm. After that, we are back in our shell.

Now, lets see what in our configuration actually changed, compared to the initial one (I’ve omitted changed .pam-old files and whitespace changes from the output, and fixed some whitespace inconsistencies to make it more readable):

--- /etc/pam.d/common-auth	2020-10-09 21:55:47.175777554 +0200
+++ /etc/pam.d/common-auth	2020-10-10 15:02:51.180819003 +0200
@@ -14,8 +14,7 @@
 # pam-auth-update(8) for details.
 
 # here are the per-package modules (the "Primary" block)
-auth	[success=2 default=ignore]	pam_unix.so nullok_secure
-auth	[success=1 default=ignore]	pam_plesk.so try_first_pass
+auth	[success=1 default=ignore]	pam_unix.so nullok_secure
 # here's the fallback if no module succeeds
 auth	requisite			pam_deny.so
 # prime the stack with a positive return value if there isn't one already;
--- /etc/pam.d/common-password	2020-10-09 21:55:47.175777554 +0200
+++ /etc/pam.d/common-password	2020-10-10 15:02:51.188819035 +0200
@@ -22,8 +22,7 @@
 # pam-auth-update(8) for details.
 
 # here are the per-package modules (the "Primary" block)
-password	optional			pam_plesk.so try_first_pass
-password	[success=1 default=ignore]	pam_unix.so obscure sha512 try_first_pass
+password	[success=1 default=ignore]	pam_unix.so obscure sha512
 # here's the fallback if no module succeeds
 password	requisite			pam_deny.so
 # prime the stack with a positive return value if there isn't one already;
--- /etc/pam.d/common-session	2020-10-09 21:45:47.678159983 +0200
+++ /etc/pam.d/common-session	2020-10-10 15:02:51.191819047 +0200
@@ -27,4 +27,5 @@
 session optional			pam_umask.so
 # and here are more per-package modules (the "Additional" block)
 session	required	pam_unix.so 
+session	optional	pam_systemd.so 
 # end of pam-auth-update config

For the people who are not well-versed in reading diffs, here is a breakdown/explanation of the changes:

  • pam_systemd.so got added to the session configuration.
  • pam_plesk.so vanished from the auth and password configurations.
  • Changed success= and try_first_pass lines do not affect us directly, they are a matter of load order (and will be fixed later).

While it’s great that pam_systemd.so shows up, the fact that pam_plesk.so (a module which we don’t know what it is for) simply vanished is not so great, so we’ll have to fix that. It appears like Strato simply added those two lines to the configuration files, but hasn’t added a configuration file for automatically generating those.

A simple fix for that would be to create that missing configuration file ourselves.

For that, lets open a new file called plesk-custom (to avoid potential conflicts down the line) in the PAM configurations folder:

tim@server:~$ sudo vim /usr/share/pam-configs/plesk-custom

I use vim, but you may choose any other text editor (or file access method), as long as you can paste in it and that it gets the file permissions right (which should be rw-r--r-- and owned by root).

Then, paste the following content in there:

Name: Plesk authentication hooks
Default: yes
Priority: 512
Password-Type: Primary
Password-Initial:
	optional	pam_plesk.so
Password:
	optional	pam_plesk.so try_first_pass
Auth-Type: Primary
Auth-Initial:
	[success=end default=ignore]	pam_plesk.so
Auth:
	[success=end default=ignore]    pam_plesk.so try_first_pass

After that, save the file and exit.

Now, we can regenerate out configuration files again:

tim@server:~$ sudo pam-auth-update

pam-auth-update dialogue with Plesk option

Our new option “Plesk authentication hooks” is enabled by default, so we just press Enter to confirm the dialogue and exit.

Now, if we check the configuration file difference again:

--- /etc/pam.d/common-auth	2020-10-09 21:55:47.175777554 +0200
+++ /etc/pam.d/common-auth	2020-10-10 15:56:46.731819429 +0200
@@ -14,8 +14,8 @@
 # pam-auth-update(8) for details.
 
 # here are the per-package modules (the "Primary" block)
-auth	[success=2 default=ignore]	pam_unix.so nullok_secure
-auth	[success=1 default=ignore]	pam_plesk.so try_first_pass
+auth	[success=2 default=ignore]	pam_plesk.so 
+auth	[success=1 default=ignore]	pam_unix.so nullok_secure try_first_pass
 # here's the fallback if no module succeeds
 auth	requisite			pam_deny.so
 # prime the stack with a positive return value if there isn't one already;
--- /etc/pam.d/common-password	2020-10-09 21:55:47.175777554 +0200
+++ /etc/pam.d/common-password	2020-10-10 15:56:46.738819457 +0200
@@ -22,8 +22,8 @@
 # pam-auth-update(8) for details.
 
 # here are the per-package modules (the "Primary" block)
-password	optional			pam_plesk.so try_first_pass
-password	[success=1 default=ignore]	pam_unix.so obscure sha512 try_first_pass
+password	optional			pam_plesk.so 
+password	[success=1 default=ignore]	pam_unix.so obscure use_authtok try_first_pass sha512
 # here's the fallback if no module succeeds
 password	requisite			pam_deny.so
 # prime the stack with a positive return value if there isn't one already;
--- /etc/pam.d/common-session	2020-10-09 21:45:47.678159983 +0200
+++ /etc/pam.d/common-session	2020-10-10 15:56:46.742819473 +0200
@@ -27,4 +27,5 @@
 session optional			pam_umask.so
 # and here are more per-package modules (the "Additional" block)
 session	required	pam_unix.so 
+session	optional	pam_systemd.so 
 # end of pam-auth-update config

That looks better! The only leftover change (apart from the added pam_systemd.so) is a reversed order of the pam_plesk.so and pam_unix.so in the auth configuration (which shouldn’t be much of an issue, since both still get queried), and a few changed arguments due to the changed load order.

Now that we fixed up the configuration, we logout and log back in again to see whether anything changed:

tim@server:~$ systemctl --user status
Failed to connect to bus: No such file or directory

Huh. Still the same issue.

However, if we look at env again, we can see that we made at least a little bit of progress:

tim@server:~$ env
[...]
XDG_SESSION_ID=7991950
[...]
XDG_RUNTIME_DIR=/run/user/1000
[...]

XDG_SESSION_ID and XDG_RUNTIME_DIR are now set properly, but the user session doesn’t quite work yet.

The pam_systemd manpage said that after setting XDG_SESSION_ID, the module tries to start an user-specific systemd service, user@<userid>.service, so lets check that for any hints:

tim@server:~$ sudo systemctl status [email protected][email protected] - User Manager for UID 1000
   Loaded: loaded (/lib/systemd/system/[email protected]; static; vendor preset: enabled)
  Drop-In: /lib/systemd/system/[email protected]
           └─timeout.conf
   Active: failed (Result: protocol) since Sat 2020-10-10 16:32:46 CEST; 8s ago
  Process: 799 ExecStart=/lib/systemd/systemd --user (code=exited, status=1/FAILURE)
 Main PID: 799 (code=exited, status=1/FAILURE)

Okt 10 16:32:46 server systemd[1]: Starting User Manager for UID 1000...
Okt 10 16:32:46 server systemd[799]: pam_unix(systemd-user:session): session opened for user tim by (uid=0)
Okt 10 16:32:46 server systemd[799]: Failed to create /user.slice/user-1000.slice/[email protected]/init.scope control group: Permission denied
Okt 10 16:32:46 server systemd[799]: Failed to allocate manager object: Permission denied
Okt 10 16:32:46 server systemd[1]: [email protected]: Failed with result 'protocol'.
Okt 10 16:32:46 server systemd[1]: Failed to start User Manager for UID 1000.

So apparently, systemd tries to start the systemd user daemon, but fails to initialize cgroup.

A quick search on the Unix StackExchange later, it appears that we have to install the libpam-cgfs module to make the system set up the user-specific cgroups when we log in.

tim@server:~$ sudo apt install libpam-cgfs
Reading package lists... Done
Building dependency tree       
Reading state information... Done
The following NEW packages will be installed:
  libpam-cgfs
0 upgraded, 1 newly installed, 0 to remove and 0 not upgraded.
Need to get 29.8 kB of archives.
After this operation, 131 kB of additional disk space will be used.
Get:1 ftp://ftp.stratoserver.net/pub/linux/ubuntu bionic-updates/universe amd64 libpam-cgfs amd64 3.0.3-0ubuntu1~18.04.1 [29.8 kB]
Fetched 29.8 kB in 0s (1052 kB/s)
Selecting previously unselected package libpam-cgfs.
(Reading database ... 90984 files and directories currently installed.)
Preparing to unpack .../libpam-cgfs_3.0.3-0ubuntu1~18.04.1_amd64.deb ...
Unpacking libpam-cgfs (3.0.3-0ubuntu1~18.04.1) ...
Setting up libpam-cgfs (3.0.3-0ubuntu1~18.04.1) ...

Since we already fixed the PAM configuration earlier in the process, the module should have been activated automatically. However, to make sure we start the configuration ourselves as well:

tim@server:~$ sudo pam-auth-update

pam-auth-update dialogue with cgroups option

Again, “Create cgroups for user login sessions” is already checked, so we just confirm with Enter.

One logout, login, and check later, we still don’t have working systemd user sessions (same message as always), but at least we get a new error to work on:

tim@server:~$ sudo systemctl status [email protected][email protected] - User Manager for UID 1000
   Loaded: loaded (/lib/systemd/system/[email protected]; static; vendor preset: enabled)
  Drop-In: /lib/systemd/system/[email protected]
           └─timeout.conf
   Active: failed (Result: protocol) since Sat 2020-10-10 16:52:57 CEST; 7s ago
  Process: 802 ExecStart=/lib/systemd/systemd --user (code=exited, status=1/FAILURE)
 Main PID: 802 (code=exited, status=1/FAILURE)

Okt 10 16:52:57 server systemd[802]: pam_unix(systemd-user:session): session opened for user tim by (uid=0)
Okt 10 16:52:57 server PAM-CGFS[802]: cgroupfs v1: Failed to initialize cpuset
Okt 10 16:52:57 server systemd[1]: [email protected]: Failed with result 'protocol'.
Okt 10 16:52:57 server systemd[1]: Failed to start User Manager for UID 1000.

At this point I decided to do a little bit of research: It seems like Strato doesn’t properly virtualize their “vServer” lineup (at least the ones I have worked with), but instead relies on containers. This is evidenced by the missing /boot files, missing fstab entries, OpenVZ/Virtuozzo-related files and quotas in the root of the drive, and misaligned init process IDs when digging a bit deeper.

So much for “installing software just the way you want”.