From 96cf898012546a059939e11a6e4aa3ddb959bc1d Mon Sep 17 00:00:00 2001 From: jurgenhaas <juergen@paragon-es.de> Date: Wed, 28 Aug 2019 17:55:22 +0200 Subject: [PATCH] docker/l3d#25 Enable MacOS support with socat --- setup/scripts/l3d | 14 +--- setup/scripts/prepareMac4L3d | 127 ++++++++++++++++++++--------------- 2 files changed, 76 insertions(+), 65 deletions(-) diff --git a/setup/scripts/l3d b/setup/scripts/l3d index f7f64ed..e576d01 100755 --- a/setup/scripts/l3d +++ b/setup/scripts/l3d @@ -1,22 +1,12 @@ #!/bin/bash -# Credit to https://github.com/uber-common/docker-ssh-agent-forward -# for this script to make L3D working on MaxOs/OSX too. SCRIPTPATH="$( cd "$(dirname "$0")" ; pwd -P )" L3DHOSTOS="$(uname -s)" +SSHAUTH=${SSH_AUTH_SOCK} +SSHAUTHSOCK=/ssh-agent case "${L3DHOSTOS}" in - Linux*) - SSHAUTH=${SSH_AUTH_SOCK} - SSHAUTHSOCK=/ssh-agent - ;; Darwin*) ${SCRIPTPATH}/prepareMac4L3d - SSHAUTH=ssh-agent - SSHAUTHSOCK=/ssh-agent/ssh-agent.sock - ;; - *) - echo "Unkown OS" - exit esac ID=$(docker container ls --all -q -f name=^l3drun$) diff --git a/setup/scripts/prepareMac4L3d b/setup/scripts/prepareMac4L3d index dd08636..966971f 100755 --- a/setup/scripts/prepareMac4L3d +++ b/setup/scripts/prepareMac4L3d @@ -1,57 +1,78 @@ #!/usr/bin/env bash -set -eo pipefail - -IMAGE_NAME=uber/ssh-agent-forward:latest -CONTAINER_NAME=pinata-sshd -VOLUME_NAME=ssh-agent -HOST_PORT=2244 -AUTHORIZED_KEYS=$(ssh-add -L | base64 | tr -d '\n') -KNOWN_HOSTS_FILE=$(mktemp -t dsaf.XXX) - -trap 'rm ${KNOWN_HOSTS_FILE}' EXIT - -docker rm -f "${CONTAINER_NAME}" >/dev/null 2>&1 || true - -docker volume create --name "${VOLUME_NAME}" - -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 + +# Credit: https://github.com/mariusgrigaitis/docker-mac-ssh-auth-sock + +SOCATID=$(docker container ls --all -q -f name=^l3d_socat$) +if [[ -n ${SOCATID} ]]; then + return +fi + +if ! which socat >/dev/null; then + echo "socat is missing. Install it and make sure the executable is in the local search path." + echo "For help see https://stackoverflow.com/questions/16808543/install-socat-on-mac" + exit 1 +fi + +if ! docker ps >/dev/null; then + echo "Docker for Mac is not running. Make sure it's running." + exit 1 +fi + +if [[ -z "${SSH_AUTH_SOCK}" ]]; then + echo "SSH_AUTH_SOCK is missing. Is ssh-agent running?" + exit 1 fi -# FIXME Find a way to get rid of this additional 1s wait +if ! test -S ${SSH_AUTH_SOCK}; then + echo "$SSH_AUTH_SOCK is not a socket. Check agent?" + exit 1 +fi + +TTY_FILE=~/Library/Containers/com.docker.docker/Data/com.docker.driver.amd64-linux/tty +TTY_FILE_NEW=~/Library/Containers/com.docker.docker/Data/vms/0/tty + +if ! test -c $TTY_FILE; then + echo "$TTY_FILE is not available. Docker for Mac setup has changed? Trying newer file..." + if ! test -c $TTY_FILE_NEW; then + echo "$TTY_FILE_NEW is not available. Docker for Mac setup has changed? Giving up." + exit 1 + else + TTY_FILE=$TTY_FILE_NEW + fi +fi + +# This is where the UGLY hack starts +# +# Problem: if you do: docker run -v $SSH_AUTH_SOCK:$SSH_AUTH_SOCK container +# you get a socket file which is mounted over osxfs from Mac host. +# This socket file can't be reused or removed because it would make ssh commands on +# host machine to not work +# +# Solution: +# 1. connect to VM over special tty channel +# 2. create an empty directory +# 3. bind mount that empty directory over $SSH_AUTH_SOCK directory +# 4. Profit +# +# This makes other docker containers see the created directory instead of osxfs mounted one. +# It also allows to create socket file under same path that does not collide with host one. +# Command is sent over special tty channel to Docker for Mac VM and does not check for errors, etc +# meaning it could be very "unreliable" +COMMAND="mkdir -p /ssh-auth-sock-hack && mount -o bind /ssh-auth-sock-hack $(dirname $SSH_AUTH_SOCK) && rmdir $SSH_AUTH_SOCK" + +echo ctr -n services.linuxkit tasks exec --exec-id 'ssh-$(hostname)-$$' '$(ctr -n services.linuxkit tasks ls -q | grep docker)' sh -c \"$COMMAND\" > $TTY_FILE +# give some time for command to execute. 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 "Docker for Mac is now prepared." + +echo "Starting socket proxy." +# This is where the proxying magic happens +# On host machine it connects to $SSH_AUTH_SOCK socket and pipes output to stdout, takes input from stdin +# On docker VM it launches a container running socat, which creates a socket file under $SSH_AUTH_SOCK path, accepts +# input and forwards it to stdout/stdin +# socat running on host machine connects stdin/stdout between those two sockets can communicate over stdin/stdout +# +# This is not really reliable because forwarding input/output over stdin/stdout does not allow for multiple communications +# at the same time. It fails when doing multiple connections to $SSH_AUTH_SOCK at the same time. +exec socat "EXEC:\"docker run -i -d --rm --restart unless-stopped --name l3d_socat -v $(dirname $SSH_AUTH_SOCK):$(dirname $SSH_AUTH_SOCK) alpine/socat UNIX-LISTEN:$SSH_AUTH_SOCK,reuseaddr,fork -\"" "EXEC:\"socat - UNIX:${SSH_AUTH_SOCK}\"" -- GitLab