SSH – Force Command execution on login even without Shell

后端 未结 4 1651
粉色の甜心
粉色の甜心 2021-01-31 20:39

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@ho

相关标签:
4条回答
  • 2021-01-31 21:23

    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
    
    0 讨论(0)
  • 2021-01-31 21:33

    The ForceCommand option runs without a PTY unless the client requests one. As a result, you don't actually have a shell to execute scripts the way you might expect. In addition, the OpenSSH SSHD_CONFIG(5) man page clearly says:

    The command is invoked by using the user's login shell with the -c option.

    That means that if you've disabled the user's login shell, or set it to something like /bin/false, then ForceCommand can't work. Assuming that:

    1. the user has a sensible shell defined,
    2. that your target script is executable, and
    3. that your script has an appropriate shebang line

    then the following should work in your global sshd_config file once properly modified with the proper username and fully-qualified pathname to your custom script:

    Match User foo
        ForceCommand /path/to/script.sh
    
    0 讨论(0)
  • 2021-01-31 21:36

    I am the author of the OP. Also, you can implement a simple logwatcher as the following written in python3, which keeps reading for a file and executes a command when line contains pattern.

    logwatcher.python3

    #!/usr/bin/env python3
    
    # follow.py
    #
    # Follow a file like tail -f.
    
    import sys
    import os
    import time
    
    def follow(thefile):
        thefile.seek(0,2)
        while True:
            line = thefile.readline()
            if not line:
                time.sleep(0.5)
                continue
            yield line
    
    if __name__ == '__main__':
        logfilename = sys.argv[1]
        pattern_string = sys.argv[2]
        command_to_execute = sys.argv[3]
    
        print("Log filename is: {}".format(logfilename))
    
        logfile = open(logfilename, "r")
        loglines = follow(logfile)
    
        for line in loglines:
            if pattern_string in line:
                os.system(command_to_execute)
    

    Usage

    1. Make the above script executable:

    chmod +x logwatcher.python3

    1. Add a cronjob to start it after reboot

    crontab -e

    Then write this line there and save it after this:

    @reboot /home/YOURUSERNAME/logwatcher.python3 "/var/log/auth.log" "session opened for user" "/sbin/myscript.sh"

    The first argument of this script is the log file to watch, and the second argument is the string for which to look in it. The third argument is the script to execute when the line is found in file.

    It is best if you use something more reliable to start/restart the script in case it crashes.

    0 讨论(0)
  • 2021-01-31 21:37

    I am the author of the OP; I came to the conclusion that what I need to achieve is not possible using SSH only to the date (OpenSSH_6.9p1 Ubuntu-2, OpenSSL 1.0.2d 9 Jul 2015), but I found a great piece of software that uses encrypted SPAuthentication to open SSH port and it's new version (to the date of this post, it's GitHub master branch) has a feature to execute a command always that a user authorizates successfully.

    FWKNOP - Encrypted Single Packet Authorization

    FWKNOP set iptables rules that allow access to given ports upon a single packet encrypted which is sent via UDP. Then after authorization it allow access for the authorized user for a given time, for example 30 seconds, closing the port after this, leaving the connection open.

    1. To install on an Ubuntu linux:

    The current version (2.6.0-2.1build1) on Ubuntu repositories to the date still doesn't allow command execution on successful SPA; (please use 2.6.8 from GitHub instead)

    On client machine:

    sudo apt-get install fwknop-client

    On server side:

    sudo apt-get install fwknop-server

    Here is a tutorial on how to setup the client and server machines https://help.ubuntu.com/community/SinglePacketAuthorization

    Then, after it is set up, on server side:

    1. Edit /etc/default/fwknop-server
    2. Change the line START_DAEMON="no" to START_DAEMON="yes"
    3. Then run:

      sudo service fwknop-server stop

      sudo service fwknop-server start

    2. Warning admin on successful SPA (email, pushover script etc)

    So, as stated above the current version present in Ubuntu repositories (2.6.0-2.1build1) cannot execute command on successful SPA. If you need this feature as of the OP, but it will be released at fwknop version (2.6.8), as can it is stated here:

    https://github.com/mrash/fwknop/issues/172

    So if you need to use it right now you can build from github branch master which have the CMD_CYCLE_OPEN option.

    3. More resources on fwknop

    https://help.ubuntu.com/community/SinglePacketAuthorization

    https://github.com/mrash/fwknop/ (project on GitHub)

    http://www.cipherdyne.org/fwknop/ (project site)

    https://www.digitalocean.com/community/tutorials/how-to-use-fwknop-to-enable-single-packet-authentication-on-ubuntu-12-04 (tutorial on DO's community)

    0 讨论(0)
提交回复
热议问题