Skip to content

Instantly share code, notes, and snippets.

@Fusl
Last active March 9, 2022 00:36
Show Gist options
  • Save Fusl/cd0c00d7b6e5f79cc07a7f43268e2365 to your computer and use it in GitHub Desktop.
Save Fusl/cd0c00d7b6e5f79cc07a7f43268e2365 to your computer and use it in GitHub Desktop.
spawn a docker container when a specific unix user connects via SSH
#!/usr/bin/env bash
set -e
### CONFIG START ###
homebasedir="/data/homes"
defaultimage="alpine"
### CONFIG END ###
# sudo up to root
test "root" "!=" "${USER}" && exec sudo -n "${0}" "${HOME}" "${@}"
homedir="${1}"; shift
# allow user to specify a docker image to use by placing a .dockerimage into their homedir
docker_image=$(test -f "${homedir}/.dockerimage" && cat "${homedir}/.dockerimage" || echo "${defaultimage}")
cap_drops="
SETPCAP Modify process capabilities.
MKNOD Create special files using mknod(2).
AUDIT_WRITE Write records to kernel auditing log.
#CHOWN Make arbitrary changes to file UIDs and GIDs (see chown(2)).
NET_RAW Use RAW and PACKET sockets.
#DAC_OVERRIDE Bypass file read, write, and execute permission checks.
#FOWNER Bypass permission checks on operations that normally require the file system UID of the process to match the UID of the file.
#FSETID Don’t clear set-user-ID and set-group-ID permission bits when a file is modified.
KILL Bypass permission checks for sending signals.
#SETGID Make arbitrary manipulations of process GIDs and supplementary GID list.
#SETUID Make arbitrary manipulations of process UIDs.
#NET_BIND_SERVICE Bind a socket to internet domain privileged ports (port numbers less than 1024).
#SYS_CHROOT Use chroot(2), change root directory.
#SETFCAP Set file capabilities.
"
cap_drop=""
for cap in $(echo "${cap_drops}" | grep -vE '^(#|$)' | cut -d$'\t' -f1); do
cap_drop="${cap_drop} --cap-drop=${cap}"
done
# check if a docker container for this user is running already
if ! docker container inspect "dockroot_${SUDO_USER}" </dev/fd/1 1>/dev/null 2>/dev/null; then
# if no docker container is running, try to spawn a container using the customers specified docker image
if ! docker container run --log-driver none ${cap_drop} --memory 512M --rm -d -v "${homedir}:/data:rw" --name "dockroot_${SUDO_USER}" "${docker_image}" sh -c 'while ! test -f /exit; do sleep 1; done' </dev/fd/1 1>&2; then
# if spawning a docker container using the customers specified docker image failed, try to spawn one using our image
if ! docker container run --log-driver none ${cap_drop} --memory 512M --rm -d -v "${homedir}:/data:rw" --name "dockroot_${SUDO_USER}" "${defaultimage}" sh -c 'while ! test -f /exit; do sleep 1; done' </dev/fd/1 1>&2; then
# if everything fails, bail out
echo "Failed to start Docker container" 1>&2
exit 1
fi
fi
if test -t 1; then
(
echo "##################################################"
echo "This is a temporary Docker container"
echo "Any modifications to this container are lost when the container is shut down"
echo "Modify /data/.dockerimage if you need another Docker image"
echo "Modify /data/.dockerstart with commands to execute on container startup"
echo "(/data/.dockerstart blocks the initial SSH connection so that automated backups succeed when they rely on commands on the remote host)"
echo "Type 'touch /exit' to shut down your container'"
echo "##################################################"
) 1>&2
fi
# symlink the users home/data directory to /root within the container
docker container exec "dockroot_${SUDO_USER}" /bin/sh -c 'mv /root /root.orig && ln -s /data /root'
# check if the user has a ~/.dockerstart file and execute that if it's executable
if test -x "${homedir}/.dockerstart"; then
echo "Executing ~/.dockerstart ..." 1>&2
docker container exec "dockroot_${SUDO_USER}" /data/.dockerstart 1>&2
fi
fi
if test -t 1; then
# execute a TTY bash if the client connected using a TTY compatible client ...
exec docker container exec -w /data -ti -e COLUMNS="`tput cols`" -e LINES="`tput lines`" -e TERM="${TERM}" "dockroot_${SUDO_USER}" /bin/sh "${@}"
else
# ... otherwise execute sh without TTY allocation
exec docker container exec -w /data -i "dockroot_${SUDO_USER}" /bin/sh "${@}"
fi
exit 255
username:x:1000:1000:dockrootusers:/data/homes/username:/dockroot.sh
%dockrootusers ALL=(ALL) NOPASSWD: /dockroot.sh
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment