remote system operations - artmg/MuGammaPi GitHub Wiki

Remote system operations

To permit this server to remotely operate other systems, for example rebooting or shutting them down, you first of all need to be in the context of the service that will make the remote calls, e.g.

If you have any scripts that require privilege on remote device, such as reboot or shutdown, then you will need to set up the user and privileges on each device. The examples below use the username sysmain but you should choose your own for security

The solution for implementing this was inspired by https://www.youtube.com/watch?v=Ai7fqCWK41w

NB: There are simpler ways to set this up, but SSH weaknesses like Pivoting are real threats to security. For more on SSH Hardening skim through the somewhat detailed https://csrc.nist.gov/publications/detail/nistir/7966/final

We use the Forced Command with Authorised Keys, so if a key is stolen it can only be used to execute that command. It simply means we need one key for reboot and another for shutdown.

Although remote operations using systemctl are now possible via dbus, we are sticking to this ssh solution as it means that the command can be authorised and executed in one simple command executed from any context.

User context

The instructions below assume that you are in the user context that will be used to initiate the remote calls. In other words you are in the home folder of the

All file locations are relative to that using .

LOCAL_SERVICE_USER=myserviceuser

# either become the user
sudo -u $LOCAL_SERVICE_USER -H -s

# or move into their folder
pushd /home/$LOCAL_SERVICE_USER
# If it cannot be assumed then specify the
DOMAIN_SUFFIX=
# Location that will be available from service context
KEY_ROOT=./.ssh
# Server hostname
KEY_ID=remote
# any CURRENT username you use to log on manually
EXISTING_USER=username

# You'll need to paste this into the ssh session in a minute, too
SYSMAIN_USER=sysmain
Remote_Server=${EXISTING_USER}@${KEY_ID}${DOMAIN_SUFFIX}
Key_Opts="${KEY_OPTIONS:-"-t ed25519"}"
# this will overwrite any existing key
echo "y" | ssh-keygen $Key_Opts -f ${KEY_ROOT}/${KEY_ID}_sysmain_reboot_key -N "" -C "sysmain $HOSTNAME to $KEY_ID reboot `date +%y%m%d`"
echo "y" | ssh-keygen $Key_Opts -f ${KEY_ROOT}/${KEY_ID}_sysmain_poweroff_key -N "" -C "sysmain $HOSTNAME to $KEY_ID power `date +%y%m%d`"

# set up a socket to hold open the connection for multiple commands
SSH_SOCKET=${KEY_ROOT}/$Remote_Server
echo "We are about to open an SSH socket in the user context 
This means no certs are yet in place, so you may need to:
  a) say 'yes' to add to known hosts
  b) enter the password for $Remote_Server"
# credit https://stackoverflow.com/a/51792596
ssh -M -f -N -o ControlPath=$SSH_SOCKET -o  StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null $Remote_Server

# you might need PasswordAuthentication yes 
# temporarily back in `/etc/ssh/sshd_config` then
# sudo systemctl reload ssh.service

# don't use ssh-copy-id as that copies to the current user 
# who is logging on, not the new user we are about to create
# for now just push the public key to tmp

scp -o ControlPath=$SSH_SOCKET ${KEY_ROOT}/${KEY_ID}_sysmain_reboot_key.pub $Remote_Server:/tmp/
scp -o ControlPath=$SSH_SOCKET ${KEY_ROOT}/${KEY_ID}_sysmain_poweroff_key.pub $Remote_Server:/tmp/
ssh -o ControlPath=$SSH_SOCKET $Remote_Server
###########
# Now you are on the remote server, this will be the new username
# Remember to paste in the **same** user you used **ABOVE**

SYSMAIN_USER=sysmain
# create a non-system user without any group memberships
sudo useradd -m $SYSMAIN_USER

#### ALLOW LOGIN AND SPECIFY COMMAND
# In a fully hardened environment we would specify a source server too
sudo mkdir -p /home/$SYSMAIN_USER/.ssh
echo command=\"/bin/systemctl reboot -i\" "$(cat /tmp/*_sysmain_reboot_key.pub)" | sudo tee -a /home/$SYSMAIN_USER/.ssh/authorized_keys
echo command=\"/bin/systemctl poweroff -i\" "$(cat /tmp/*_sysmain_poweroff_key.pub)" | sudo tee -a /home/$SYSMAIN_USER/.ssh/authorized_keys
# systemctl with --ignore-inhibitors (-i) to force system down even if users logged on

# ensure that permissions are correct on installed public keys
# see https://unix.stackexchange.com/a/365958
# credit https://unix.stackexchange.com/a/210232
sudo chown -R $SYSMAIN_USER:$SYSMAIN_USER /home/$SYSMAIN_USER/.ssh
sudo chmod 700 /home/$SYSMAIN_USER/.ssh
sudo chmod 600 /home/$SYSMAIN_USER/.ssh/authorized_keys
sudo ls -la /home/$SYSMAIN_USER/.ssh/

#### ALLOW calling the command
# granular permissions to not require sudo
# delegate only certain commands to certain users 
# credit https://unix.stackexchange.com/a/465236
# /bin/systemctl is the command that actually performs the reboots on debian 

sudo tee /etc/sudoers.d/101_HA_sysmain_down <<EOF!
# allow Home Assistant to ssh as this user and perform maintenance without sudo
$SYSMAIN_USER ALL=NOPASSWD: /bin/systemctl
EOF!

#### ALLOW THE ACTIONS IN POLKIT
# help - 
https://github.com/artmg/lubuild/blob/master/help/understand/layers-on-your-desktop.md
# credit https://askubuntu.com/a/251942
sudo tee /etc/polkit-1/localauthority/50-local.d/allow-$SYSMAIN_USER-to-shutdown.pkla<<EOF!
[Allow this user to manage services]
Identity=unix-user:$SYSMAIN_USER
Action=org.freedesktop.systemd1.manage-units
ResultAny=yes

[Allow this user to set wall message]
Identity=unix-user:$SYSMAIN_USER
Action=org.freedesktop.login1.set-wall-message
ResultAny=yes

[Allow this user to poweroff]
Identity=unix-user:$SYSMAIN_USER
Action=org.freedesktop.login1.power-off
Action=org.freedesktop.login1.power-off-multiple-sessions
ResultAny=yes

[Allow this user to reboot]
Identity=unix-user:$SYSMAIN_USER
Action=org.freedesktop.login1.reboot
Action=org.freedesktop.login1.reboot-multiple-sessions
ResultAny=yes
EOF!

# does PolKit need restarting?
#sudo invoke-rc.d dbus restart

### Cleanup

# Exit remote shell
exit
# Need to paste the next bits separately #########

# exit the SSH 'Master' socket to close that
ssh -S $SSH_SOCKET -O exit $Remote_Server

## Example usage

echo ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -i `realpath $KEY_ROOT`/${KEY_ID}_sysmain_reboot_key ${SYSMAIN_USER}@${KEY_ID}${DOMAIN_SUFFIX}


# EITHER Exit the HA user context
exit

# OR the folder
popd

Footnotes

Note that for a combination of simplicity and security this method uses a key per command, due to the way that SSH Auth Keys Forced Command replaces any proposed commands with the single one specified by the key.

You could get around this limitations by using a wrapper like 'the only script' at http://at.magma-soft.at/sw/blog/posts/The_Only_Way_For_SSH_Forced_Commands/

If you were permitting a range of options for execution in this way, you might also consider allowing the systemctl command in your sudoers allowed file, as this offers not only shutdown / reboot but other job control too.