diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..5c7247b --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,7 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [] +} \ No newline at end of file diff --git a/Dockerfile b/Dockerfile index 09e82d4..4b73c74 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,12 +1,24 @@ -FROM alpine -MAINTAINER Anil Madhavapeddy -RUN apk update && apk add openssh && \ - apk add --update --repository http://dl-cdn.alpinelinux.org/alpine/edge/community/ tini -RUN mkdir /root/.ssh && \ - chmod 700 /root/.ssh && \ - ssh-keygen -A -COPY ssh-find-agent.sh /root/ssh-find-agent.sh +FROM alpine:3.7 + +RUN { set -eux; \ + \ + mkdir /root/.ssh; \ + chmod 700 /root/.ssh; \ +} + EXPOSE 22 -VOLUME ["/root/.ssh/authorized_keys"] -ENTRYPOINT ["/usr/bin/tini","--"] -CMD ["/usr/sbin/sshd","-D"] + +VOLUME ["/ssh-agent"] + +ENTRYPOINT ["/sbin/tini", "--", "/docker-entrypoint.sh"] + +CMD ["/usr/sbin/sshd", "-D"] + +RUN apk add --no-cache \ + openssh \ + socat \ + tini \ + ; + +COPY docker-entrypoint.sh / +COPY ssh-entrypoint.sh / diff --git a/Makefile b/Makefile index 49c0488..3f7afeb 100644 --- a/Makefile +++ b/Makefile @@ -1,5 +1,5 @@ all: - ./pinata-build-sshd.sh + ./pinata-ssh-pull.sh || ./pinata-ssh-build.sh @echo Please run "make install" PREFIX ?= /usr/local @@ -7,11 +7,7 @@ BINDIR ?= $(PREFIX)/bin install: @if [ ! -d "$(PREFIX)" ]; then echo Error: need a $(PREFIX) directory; exit 1; fi - @mkdir -p $(PREFIX)/share/pinata-ssh-agent - cp Dockerfile $(PREFIX)/share/pinata-ssh-agent - cp ssh-build.sh $(PREFIX)/share/pinata-ssh-agent/ssh-build - cp ssh-find-agent.sh $(PREFIX)/share/pinata-ssh-agent/ssh-find-agent.sh @mkdir -p $(BINDIR) - cp pinata-build-sshd.sh $(BINDIR)/pinata-build-sshd cp pinata-ssh-forward.sh $(BINDIR)/pinata-ssh-forward cp pinata-ssh-mount.sh $(BINDIR)/pinata-ssh-mount + cp pinata-ssh-pull.sh $(BINDIR)/pinata-ssh-pull diff --git a/README.md b/README.md index 1389361..67b9b1c 100644 --- a/README.md +++ b/README.md @@ -1,13 +1,15 @@ Forward SSH agent socket into a container -Still experimental -- contact anil@recoil.org if you want help. +Still experimental -- contact anil@recoil.org or bryan@uber.com if you want help. + ## Installation Assuming you have a `/usr/local` ``` -$ git clone git://github.com/avsm/docker-ssh-agent-forward +$ git clone git://github.com/uber-common/docker-ssh-agent-forward +$ cd docker-ssh-agent-forward $ make $ make install ``` @@ -15,28 +17,47 @@ $ make install On every boot, do: ``` -$ pinata-ssh-forward +pinata-ssh-forward ``` -and the you can run `pinata-ssh-mount` to get a Docker CLI fragment -that adds the SSH agent socket and set `SSH_AUTH_SOCK` within the container. +and the you can run `pinata-ssh-mount` to get a Docker CLI fragment that adds +the SSH agent socket and sets `SSH_AUTH_SOCK` within the container. ``` -$ pinata-ssh-mount --v /Users/avsm/.pinata-sshd/ssh-1azk9Mmd27/agent.16:/tmp/ssh-agent.sock --env SSH_AUTH_SOCK=/tmp/ssh-agent.sock +$ pinata-ssh-mount +-v ssh-agent:/ssh-agent -e SSH_AUTH_SOCK=/ssh-agent/ssh-agent.sock -$ docker run -it `pinata-ssh-mount` ocaml/opam ssh git@github.com +$ docker run -it $(pinata-ssh-mount) +/ssh-agent-forward ssh -T git@github.com The authenticity of host 'github.com (192.30.252.128)' can't be established. RSA key fingerprint is 16:27:ac:a5:76:28:2d:36:63:1b:56:4d:eb:df:a6:48. Are you sure you want to continue connecting (yes/no)? yes Warning: Permanently added 'github.com,192.30.252.128' (RSA) to the list of known hosts. PTY allocation request failed on channel 0 Hi avsm! You've successfully authenticated, but GitHub does not provide shell access. -Connection to github.com closed. ``` +To fetch the latest image, do: + +``` +pinata-ssh-pull +``` + +## Troubleshooting + +If pinata-ssh-forward fails to run, run `ssh-add -l`. If there are no identities, then run `ssh-add`. + +## Developing + +To build an image yourself rather than fetching from Docker Hub, run +`./pinata-ssh-build.sh` from your clone of this repo. + +We didn't bother installing the build script with the Makefile since using the +hub image should be the common case. + ## Contributors * Justin Cormack +* https://github.com/uber-common/docker-ssh-agent-forward/graphs/contributors [License](LICENSE.md) is ISC. diff --git a/docker-entrypoint.sh b/docker-entrypoint.sh new file mode 100755 index 0000000..dcda3cc --- /dev/null +++ b/docker-entrypoint.sh @@ -0,0 +1,10 @@ +#!/bin/sh +set -eo pipefail + +echo "$AUTHORIZED_KEYS" | base64 -d >/root/.ssh/authorized_keys + +chown root:root /root/.ssh/authorized_keys + +ssh-keygen -A + +exec "$@" diff --git a/pinata-build-sshd.sh b/pinata-build-sshd.sh deleted file mode 100755 index 23e9a85..0000000 --- a/pinata-build-sshd.sh +++ /dev/null @@ -1,4 +0,0 @@ -#!/bin/sh - -cd /usr/local/share/pinata-ssh-agent -docker build -t pinata-sshd . diff --git a/pinata-ssh-build.sh b/pinata-ssh-build.sh new file mode 100755 index 0000000..a3ceae2 --- /dev/null +++ b/pinata-ssh-build.sh @@ -0,0 +1,2 @@ +#!/bin/sh +exec docker build -t uber/ssh-agent-forward:latest . diff --git a/pinata-ssh-forward.sh b/pinata-ssh-forward.sh index 4b236d4..52da7a2 100755 --- a/pinata-ssh-forward.sh +++ b/pinata-ssh-forward.sh @@ -1,29 +1,62 @@ -#!/bin/sh -e +#!/usr/bin/env bash +set -eo pipefail -IMAGE_NAME=pinata-sshd +IMAGE_NAME=uber/ssh-agent-forward:latest CONTAINER_NAME=pinata-sshd -LOCAL_STATE=~/.pinata-sshd -LOCAL_PORT=2244 +VOLUME_NAME=ssh-agent +HOST_PORT=2244 +AUTHORIZED_KEYS=$(ssh-add -L | base64 | tr -d '\n') +KNOWN_HOSTS_FILE=$(mktemp -t dsaf.XXX) -docker rm -f ${CONTAINER_NAME} >/dev/null 2>&1 || true -rm -rf ${LOCAL_STATE} -mkdir -p ${LOCAL_STATE} +trap 'rm ${KNOWN_HOSTS_FILE}' EXIT -docker run --name ${CONTAINER_NAME} \ - -v ~/.ssh/id_rsa.pub:/root/.ssh/authorized_keys \ - -v ${LOCAL_STATE}:/tmp \ - -d -p ${LOCAL_PORT}:22 ${IMAGE_NAME} > /dev/null +docker rm -f "${CONTAINER_NAME}" >/dev/null 2>&1 || true -IP=`docker inspect --format '{{(index (index .NetworkSettings.Ports "22/tcp") 0).HostIp }}' ${CONTAINER_NAME}` -ssh-keyscan -p ${LOCAL_PORT} ${IP} > ${LOCAL_STATE}/known_hosts 2>/dev/null +docker volume create --name "${VOLUME_NAME}" -ssh -f -o "UserKnownHostsFile=${LOCAL_STATE}/known_hosts" \ - -A -p ${LOCAL_PORT} root@${IP} \ - /root/ssh-find-agent.sh +docker run \ + --name "${CONTAINER_NAME}" \ + -e AUTHORIZED_KEYS="${AUTHORIZED_KEYS}" \ + -v ${VOLUME_NAME}:/ssh-agent \ + -d \ + -p "${HOST_PORT}:22" \ + "${IMAGE_NAME}" >/dev/null \ +; + +if [ "${DOCKER_HOST}" ]; then + HOST_IP=$(echo "$DOCKER_HOST" | awk -F '//' '{print $2}' | awk -F ':' '{print $1}') +else + HOST_IP=127.0.0.1 +fi + +# FIXME Find a way to get rid of this additional 1s wait +sleep 1 +while ! nc -z -w5 ${HOST_IP} ${HOST_PORT}; do sleep 0.1; done + +ssh-keyscan -p "${HOST_PORT}" "${HOST_IP}" >"${KNOWN_HOSTS_FILE}" 2>/dev/null + +# show the keys that are being forwarded +ssh \ + -A \ + -o "UserKnownHostsFile=${KNOWN_HOSTS_FILE}" \ + -p "${HOST_PORT}" \ + -S none \ + "root@${HOST_IP}" \ + ssh-add -l + +# keep the agent running +ssh \ + -A \ + -f \ + -o "UserKnownHostsFile=${KNOWN_HOSTS_FILE}" \ + -p "${HOST_PORT}" \ + -S none \ + "root@${HOST_IP}" \ + /ssh-entrypoint.sh echo 'Agent forwarding successfully started.' echo 'Run "pinata-ssh-mount" to get a command-line fragment that' echo 'can be added to "docker run" to mount the SSH agent socket.' echo "" echo 'For example:' -echo 'docker run -it `pinata-ssh-mount` ocaml/opam ssh git@github.com' +echo "docker run -it \$(pinata-ssh-mount) uber/ssh-agent-forward ssh -T git@github.com" diff --git a/pinata-ssh-mount.sh b/pinata-ssh-mount.sh index 9835091..56e9b78 100755 --- a/pinata-ssh-mount.sh +++ b/pinata-ssh-mount.sh @@ -1,5 +1,2 @@ -#!/bin/sh - -LOCAL_STATE=~/.pinata-sshd -AGENT=`cat ${LOCAL_STATE}/agent_socket_path | sed -e 's,/tmp/,,g'` -echo "-v ${LOCAL_STATE}/$AGENT:/tmp/ssh-agent.sock --env SSH_AUTH_SOCK=/tmp/ssh-agent.sock" +#!/usr/bin/env bash +echo "-v ssh-agent:/ssh-agent -e SSH_AUTH_SOCK=/ssh-agent/ssh-agent.sock" diff --git a/pinata-ssh-pull.sh b/pinata-ssh-pull.sh new file mode 100755 index 0000000..f8f97f6 --- /dev/null +++ b/pinata-ssh-pull.sh @@ -0,0 +1,2 @@ +#!/bin/sh +exec docker pull uber/ssh-agent-forward:latest diff --git a/ssh-build.sh b/ssh-build.sh deleted file mode 100755 index 4a65084..0000000 --- a/ssh-build.sh +++ /dev/null @@ -1,5 +0,0 @@ -#!/bin/sh - -IMAGE_NAME=pinata-sshd - -docker build -q -t ${IMAGE_NAME} . diff --git a/ssh-entrypoint.sh b/ssh-entrypoint.sh new file mode 100755 index 0000000..33e0fa2 --- /dev/null +++ b/ssh-entrypoint.sh @@ -0,0 +1,10 @@ +#!/bin/sh +# Forward SSH agent socket to a well-known location +set -eo pipefail + +FORWARDED_SOCKET=/ssh-agent/ssh-agent.sock + +[ -z "$SSH_AUTH_SOCK" ] && exit 1 + +rm -f "${FORWARDED_SOCKET}" +socat UNIX-LISTEN:"${FORWARDED_SOCKET}",fork,mode=777 UNIX-CONNECT:"${SSH_AUTH_SOCK}" diff --git a/ssh-find-agent.sh b/ssh-find-agent.sh deleted file mode 100755 index 9dfb677..0000000 --- a/ssh-find-agent.sh +++ /dev/null @@ -1,9 +0,0 @@ -#!/bin/sh -e -# Log the location of the SSH agent to a file - -finish() { - rm -f /tmp/agent_socket_path -} -trap finish EXIT -echo $SSH_AUTH_SOCK > /tmp/agent_socket_path -tail -f /dev/null