Home

Awesome

ARCHIVAL NOTICE

This project is no longer maintained and is left up for historic purposes.

dockersh

A user shell for isolated, containerized environments.

What is this?

dockersh is designed to be used as a login shell on machines with multiple interactive users.

When a user invokes dockersh, it will bring up a Docker container (if not already running), and then spawn a new interactive shell in the container's namespace.

dockersh can be used as a shell in /etc/passwd or as an ssh ForceCommand.

This allows you to have a single ssh process on the normal ssh port which places user sessions into their own individual docker containers in a secure and locked down manner.

Why do I want this?

You want to allow multiple users to ssh onto a single box, but you'd like some isolation between those users. With dockersh each user enters their own individual docker container (acting like a lightweight virtual machine), with their home directory mounted from the host system (so that user data is persistent between container restarts), but with its own kernel namespaces for processes and networking.

This means that the user is isolated from the rest of the system, and they can only see their own processes, and have their own network stack. This gives better privacy between users, and can also be used for more easily separating each user's processes from the rest of the system with per user constraints.

Normally to give users individual containers you have to run an ssh daemon in each container, and either have have a different port for each user to ssh to or some nasty Forcecommand hacks (which only work with agent forwarding from the client).

Dockersh eliminates the need for any of these techniques by acting like a regular shell which can be used in /etc/passwd or as an ssh ForceCommand.
This allows you to have a single ssh process, on the normal ssh port, and gives a secure way to connect users into their own individual docker containers.

SECURITY WARNING

dockersh tries hard to drop all privileges as soon as possible, including disabling the suid, sgid, raw sockets and mknod capabilities of the target process (and all children), however this doesn't mean that it is safe enough to allow public access to dockersh containers!

WARNING: Whilst this project tries to make users inside containers have lowered privileges and drops capabilities to limit users ability to escalate their privilege level, it is not certain to be completely secure. Notably when Docker adds user namespace support, this can be used to further lock down privileges.

SECOND WARNING: The dockersh binary needs the suid bit set so that it can make the syscalls to adjust kernel namespaces, so any security issues in this code are likely to be exploitable to root.

Requirements

Linux >= 3.8

Docker >= 1.2.0

If you want to build it locally (rather than in a docker container), Go >= 1.2

Installation

With docker

(This is the recommended method).

Build the Dockerfile in the local directory into an image, and run it like this:

$ docker build .
# Progress, takes a while the first time..
....
Successfully built 3006a08eef2e 
$ docker run -v /usr/local/bin:/target 3006a08eef2e

Without docker

You need to install golang (tested on 1.2 and 1.3), then you should just be able to run:

go get
make

and a 'dockersh' binary will be generated in your $GOPATH (or your current working directory if $GOPATH isn't set). N.B. This binary needs to be moved to where you would like to install it (recommended /usr/local/bin), and owned by root + u+s (suid). This is done automatically if you use the Docker based installed, but you need to do it manually if you're compiling the binary yourself.

Invoking dockersh

There are two main methods of invoking dockersh. Either:

  1. Put the path to dockersh into /etc/shells, and then change the users shell in /etc/passwd (e.g. chsh myuser -s /usr/local/bin/dockersh)
  2. Set dockersh as the ssh ForceCommand in the users $HOME/.ssh/config, or globally in /etc/ssh/ssh_config

Note: The dockersh binary needs the suid bit set to operate!

Configuration

We use gcfg to read configs in an ini style format.

The global config file, /etc/dockershrc has a [dockersh] block in it, and zero or more [user "foo"] blocks.

This can be used to set settings globally or per user, and also to enable the setting of settings in the (optional) per user configuration file (~/.dockersh), if enabled.

Config file values

Setting nameTypeDescriptionDefault valueExample value
imagenameStringThe name of the container image to launch for the user. The %u sequence will interpolate the usernamebusyboxubuntu, or %u/mydockersh
containernameStringThe name of the container (per user) which is launched.%u_dockersh%u-dsh
mounthomeBoolIf the users home directory should be mounted in the target containerfalsetrue
mounttmpBoolIf /tmp should be mounted into the target container (so that ssh agent forwarding works). N.B. Security riskfalsetrue
mounthometoStringWhere to map the user's home directory inside the container.%h/opt/home/myhomedir
mounthomefromStringWhere to map the user's home directory from on the host.%h/opt/home/%u
usercwdStringWhere to chdir into the container when starting a shell.%h/
containerusernameStringUsername which should be used inside the container.%uroot
shellStringThe shell that should be started for the user inside the container./bin/ash/bin/bash
mountdockersocketBoolIf to mount the docker socket from the host. (DANGEROUS)falsetrue
dockersocketStringThe location of the docker socket from the host./var/run/docker.sock/opt/docker/var/run/docker.sock
entrypointStringThe entrypoint for the persistent process to keep the container runninginternal/sbin/yoursupervisor
cmdArray of StringsAdditional parameters to pass when launching the container as the command line-c'/echo foo'
dockeroptArray of StringsAdditional options to pass to docker when launching the container. Can be used to mount additional volumes or limit memory etc.-v /some/place:/foovol
enableuserconfigBoolSet to true to enable reading of per user ~/.dockersh filesfalsetrue
enableuserimagenameBoolSet to true to enable reading of imagename parameter from ~/.dockersh filesfalsetrue
enableusercontainernameBoolSet to true to enable reading of containername parameter from ~/.dockersh files. (Dangerous!)falsetrue
enableusermounthomeBoolSet to true to enable reading of mounthome parameter from ~/.dockersh filesfalsetrue
enableusermounttmpBoolSet to true to enable reading of mounttmp parameter from ~/.dockersh filesfalsetrue
enableusermounthometoBoolSet to true to enable reading of mounthometo parameter from ~/.dockersh filesfalsetrue
enableusermounthomefromBoolSet to true to enable reading of mounthomefrom parameter from ~/.dockersh filesfalsetrue
enableuserusercwdBoolSet to true to enable reading of usercwd parameter from ~/.dockersh filesfalsetrue
enableusercontainerusernameboolSet to true to enable reading of containerusername parameter from ~/.dockersh filesfalsetrue
enableusershellBoolSet to true to enable reading of shell parameter from ~/.dockersh filesfalsetrue
enableuserentrypointBoolSet to true to enable users to set their own supervisor daemon / entry point to the container for PID 1falsetrue
enableusercmdBoolSet to true to enable users to set the additional command parameters to the entry pointfalsetrue
enableuserdockeroptBoolSet to true to enable users to set additional options to the docker container that's started. (Dangerous!)falsetrue

Notes:

Config interpolations

The following sequences are interpolated if found in configuration variables:

SequenceInterpolation
%uThe username of the user running dockersh
%hThe homedirectory (from /etc/passwd) of the user running dockersh

Example configs

A very restricted environment, with only the busybox container, limited to 32M of memory, /etc/dockersh looks like this:

[dockersh]
imagename = busybox
shell = /bin/ash
usercwd = /

A fairly restricted shell environment, but with homedirectories and one admin user being allowed additional privs, set the following /etc/dockersh

[dockersh]
imagename = ubuntu:precise
shell = /bin/bash
mounthome

[user "someadminguy"]
mounttmp
mountdockersocket

In a less restrictive environment, you may allow users to choose their own container and shell, from a 'shell' container they have uploaded to the registry, and have ssh agent forwarding working, with the following /etc/dockersh

[dockersh]
imagename = "%u/shell"
mounthome
mounttmp
enableuserconfig
enableusershell

[user "someadminguy"]
mountdockersocket

And an example user's ~/.dockersh

[dockersh]
shell = /bin/zsh

Or just allowing your users to run whatever container they want:

[dockersh]
mounthome
mounttmp
enableuserconfig
enableuserimagename

Caveats

TODO

Contributing

Patches are very very welcome!

This is our first real Go project, so we apologise about the shoddy quality of the code.

Please make a branch and send us a pull request.

Please ensure that you use the supplied pre-commit hook to correctly format your code with go fmt:

ln -s hooks/pre-commit .git/hooks/pre-commit

Copyright

Copyright (c) 2014 Yelp. Some rights are reserved (see the LICENSE file for more details).