How to restrict SSH users to a predefined set of commands after login?

后端 未结 9 1373
灰色年华
灰色年华 2020-11-29 15:28

This is a idea for a security. Our employees shall have access to some commands on a linux server but not all. They shall e.g. have the possibility to access a log file (

相关标签:
9条回答
  • 2020-11-29 16:12

    GNU Rush may be the most flexible and secure way to accomplish this:

    GNU Rush is a Restricted User Shell, designed for sites that provide limited remote access to their resources, such as svn or git repositories, scp, or the like. Using a sophisticated configuration file, GNU Rush gives you complete control over the command lines that users execute, as well as over the usage of system resources, such as virtual memory, CPU time, etc.

    0 讨论(0)
  • 2020-11-29 16:17

    Why don't you write your own login-shell? It would be quite simple to use Bash for this, but you can use any language.

    Example in Bash

    Use your favorite editor to create the file /root/rbash.sh (this can be any name or path, but should be chown root:root and chmod 700):

    #!/bin/bash
    
    commands=("man" "pwd" "ls" "whoami")
    timestamp(){ date +'%Y-%m-%s %H:%M:%S'; }
    log(){ echo -e "$(timestamp)\t$1\t$(whoami)\t$2" > /var/log/rbash.log; }
    trycmd()
    {
        # Provide an option to exit the shell
        if [[ "$ln" == "exit" ]] || [[ "$ln" == "q" ]]
        then
            exit
            
        # You can do exact string matching for some alias:
        elif [[ "$ln" == "help" ]]
        then
            echo "Type exit or q to quit."
            echo "Commands you can use:"
            echo "  help"
            echo "  echo"
            echo "${commands[@]}" | tr ' ' '\n' | awk '{print "  " $0}'
        
        # You can use custom regular expression matching:
        elif [[ "$ln" =~ ^echo\ .*$ ]]
        then
            ln="${ln:5}"
            echo "$ln" # Beware, these double quotes are important to prevent malicious injection
            
            # For example, optionally you can log this command
            log COMMAND "echo $ln"
        
        # Or you could even check an array of commands:
        else
            ok=false
            for cmd in "${commands[@]}"
            do
                if [[ "$cmd" == "$ln" ]]
                then
                    ok=true
                fi
            done
            if $ok
            then
                $ln
            else
                log DENIED "$cmd"
            fi
        fi
    }
    
    # Optionally show a friendly welcome-message with instructions since it is a custom shell
    echo "$(timestamp) Welcome, $(whoami). Type 'help' for information."
    
    # Optionally log the login
    log LOGIN "$@"
    
    # Optionally log the logout
    trap "trap=\"\";log LOGOUT;exit" EXIT
    
    # Optionally check for '-c custom_command' arguments passed directly to shell
    # Then you can also use ssh user@host custom_command, which will execute /root/rbash.sh
    if [[ "$1" == "-c" ]]
    then
        shift
        trycmd "$@"
    else
        while echo -n "> " && read ln
        do
            trycmd "$ln"
        done
    fi
    

    All you have to do is set this executable as your login shell. For example, edit your /etc/passwd file, and replace your current login shell of that user /bin/bash with /root/rbash.sh.

    This is just a simple example, but you can make it as advanced as you want, the idea is there. Be careful to not lock yourself out by changing login shell of your own and only user. And always test weird symbols and commands to see if it is actually secure.

    You can test it with: su -s /root/rbash.sh.

    Beware, make sure to match the whole command, and be careful with wildcards! Better exclude Bash-symbols such as ;, &, &&, ||, $, and backticks to be sure.

    Depending on the freedom you give the user, it won't get much safer than this. I've found that often I only needed to make a user that has access to only a few relevant commands, and in that case this is really the better solution. However, do you wish to give more freedom, a jail and permissions might be more appropriate. Mistakes are easily made, and only noticed when it's already too late.

    0 讨论(0)
  • 2020-11-29 16:19

    ssh follows the rsh tradition by using the user's shell program from the password file to execute commands.

    This means that we can solve this without involving ssh configuration in any way.

    If you don't want the user to be able to have shell access, then simply replace that user's shell with a script. If you look in /etc/passwd you will see that there is a field which assigns a shell command interpreter to each user. The script is used as the shell both for their interactive login ssh user@host as well as for commands ssh user@host command arg ....

    Here is an example. I created a user foo whose shell is a script. The script prints the message my arguments are: followed by its arguments (each on a separate line and in angle brackets) and terminates. In the log in case, there are no arguments. Here is what happens:

    webserver:~# ssh foo@localhost
    foo@localhost's password:
    Linux webserver [ snip ]
    [ snip ]
    my arguments are:
    Connection to localhost closed.
    

    If the user tries to run a command, it looks like this:

    webserver:~# ssh foo@localhost cat /etc/passwd
    foo@localhost's password:
    my arguments are:
    <-c>
    <cat /etc/passwd>
    

    Our "shell" receives a -c style invocation, with the entire command as one argument, just the same way that /bin/sh would receive it.

    So as you can see, what we can do now is develop the script further so that it recognizes the case when it has been invoked with a -c argument, and then parses the string (say by pattern matching). Those strings which are allowed can be passed to the real shell by recursively invoking /bin/bash -c <string>. The reject case can print an error message and terminate (including the case when -c is missing).

    You have to be careful how you write this. I recommend writing only positive matches which allow only very specific things, and disallow everything else.

    Note: if you are root, you can still log into this account by overriding the shell in the su command, like this su -s /bin/bash foo. (Substitute shell of choice.) Non-root cannot do this.

    Here is an example script: restrict the user into only using ssh for git access to repositories under /git.

    #!/bin/sh
    
    if [ $# -ne 2 ] || [ "$1" != "-c" ] ; then
      printf "interactive login not permitted\n"
      exit 1
    fi
    
    set -- $2
    
    if [ $# != 2 ] ; then
      printf "wrong number of arguments\n"
      exit 1
    fi
    
    case "$1" in
      ( git-upload-pack | git-receive-pack )
        ;; # continue execution
      ( * )
        printf "command not allowed\n"
        exit 1
        ;;
    esac
    
    # Canonicalize the path name: we don't want escape out of
    # git via ../ path components.
    
    gitpath=$(readlink -f "$2")  # GNU Coreutils specific
    
    case "$gitpath" in
      ( /git/* )
         ;; # continue execution
      ( * )
        printf "access denied outside of /git\n"
        exit 1
        ;;
    esac
    
    if ! [ -e "$gitpath" ] ; then
       printf "that git repo doesn't exist\n"
       exit 1
    fi
    
    "$1" "$gitpath"
    

    Of course, we are trusting that these Git programs git-upload-pack and git-receive-pack don't have holes or escape hatches that will give users access to the system.

    That is inherent in this kind of restriction scheme. The user is authenticated to execute code in a certain security domain, and we are kludging in a restriction to limit that domain to a subdomain. For instance if you allow a user to run the vim command on a specific file to edit it, the user can just get a shell with :!sh[Enter].

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