In the last few days, I've needed to set up several long-running services and I just wanted to take a minute to talk about how helpful systemd's user services have been.
The things I wanted to run are:
- A Node.js server which is started with
- A Node.js client (for testing)
- MoinMoin (a wiki written in Python)
- Ghost (blog software for Node.js)
- SyncThing (file sync like DropBox)
For the first one, I figured systemd would be useful, and I figured I might as well try running in user mode. The server isn't particularly stable, so systemd's automatic-restarting is helpful, and the sandboxing is also nice to have.
Once I had the config file, I realized that it's trivial to adapt it to any other long-running process. I also found another advantage: Since these run in user-mode, it's trivial to create a user for each service, do whatever I need to in their home directory, and then lock things down. I can log in as those users and run updates, and the only thing I need root access for is to give them permission to run services when not logged in ("lingering").
Creating single-use users is easy:
sudo adduser moin
To be able to ssh in as that user using the same keys as the current user:
sudo -u moin mkdir ~moin/.ssh sudo cp ~/.ssh/authorized_keys ~moin/.ssh/authorized_keys sudo chown moin:moin ~/.ssh/authorized_keys
If multiple people need access, you might want to generate the
authorized_keys file from their public keys rather than just copying your own.
Here's what my Ghost config looks like (in
[Unit] AssertPathExists=/home/ghost/ghost [Service] WorkingDirectory=/home/ghost/ghost Environment=GHOST_NODE_VERSION_CHECK=false ExecStart=/usr/bin/npm start --production Restart=always PrivateTmp=true NoNewPrivileges=true [Install] WantedBy=default.target
The nice bits are that if it crashes, it will restart, it has its own
/tmp, and even if it was hacked, it can't gain root priviledges (although, I'm not sure if these options work right in user mode).
With barely any changes, it works for MoinMoin too:
[Unit] AssertPathExists=/home/moin/moin [Service] WorkingDirectory=/home/moin/moin ExecStart=/usr/bin/python2 /home/moin/moin/wikiserver.py Restart=always PrivateTmp=true NoNewPrivileges=true [Install] WantedBy=default.target
For my custom Node.js server, I could even have systemd provide the socket and then lock things down even more.
As root, give the user permission to run services when they're not logged in:
sudo loginctl enable-linger moin
Now log in as the user who will run this server. You must use a real login, like connecting over SSH.
sudo -s -u won't give you the right environment.
ssh email@example.com systemctl --user enable moin
I just want to share how easy it was to do this, since separating services to each be run by a different user is good for security, and apparently it's super easy these days.