Restricted shell for managing files and git repositories

Think about a web hosting company that wants to let users manage files and git repositories via ssh. This includes:

  • secure copy (scp)
  • creating, copying, moving/renaming and deleting files
  • executing a narrow subset of commands for source control and text editing (git, vim, nano)

We would like to implement that and looked into the following options:

  • rssh
  • scponly

These would allow for the scp part, but using git doesn't seem to be possible. There is a patch in Launchpad, but I'm not sure what to make of it. There is git-shell, too, but it doesn't seem to allow editors. Maybe vim is even too much, because it could be used to execute more code, so we could drop that (vim, or text editors completely, if must be) if it is too much.

We basically want to lock down the shell, so the user can manage (and edit) files and git repositories, but the user should not be able to execute any other programs on the system. The biggest problem would be abuse of network and computing resources, but using the system as a proxy too. You name it. Is there a way to do this or do we maybe even have the wrong approach on this problem?


You have two complementary ways to implement this:

Granting users permissions to use git repositories remotely

Use gitolite3 to provide a hub-live repositories schema (this is described in detail here), that basically requires you to have a bare repository (a hub repo) to let your users to push/pull from and a checked-out version of the same repo (a live repo) located in the appropriate path, say /srv/www/html, for example.

I like to use gitolite3 to handle the hub repo, but that is not a requirement, though it's rather convenient to have fine-grained access control bound to your LDAP of choice if needs be. gitolite3 can provide fine-grained control down to the branch level.

It is also a good practice to limit the capabilities of the gitolite3 user via sudo, and handle the hooks by means of sudo calls. This is a working example using gitolite3 hooks (feel free to adapt them -or enhance/fix them- to suit your needs):

  • The relevant content of the /etc/sudoers or /etc/sudoers.d/gitolite3 would be something along the lines of:

    Cmnd_Alias        GITOLITE_CMDS = /usr/bin/git, /bin/chown, /bin/find, /usr/bin/xargs, /bin/chmod, /sbin/restorecon, /usr/local/sbin/publisher-hub2live
    Cmnd_Alias GITOLITE_APACHE_CMDS = /usr/sbin/apachectl graceful
    Defaults:gitolite3 !requiretty
    Defaults:gitolite3 lecture=never
    gitolite3                ALL = (root)NOPASSWD: GITOLITE_CMDS
    gitolite3       APACHE_HOSTS = (root)NOPASSWD: GITOLITE_APACHE_CMDS
    
  • hub repo post-update hook:

    #!/bin/sh
    
    echo "****"
    echo "**** Calling publisher-hub2live script [Hub's post-update hook]"
    echo "****"
    
    sudo /usr/local/sbin/publisher-hub2live "/srv/www/html" "root:apache" "2750" "640"
    
    exit 0
    
  • publisher-hub2live script:

    #!/bin/sh
    
    echo "****"
    echo "**** Pulling changes into Live [publisher-hub2live]"
    echo "****"
    
    cd "$1" || exit
    umask 0022
    unset GIT_DIR
    /usr/bin/git pull hub master
    
    # custom actions here
    # e.g call grunt tasks
    /bin/chown -R "$2" "$1"
    /bin/find "$1" -type d -exec chmod "$3" {} +
    /bin/find "$1" -type f -exec chmod "$4" {} +
    /bin/chmod u+x "$1"/.git/hooks/post-commit
    /sbin/restorecon -R -v "$1"
    exec /usr/bin/git update-server-info
    
    exit 0
    

Limiting the ability to execute non-authorized commands in a login shell

What you'd need to implement is a reproducible, auditable method of limiting the ability of the user to perform actions other than the strictly allowed.

It is not required but it helps if you have your users registered in LDAP, and you have already deployed the mechanisms to perform LDAP authentication, be it by means of a PAM module, or using freeIPA and sssd.

To implement this scenario, what I currently do is the following (be aware that this kind of restrictions requires that several conditions are met, otherwise the restriction can be easily circumvented):

  • The users do not belong to the wheel group, the only one authorized to use su (enforced via PAM). Usually, a non-LDAP user (sysadm) exists to allow trusted administrators performing actions in cases of disaster recovery or LDAP unavailability.
  • The users are given a properly secured rbash with a read-only PATH pointing to a private ~/bin, this ~/bin/ directory contains links to all allowed commands, for example:

    $ ll ~/bin
    total 0
    lrwxrwxrwx. 1 root dawud 14 Sep 17 08:58 clear -> /usr/bin/clear*
    lrwxrwxrwx. 1 root dawud  7 Sep 17 08:58 df -> /bin/df*
    lrwxrwxrwx. 1 root dawud 10 Sep 17 08:58 egrep -> /bin/egrep*
    lrwxrwxrwx. 1 root dawud  8 Sep 17 08:58 env -> /bin/env*
    lrwxrwxrwx. 1 root dawud 10 Sep 17 08:58 fgrep -> /bin/fgrep*
    lrwxrwxrwx. 1 root dawud 14 Sep 17 08:58 git -> /usr/bin/git*
    lrwxrwxrwx. 1 root dawud  9 Sep 17 08:58 grep -> /bin/grep*
    lrwxrwxrwx. 1 root dawud 10 Sep 17 08:58 rview -> /bin/rview*
    lrwxrwxrwx. 1 root dawud 13 Sep 17 08:58 sudo -> /usr/bin/sudo*
    lrwxrwxrwx. 1 root dawud 17 Sep 17 08:58 sudoedit -> /usr/bin/sudoedit*
    lrwxrwxrwx. 1 root dawud 13 Sep 17 08:58 tail -> /usr/bin/tail*
    lrwxrwxrwx. 1 root dawud 11 Sep 17 08:58 wc -> /usr/bin/wc*
    
  • the users are given a restricted, read-only environment (think of stuff like LESSSECURE, TMOUT, HISTFILE variables). This is to avoid shell escapes from commands like less and ensure auditability.

  • the only allowed editor isrvim, for the same reason. Users can only execute sudoedit, whic in configured to run rvim in the sudo configuration:

    Defaults editor=/usr/bin/rvim
    
  • if MAC restrictions are in place (the specific GNU/Linux distribution you are using has SELinux enabled), the users are mapped to the SELinux user staff_u and given rights to execute commands as other user as required via sudo. The specific sudorules allowed need to be carefully reviewed to prevent the user from circumventing these limitations, and can also be deployed in your existing LDAP infrastructure (this is one of freeIPA features).

  • the users' /home, /tmp and possibly /var/tmp are polyinstantiated via /etc/security/namespace.conf:

    /tmp       /tmp/.inst/tmp.inst-$USER-     tmpdir:create   root
    /var/tmp   /tmp/.inst/var-tmp.inst-$USER- tmpdir:create   root
    $HOME      $HOME/$USER.inst/              tmpdir:create   root
    

    Polyinstantiation of directories is not a new feature, it has been available for quite a long time now. As a reference, see this article from 2006. As a matter of fact, a lot of modules already use pam_namespace by default, but the default configuration at /etc/security/namespace.conf does not enable polyinstantiation. Also, /etc/security/namespace.init should make all skeletal files read-only for the users and owned by root.

This way you can choose whether the users can execute any command on their own behalf (via a link in the private ~/bin directory, provisioned via /etc/skel, as explained above), on behalf of other user (via sudo) or none at all.