Search code examples
sshsshd

SSH – Force Command execution on login even without Shell


I am creating a restricted user without shell for port forwarding only and I need to execute a script on login via pubkey, even if the user is connected via ssh -N user@host which doesn't asks SSH server for a shell.

The script should warn admin on connections authenticated with pubkey, so the user connecting shouldn't be able to skip the execution of the script (e.g., by connecting with ssh -N).

I have tried to no avail:

  • Setting the command at /etc/ssh/sshrc.
  • Using command="COMMAND" in .ssh/authorized_keys (man authorized_keys)
  • Setting up a script with the command as user's shell. (chsh -s /sbin/myscript.sh USERNAME)
  • Matching user in /etc/ssh/sshd_config like: Match User MYUSERNAME ForceCommand "/sbin/myscript.sh"

All work when user asks for shell, but if logged only for port forwarding and no shell (ssh -N) it doesn't work.


Solution

  • If you only need to run a script you can rely on pam_exec.

    Basically you reference the script you need to run in the /etc/pam.d/sshd configuration:

    session optional pam_exec.so seteuid /path/to/script.sh
    

    After some testing you may want to change optional to required.

    Please refer to this answer "bash - How do I set up an email alert when a ssh login is successful? - Ask Ubuntu" for a similar request.

    Indeed in the script only a limited subset on the environment variables is available:

    LANGUAGE=en_US.UTF-8
    PAM_USER=bitnami
    PAM_RHOST=192.168.1.17
    PAM_TYPE=open_session
    PAM_SERVICE=sshd
    PAM_TTY=ssh
    LANG=en_US.UTF-8
    LC_ALL=en_US.UTF-8
    PWD=/
    

    If you want to get the user info from authorized_keys this script could be helpful:

    #!/bin/bash
    # Get user from authorized_keys
    # pam_exec_login.sh
    # * [ssh - What is the SHA256 that comes on the sshd entry in auth.log? - Server Fault](https://serverfault.com/questions/888281/what-is-the-sha256-that-comes-on-the-sshd-entry-in-auth-log)
    # * [bash - How to get all fingerprints for .ssh/authorized_keys(2) file - Server Fault](https://serverfault.com/questions/413231/how-to-get-all-fingerprints-for-ssh-authorized-keys2-file)
    
    # Setup log
    b=$(basename $0| cut -d. -f1)
    log="/tmp/${b}.log"
    
    function timeStamp () {
      echo "$(date '+%b %d %H:%M:%S') ${HOSTNAME} $b[$$]:"
    }
    
    # Check if opening a remote session with sshd
    if [ "${PAM_TYPE}" != "open_session" ] || [ $PAM_SERVICE != "sshd" ] || [ $PAM_RHOST == "::1" ]; then
      exit $PAM_SUCCESS
    fi
    
    # Get info from auth.log
    authLogLine=$(journalctl -u ssh.service |tail -100 |grep "sshd\[${PPID}\]" |grep "${PAM_RHOST}")
    echo ${authLogLine} >> ${log}
    
    PAM_USER_PORT=$(echo ${authLogLine}| sed -r 's/.*port (.*) ssh2.*/\1/')
    PAM_USER_SHA256=$(echo ${authLogLine}| sed -r 's/.*SHA256:(.*)/\1/')
    
    # Get details from .ssh/authorized_keys
    authFile="/home/${PAM_USER}/.ssh/authorized_keys"
    PAM_USER_authorized_keys=""
    
    while read l; do
      if [[ -n "$l" && "${l###}" = "$l" ]]; then
        authFileSHA256=$(ssh-keygen -l -f <(echo "$l"))
        if [[ "${authFileSHA256}" == *"${PAM_USER_SHA256}"* ]]; then
          PAM_USER_authorized_keys=$(echo ${authFileSHA256}| cut -d" " -f3)
          break
        fi
      fi
    done < ${authFile}
    
    if [[ -n ${PAM_USER_authorized_keys} ]]
    then
      echo "$(timeStamp) Local user: ${PAM_USER}, authorized_keys user: ${PAM_USER_authorized_keys}" >> ${log}
    else
      echo "$(timeStamp) WARNING: no matching user in authorized_keys" >> ${log}
    fi