Manage systemd services remotely over SSH protocol.

Create user for systemd manager

Create systemd-manager user on remote machine.

$ sudo useradd --create-home systemd-manager

Prepare the directory and file that will contain public keys for public key authentication.

$ sudo mkdir /home/systemd-manager/.ssh
$ sudo touch /home/systemd-manager/.ssh/authorized_keys
$ sudo chown systemd-manager:systemd-manager /home/systemd-manager/.ssh /home/systemd-manager/.ssh/authorized_keys
$ sudo chmod 700 /home/systemd-manager/.ssh
$ sudo chmod 600 /home/systemd-manager/.ssh/authorized_keys

Populate public keys.

Remember to limit public keys to the systemd-stdio-bridge application that will be used to connect standard input to the D-Bus transport.

command="systemd-stdio-bridge",no-agent-forwarding,no-port-forwarding,no-pty,no-user-rc ssh-rsa AAAAC6Mye[...]RhUZQC systemd@manager

Configure authorization framework

Install policykit-1 application-level toolkit for managing administrative policies and privileges to populate configuration files.

$ sudo apt install policykit-1

Create authorization policy to allow systemd-manager user to execute systemctl commands without sudo.

$ cat << EOF | sudo tee /etc/polkit-1/localauthority/50-local.d/systemd-manager.pkla
[Allow user systemd-manager to execute systemctl commands]
Identity=unix-user:systemd-manager
Action=org.freedesktop.systemd1.manage-units
ResultAny=yes
EOF

Manage systemd services remotely

Display service status.

$ systemctl --host systemd-manager@server.example.org status nginx
● nginx.service - A high performance web server and a reverse proxy server
     Loaded: loaded (/lib/systemd/system/nginx.service; enabled; vendor preset: enabled)
    Drop-In: /etc/systemd/system.control/nginx.service.d
             └─50-MemoryLimit.conf
     Active: active (running) since Mon 2020-09-21 00:00:38 CEST; 25min ago
       Docs: man:nginx(8)
    Process: 1571 ExecStartPre=/usr/sbin/nginx -t -q -g daemon on; master_process on; (code=exited, status=0/SUCCESS)
    Process: 1572 ExecStart=/usr/sbin/nginx -g daemon on; master_process on; (code=exited, status=0/SUCCESS)
   Main PID: 1573
      Tasks: 3 (limit: 1149)
     Memory: 3.8M (limit: 1.0G)
     CGroup: /system.slice/nginx.service
             ├─1573 nginx: master process /usr/sbin/nginx -g daemon on; master_process on;
             ├─1574 nginx: worker process
             └─1575 nginx: worker process

Restart service.

$ systemctl --host systemd-manager@server.example.org restart nginx

Set property.

$ systemctl --host systemd-manager@server.example.org set-property nginx  MemoryLimit=2G

Check if service is enabled.

$ systemctl --host systemd-manager@server.example.org is-enabled nginx
enabled

List timers.

$ systemctl --host systemd-manager@server.example.org list-timers
NEXT                         LEFT               LAST                         PASSED      UNIT                         ACTIVATES
Thu 2020-09-03 23:50:16 CEST 2 weeks 3 days ago Sun 2020-09-20 23:23:30 CEST 1h 4min ago systemd-tmpfiles-clean.timer systemd-tmpfiles-clean.service
Mon 2020-09-21 00:47:04 CEST 18min left         Sun 2020-09-20 13:24:52 CEST 11h ago     apt-daily.timer              apt-daily.service
Mon 2020-09-21 02:00:00 CEST 1h 31min left      Sun 2020-09-20 13:24:52 CEST 11h ago     logrotate.timer              logrotate.service
Mon 2020-09-21 02:00:00 CEST 1h 31min left      Sun 2020-09-20 13:24:52 CEST 11h ago     man-db.timer                 man-db.service
Mon 2020-09-21 08:05:29 CEST 7h left            Sun 2020-09-20 13:24:52 CEST 11h ago     apt-daily-upgrade.timer      apt-daily-upgrade.service
5 timers listed.
Pass --all to see loaded but inactive timers, too.

List sockets.

$ systemctl --host systemd-manager@server.example.org list-sockets
LISTEN                          UNITS                           ACTIVATES
/run/initctl                    systemd-initctl.socket          systemd-initctl.service
/run/systemd/fsck.progress      systemd-fsckd.socket            systemd-fsckd.service
/run/systemd/journal/dev-log    systemd-journald-dev-log.socket systemd-journald.service
/run/systemd/journal/socket     systemd-journald.socket         systemd-journald.service
/run/systemd/journal/stdout     systemd-journald.socket         systemd-journald.service
/run/systemd/journal/syslog     syslog.socket                   rsyslog.service
/run/udev/control               systemd-udevd-control.socket    systemd-udevd.service
/var/run/dbus/system_bus_socket dbus.socket                     dbus.service
audit 1                         systemd-journald-audit.socket   systemd-journald.service
kobject-uevent 1                systemd-udevd-kernel.socket     systemd-udevd.service
10 sockets listed.
Pass --all to see loaded but inactive sockets, too.

Authorize additional operations

Sometimes you need to authorize additional operations, for example to reload the systemd manager configuration.

$ systemctl --host systemd-manager@server.example.org daemon-reload
Failed to reload daemon: Interactive authentication required.

You just need to add an additional action or use an asterisk (not recommended).

Action=org.freedesktop.systemd1.manage-units;org.freedesktop.systemd1.reload-daemon
Action=org.freedesktop.systemd1.*

Use pkaction utility to display every possible action.

$ pkaction
org.dpkg.pkexec.update-alternatives
org.freedesktop.hostname1.get-product-uuid
org.freedesktop.hostname1.set-hostname
org.freedesktop.hostname1.set-machine-info
org.freedesktop.hostname1.set-static-hostname
org.freedesktop.locale1.set-keyboard
org.freedesktop.locale1.set-locale
org.freedesktop.login1.attach-device
org.freedesktop.login1.flush-devices
org.freedesktop.login1.halt
org.freedesktop.login1.halt-ignore-inhibit
org.freedesktop.login1.halt-multiple-sessions
org.freedesktop.login1.hibernate
org.freedesktop.login1.hibernate-ignore-inhibit
org.freedesktop.login1.hibernate-multiple-sessions
org.freedesktop.login1.inhibit-block-idle
org.freedesktop.login1.inhibit-block-shutdown
org.freedesktop.login1.inhibit-block-sleep
org.freedesktop.login1.inhibit-delay-shutdown
org.freedesktop.login1.inhibit-delay-sleep
org.freedesktop.login1.inhibit-handle-hibernate-key
org.freedesktop.login1.inhibit-handle-lid-switch
org.freedesktop.login1.inhibit-handle-power-key
org.freedesktop.login1.inhibit-handle-suspend-key
org.freedesktop.login1.lock-sessions
org.freedesktop.login1.manage
org.freedesktop.login1.power-off
org.freedesktop.login1.power-off-ignore-inhibit
org.freedesktop.login1.power-off-multiple-sessions
org.freedesktop.login1.reboot
org.freedesktop.login1.reboot-ignore-inhibit
org.freedesktop.login1.reboot-multiple-sessions
org.freedesktop.login1.set-reboot-to-firmware-setup
org.freedesktop.login1.set-self-linger
org.freedesktop.login1.set-user-linger
org.freedesktop.login1.set-wall-message
org.freedesktop.login1.suspend
org.freedesktop.login1.suspend-ignore-inhibit
org.freedesktop.login1.suspend-multiple-sessions
org.freedesktop.policykit.exec
org.freedesktop.policykit.lockdown
org.freedesktop.resolve1.register-service
org.freedesktop.resolve1.unregister-service
org.freedesktop.systemd1.manage-unit-files
org.freedesktop.systemd1.manage-units
org.freedesktop.systemd1.reload-daemon
org.freedesktop.systemd1.reply-password
org.freedesktop.systemd1.set-environment
org.freedesktop.timedate1.set-local-rtc
org.freedesktop.timedate1.set-ntp
org.freedesktop.timedate1.set-time
org.freedesktop.timedate1.set-timezone

Inspect specific action from the above-mentioned list.

$ pkaction --verbose --action-id org.freedesktop.systemd1.manage-units
org.freedesktop.systemd1.manage-units:
  description:       Manage system services or other units
  message:           Authentication is required to manage system services or other units.
  vendor:            The systemd Project
  vendor_url:        http://www.freedesktop.org/wiki/Software/systemd
  icon:
  implicit any:      auth_admin
  implicit inactive: auth_admin
  implicit active:   auth_admin_keep

Alternatively, inspect systemd policy file to identify possible actions.

$ cat /usr/share/polkit-1/actions/org.freedesktop.systemd1.policy
<?xml version="1.0" encoding="UTF-8"?> <!--*-nxml-*-->
<!DOCTYPE policyconfig PUBLIC "-//freedesktop//DTD PolicyKit Policy Configuration 1.0//EN"
        "http://www.freedesktop.org/standards/PolicyKit/1/policyconfig.dtd">
<!--
  SPDX-License-Identifier: LGPL-2.1+
  This file is part of systemd.
  systemd is free software; you can redistribute it and/or modify it
  under the terms of the GNU Lesser General Public License as published by
  the Free Software Foundation; either version 2.1 of the License, or
  (at your option) any later version.
-->
<policyconfig>
        <vendor>The systemd Project</vendor>
        <vendor_url>http://www.freedesktop.org/wiki/Software/systemd</vendor_url>
        <action id="org.freedesktop.systemd1.reply-password">
                <description gettext-domain="systemd">Send passphrase back to system</description>
                <message gettext-domain="systemd">Authentication is required to send the entered passphrase back to the system.</message>
                <defaults>
                        <allow_any>no</allow_any>
                        <allow_inactive>no</allow_inactive>
                        <allow_active>auth_admin_keep</allow_active>
                </defaults>
                <annotate key="org.freedesktop.policykit.exec.path">/lib/systemd/systemd-reply-password</annotate>
        </action>
        <action id="org.freedesktop.systemd1.manage-units">
                <description gettext-domain="systemd">Manage system services or other units</description>
                <message gettext-domain="systemd">Authentication is required to manage system services or other units.</message>
                <defaults>
                        <allow_any>auth_admin</allow_any>
                        <allow_inactive>auth_admin</allow_inactive>
                        <allow_active>auth_admin_keep</allow_active>
                </defaults>
        </action>
        <action id="org.freedesktop.systemd1.manage-unit-files">
                <description gettext-domain="systemd">Manage system service or unit files</description>
                <message gettext-domain="systemd">Authentication is required to manage system service or unit files.</message>
                <defaults>
                        <allow_any>auth_admin</allow_any>
                        <allow_inactive>auth_admin</allow_inactive>
                        <allow_active>auth_admin_keep</allow_active>
                </defaults>
                <annotate key="org.freedesktop.policykit.imply">org.freedesktop.systemd1.reload-daemon org.freedesktop.systemd1.manage-units</annotate>
        </action>
        <action id="org.freedesktop.systemd1.set-environment">
                <description gettext-domain="systemd">Set or unset system and service manager environment variables</description>
                <message gettext-domain="systemd">Authentication is required to set or unset system and service manager environment variables.</message>
                <defaults>
                        <allow_any>auth_admin</allow_any>
                        <allow_inactive>auth_admin</allow_inactive>
                        <allow_active>auth_admin_keep</allow_active>
                </defaults>
        </action>
        <action id="org.freedesktop.systemd1.reload-daemon">
                <description gettext-domain="systemd">Reload the systemd state</description>
                <message gettext-domain="systemd">Authentication is required to reload the systemd state.</message>
                <defaults>
                        <allow_any>auth_admin</allow_any>
                        <allow_inactive>auth_admin</allow_inactive>
                        <allow_active>auth_admin_keep</allow_active>
                </defaults>
        </action>
</policyconfig>

Limitations

You cannot edit services.

$ systemctl --host systemd-manager@server.example.org edit nginx
Cannot edit units remotely.

You cannot inspect service logs which should be obvious.