Skip to content

Commit

Permalink
Key file doesn't need to be base64-encoded
Browse files Browse the repository at this point in the history
  • Loading branch information
mjnaderi committed Aug 13, 2024
1 parent 5086242 commit 0a05133
Show file tree
Hide file tree
Showing 3 changed files with 83 additions and 90 deletions.
59 changes: 25 additions & 34 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,12 @@ The server image supports the following environment variables:

#### SSH keys

| Environment Variable | Description |
| --------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------- |
| `SERVER_ED25519_PRIVATE_KEY_BASE64` <br> _required_ | The server's host private key (ed25519). The client needs to have the corresponding public key in `known_hosts`. |
| `SERVER_ED25519_PRIVATE_KEY_BASE64_FILE` | Alternative to `SERVER_ED25519_PRIVATE_KEY_BASE64`, for usage with docker secrets. |
| `SERVER_ED25519_PUBLIC_KEY` | The server's host public key (ed25519). |
| `CLIENT_AUTHORIZED_KEYS` <br> _required_ | The client public keys authorized to connect as `tunnel` user. The keys should be separated by semicolons (`;`). |
| Environment Variable | Description |
| ------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------- |
| `SERVER_ED25519_PRIVATE_KEY_FILE` <br> _required_ | Path to server's host private key (ed25519). The client needs to have the corresponding public key in `known_hosts`. |
| `SERVER_ED25519_PRIVATE_KEY_BASE64` | Alternative to `SERVER_ED25519_PRIVATE_KEY_FILE` (base64-encoded value). |
| `SERVER_ED25519_PUBLIC_KEY` | The server's host public key (ed25519). |
| `CLIENT_AUTHORIZED_KEYS` <br> _required_ | The client public keys authorized to connect as `tunnel` user. The keys should be separated by semicolons (`;`). |

#### SSHD options

Expand Down Expand Up @@ -59,11 +59,11 @@ The client image supports the following environment variables:

#### SSH keys

| Environment Variable | Equivalent ssh_config Argument |
| --------------------------------------------------- | ---------------------------------------------------------------------------------- |
| `SERVER_ED25519_PUBLIC_KEY` <br> _required_ | The server's host public key (ed25519). This key will be added to `known_hosts`. |
| `CLIENT_ED25519_PRIVATE_KEY_BASE64` <br> _required_ | The client's SSH private key. |
| `CLIENT_ED25519_PRIVATE_KEY_BASE64_FILE` | Alternative to `CLIENT_ED25519_PRIVATE_KEY_BASE64`, for usage with docker secrets. |
| Environment Variable | Equivalent ssh_config Argument |
| ------------------------------------------------- | -------------------------------------------------------------------------------- |
| `SERVER_ED25519_PUBLIC_KEY` <br> _required_ | The server's host public key (ed25519). This key will be added to `known_hosts`. |
| `CLIENT_ED25519_PRIVATE_KEY_FILE` <br> _required_ | Path to the client's SSH private key. |
| `CLIENT_ED25519_PRIVATE_KEY_BASE64` | Alternative to `CLIENT_ED25519_PRIVATE_KEY_FILE` (base64-encoded value) |

#### SSH options

Expand Down Expand Up @@ -114,12 +114,7 @@ We encode the private keys in base64 format to pass them as environment variable

```shell
ssh-keygen -t ed25519 -N '' -C key1-$(date -I) -f key1
cat key1 | base64 -w 0 > key1.base64
chmod 600 key1.base64

ssh-keygen -t ed25519 -N '' -C key2-$(date -I) -f key2
cat key2 | base64 -w 0 > key2.base64
chmod 600 key2.base64
```

> [!WARNING]
Expand All @@ -135,25 +130,21 @@ the client and server containers should be run on different hosts.
Start server and client services:

```shell
KEY1_BASE64=$(cat key1.base64)
KEY2_PUB=$(cat key2.pub)
docker run --name tunnel-server --rm -it --init \
--user 12345:12345 \
-e SERVER_ED25519_PRIVATE_KEY_BASE64="$KEY1_BASE64" \
-e CLIENT_AUTHORIZED_KEYS="$KEY2_PUB" \
-e SERVER_ED25519_PRIVATE_KEY_BASE64="$(cat key1 | base64 -w 0)" \
-e CLIENT_AUTHORIZED_KEYS="$(cat key2.pub)" \
-e SSHD_PERMIT_LISTEN="0.0.0.0:4444" \
-p 2222:22 \
-p 127.0.0.1:4444:4444 \
ghcr.io/querateam/docker-ssh-tunnel/server
```

```shell
KEY2_BASE64=$(cat key2.base64)
KEY1_PUB=$(cat key1.pub)
docker run --name tunnel-client --rm -it --init --add-host=host.docker.internal:host-gateway \
--user 12345:12345 \
-e SERVER_ED25519_PUBLIC_KEY="$KEY1_PUB" \
-e CLIENT_ED25519_PRIVATE_KEY_BASE64="$KEY2_BASE64" \
-e SERVER_ED25519_PUBLIC_KEY="$(cat key1.pub)" \
-e CLIENT_ED25519_PRIVATE_KEY_BASE64="$(cat key2 | base64 -w 0)" \
-e SSH_HOSTNAME="host.docker.internal" \
-e SSH_PORT="2222" \
-e SSH_CLI_OPTIONS="-R 0.0.0.0:4444:127.0.0.1:6666" \
Expand Down Expand Up @@ -181,11 +172,11 @@ services:
tunnel-server:
image: ghcr.io/querateam/docker-ssh-tunnel/server
restart: always
user: 12345:12345 # must have read access to key1.base64
user: 12345:12345 # must have read access to key1
secrets:
- key1_base64
- key1
environment:
SERVER_ED25519_PRIVATE_KEY_BASE64_FILE: /run/secrets/key1_base64
SERVER_ED25519_PRIVATE_KEY_FILE: /run/secrets/key1
CLIENT_AUTHORIZED_KEYS: ... value of key2.pub ...
SSHD_PERMIT_LISTEN: 0.0.0.0:4444
ports:
Expand All @@ -195,23 +186,23 @@ services:
tunnel-client:
image: ghcr.io/querateam/docker-ssh-tunnel/client
restart: always
user: 12345:12345 # must have read access to key2.base64
user: 12345:12345 # must have read access to key2
secrets:
- key2_base64
- key2
environment:
CLIENT_ED25519_PRIVATE_KEY_FILE: /run/secrets/key2
SERVER_ED25519_PUBLIC_KEY: ... value of key1.pub ...
CLIENT_ED25519_PRIVATE_KEY_BASE64_FILE: /run/secrets/key2_base64
SSH_HOSTNAME: host.docker.internal
SSH_PORT: 2222
SSH_CLI_OPTIONS: -R 0.0.0.0:4444:127.0.0.1:6666
extra_hosts:
- host.docker.internal:host-gateway

secrets:
key1_base64:
file: key1.base64
key2_base64:
file: key2.base64
key1:
file: ./key1
key2:
file: ./key2
```
To test the tunnel connection,
Expand Down
57 changes: 29 additions & 28 deletions client/entrypoint.sh
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
#!/bin/sh

# Ensure the script is not run by the "root" user.
if [ "$(id -u)" = "0" ]; then
if [ "$(id -u)" == "0" ]; then
echo "This image should not be run as the 'root' user. Exiting..."
exit 1
fi

if [ -z "$SSH_HOSTNAME" ]; then
if [ -z "${SSH_HOSTNAME}" ]; then
echo "SSH_HOSTNAME is not set. Exiting..."
exit 1
fi
Expand All @@ -20,37 +20,38 @@ export HOME="/tmp/tunnel"
echo "tunnel:x:$(id -u):$(id -g):Tunnel User:${HOME}:/bin/false" >/tmp/passwd
echo "tunnel:x:$(id -g):tunnel" >/tmp/group
export LD_PRELOAD=/usr/lib/libnss_wrapper.so NSS_WRAPPER_PASSWD=/tmp/passwd NSS_WRAPPER_GROUP=/tmp/group
mkdir -p "$HOME/.ssh"
chmod -R 700 "$HOME"
mkdir -p "${HOME}/.ssh"
chmod -R 700 "${HOME}"

################################
# read private key from file #
# setup keys #
################################
if [ -n "${CLIENT_ED25519_PRIVATE_KEY_BASE64_FILE:-}" ]; then
if [ -r "${CLIENT_ED25519_PRIVATE_KEY_BASE64_FILE:-}" ]; then
CLIENT_ED25519_PRIVATE_KEY_BASE64="$(cat "${CLIENT_ED25519_PRIVATE_KEY_BASE64_FILE}")"
if [ -n "${CLIENT_ED25519_PRIVATE_KEY_FILE}" ]; then
if [ -r "${CLIENT_ED25519_PRIVATE_KEY_FILE}" ]; then
if [ "${CLIENT_ED25519_PRIVATE_KEY_FILE}" != "${HOME}/.ssh/id_ed25519" ]; then
cp "${CLIENT_ED25519_PRIVATE_KEY_FILE}" "${HOME}/.ssh/id_ed25519"
chmod 600 "${HOME}/.ssh/id_ed25519"
fi
echo "Installed private key from key file."
else
echo "'${CLIENT_ED25519_PRIVATE_KEY_BASE64_FILE:-}' is not readable."
echo "'${CLIENT_ED25519_PRIVATE_KEY_FILE}' is not readable. Exiting..."
exit 1
fi
fi

################################
# setup keys #
################################
if [ -z "$CLIENT_ED25519_PRIVATE_KEY_BASE64" ]; then
echo "CLIENT_ED25519_PRIVATE_KEY_BASE64 is not set. Exiting..."
exit 1
elif [ -n "${CLIENT_ED25519_PRIVATE_KEY_BASE64}" ]; then
echo "${CLIENT_ED25519_PRIVATE_KEY_BASE64}" | base64 -d >"${HOME}/.ssh/id_ed25519"
chmod 600 "${HOME}/.ssh/id_ed25519"
echo "Installed private key from env var."
else
echo "$CLIENT_ED25519_PRIVATE_KEY_BASE64" | base64 -d >$HOME/.ssh/id_ed25519
chmod 600 $HOME/.ssh/id_ed25519
echo "No private key provided. Exiting..."
exit 1
fi

if [ -z "$SERVER_ED25519_PUBLIC_KEY" ]; then
if [ -n "${SERVER_ED25519_PUBLIC_KEY}" ]; then
echo "[${SSH_HOSTNAME}]:${SSH_PORT:-22} ${SERVER_ED25519_PUBLIC_KEY}" >"${HOME}/.ssh/known_hosts"
chmod 600 "${HOME}/.ssh/known_hosts"
else
echo "SERVER_ED25519_PUBLIC_KEY is not set. Exiting..."
exit 1
else
echo "[$SSH_HOSTNAME]:${SSH_PORT:-22} $SERVER_ED25519_PUBLIC_KEY" >$HOME/.ssh/known_hosts
chmod 600 $HOME/.ssh/known_hosts
fi

################################
Expand All @@ -63,16 +64,16 @@ ServerAliveInterval ${SSH_SERVER_ALIVE_INTERVAL:-10}
ServerAliveCountMax ${SSH_SERVER_ALIVE_COUNT_MAX:-3}
ExitOnForwardFailure ${SSH_EXIT_ON_FORWARD_FAILURE:-yes}
SessionType ${SSH_SESSION_TYPE:-none}
" >$HOME/.ssh/config
" >"${HOME}/.ssh/config"

################################
# autossh options #
################################
export AUTOSSH_PORT=${AUTOSSH_PORT:-0}
export AUTOSSH_GATETIME=${AUTOSSH_GATETIME:-0}
export AUTOSSH_POLL=${AUTOSSH_POLL:-30}
export AUTOSSH_PORT="${AUTOSSH_PORT:-0}"
export AUTOSSH_GATETIME="${AUTOSSH_GATETIME:-0}"
export AUTOSSH_POLL="${AUTOSSH_POLL:-30}"

################################
# start the SSH tunnel #
################################
exec /usr/bin/autossh -T $SSH_CLI_OPTIONS $SSH_HOSTNAME
exec /usr/bin/autossh -T ${SSH_CLI_OPTIONS} "${SSH_HOSTNAME}"
57 changes: 29 additions & 28 deletions server/entrypoint.sh
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
#!/bin/sh

# Ensure the script is not run by the "root" user.
if [ "$(id -u)" = "0" ]; then
if [ "$(id -u)" == "0" ]; then
echo "This image should not be run as the 'root' user. Exiting..."
exit 1
fi
Expand All @@ -15,49 +15,50 @@ export HOME="/tmp/tunnel"
echo "tunnel:x:$(id -u):$(id -g):Tunnel User:${HOME}:/bin/false" >/tmp/passwd
echo "tunnel:x:$(id -g):tunnel" >/tmp/group
export LD_PRELOAD=/usr/lib/libnss_wrapper.so NSS_WRAPPER_PASSWD=/tmp/passwd NSS_WRAPPER_GROUP=/tmp/group
mkdir -p "$HOME/sshd" "$HOME/.ssh"
chmod -R 700 "$HOME"
mkdir -p "${HOME}/sshd" "${HOME}/.ssh"
chmod -R 700 "${HOME}"

################################
# read private key from file #
# setup host key #
################################
if [ -n "${SERVER_ED25519_PRIVATE_KEY_BASE64_FILE:-}" ]; then
if [ -r "${SERVER_ED25519_PRIVATE_KEY_BASE64_FILE:-}" ]; then
SERVER_ED25519_PRIVATE_KEY_BASE64="$(cat "${SERVER_ED25519_PRIVATE_KEY_BASE64_FILE}")"
if [ -n "${SERVER_ED25519_PRIVATE_KEY_FILE}" ]; then
if [ -r "${SERVER_ED25519_PRIVATE_KEY_FILE}" ]; then
if [ "${SERVER_ED25519_PRIVATE_KEY_FILE}" != "${HOME}/sshd/ssh_host_ed25519_key" ]; then
cp "${SERVER_ED25519_PRIVATE_KEY_FILE}" "${HOME}/sshd/ssh_host_ed25519_key"
chmod 600 "${HOME}/sshd/ssh_host_ed25519_key"
fi
echo "Installed host key from key file."
else
echo "'${SERVER_ED25519_PRIVATE_KEY_BASE64_FILE:-}' is not readable."
echo "'${SERVER_ED25519_PRIVATE_KEY_FILE}' is not readable. Exiting..."
exit 1
fi
fi

################################
# setup host key #
################################
if [ -z "$SERVER_ED25519_PRIVATE_KEY_BASE64" ]; then
echo "SERVER_ED25519_PRIVATE_KEY_BASE64 is not set. Exiting..."
exit 1
elif [ -n "${SERVER_ED25519_PRIVATE_KEY_BASE64}" ]; then
echo "${SERVER_ED25519_PRIVATE_KEY_BASE64}" | base64 -d >"${HOME}/sshd/ssh_host_ed25519_key"
chmod 600 "${HOME}/sshd/ssh_host_ed25519_key"
echo "Installed host key from env var."
else
echo "Setting up the host key from the environment variable..."
echo "$SERVER_ED25519_PRIVATE_KEY_BASE64" | base64 -d >"$HOME/sshd/ssh_host_ed25519_key"
chmod 600 "$HOME/sshd/ssh_host_ed25519_key"
echo "No private key provided. Exiting..."
exit 1
fi
if [ -n "$SERVER_ED25519_PUBLIC_KEY" ]; then
echo "$SERVER_ED25519_PUBLIC_KEY" >"$HOME/sshd/ssh_host_ed25519_key.pub"
chmod 644 "$HOME/sshd/ssh_host_ed25519_key.pub"

if [ -n "${SERVER_ED25519_PUBLIC_KEY}" ]; then
echo "${SERVER_ED25519_PUBLIC_KEY}" >"${HOME}/sshd/ssh_host_ed25519_key.pub"
chmod 644 "${HOME}/sshd/ssh_host_ed25519_key.pub"
fi

################################
# configure authorized_keys #
################################
if [ -z "$CLIENT_AUTHORIZED_KEYS" ]; then
if [ -z "${CLIENT_AUTHORIZED_KEYS}" ]; then
echo "CLIENT_AUTHORIZED_KEYS is not set. Exiting..."
exit 1
else
# Split the CLIENT_AUTHORIZED_KEYS variable by semicolon and add each to authorized_keys
echo "$CLIENT_AUTHORIZED_KEYS" | tr ';' '\n' | while IFS= read -r key; do
echo "${CLIENT_AUTHORIZED_KEYS}" | tr ';' '\n' | while IFS= read -r key; do
# Process each key here
echo "$key" >>"$HOME/.ssh/authorized_keys"
echo "${key}" >>"${HOME}/.ssh/authorized_keys"
done
chmod 600 "$HOME/.ssh/authorized_keys"
chmod 600 "${HOME}/.ssh/authorized_keys"
fi

################################
Expand Down Expand Up @@ -87,9 +88,9 @@ X11Forwarding ${SSHD_X11_FORWARDING:-no}
AllowAgentForwarding ${SSHD_ALLOW_AGENT_FORWARDING:-no}
ForceCommand ${SSHD_FORCE_COMMAND:-"/sbin/nologin"}
AllowUsers ${SSHD_ALLOW_USERS:-tunnel}
" >"$HOME/sshd/sshd.conf"
" >"${HOME}/sshd/sshd.conf"

################################
# Start sshd #
################################
exec /usr/sbin/sshd -D -e -f "$HOME/sshd/sshd.conf"
exec /usr/sbin/sshd -D -e -f "${HOME}/sshd/sshd.conf"

0 comments on commit 0a05133

Please sign in to comment.