An awesome sshjumphost (bastion host) for easily accessing container backend networks and other ssh gateway games.
The sshjumphost
makes easy work of setting up containerized ssh-key based or
user-ca-key based authentication ssh jumphosts.
By default, sshjumphost does not provide any login-shell since the intent is
to jump from the sshjumphost to another. All TCP port forwarding and -J
style ssh jumphost functionality is possible without a shell. You can still
get a shell setting the SSH_SHELL
environment variable.
Example: establish a SOCKS proxy inside your backend network
user@computer ~/$ ssh -D 1080 username@awesome.company.net
#
# sshjumphost
# version: 3.0.0 (a0dbd7a7)
# documentation: https://github.com/threatpatrols/docker-sshjumphost
#
# NB: set the SSH_SHELL environment variable to enable a login-shell at the sshjumphost.
#
docker pull threatpatrols/sshjumphost
services:
externalhost01:
# Description :
# - username adjusted to "awesome" rather than the default "sshjumphost"
# - ssh key(s) supplied via environment variable in this example externalhost01
# - mount /data/hostkeys to store the hostkeys so these remain persistent on restart
# - this host is attached to the "awesome_network" that the "internalhost01" is also attached to below
image: threatpatrols/sshjumphost:latest
restart: unless-stopped
ports:
- 22222:22/tcp
volumes:
- "hostkeys_externalhost01:/data/hostkeys"
networks:
- awesome_network
environment:
SSH_USERNAME: "awesome" # NB: default = sshjumphost
SSH_AUTHORIZED_KEYS: "sk-ecdsa-sha2-nistp256@openssh.com xxxxxxxxxxxxxxxxxxxxxxxxx" # NB: use your ssh key(s) here
internalhost01:
# Description :
# - username here is also adjusted to "awesome" rather than the default "sshjumphost"
# - ssh key(s) supplied via volume mount in this example internalhost01, alternative would be to simply
# mount "/data/userkeys/authorized_keys" and not set SSH_AUTHORIZED_KEYS
# - mount /data/hostkeys to store the hostkeys so these remain persistent on restart
# - this host is also attached to the "awesome_network" which makes it reachable from the above externalhost01 host
# - this host has SSH_SHELL defined which hence provides the user with a shell for the sake of the example
image: threatpatrols/sshjumphost:latest
restart: unless-stopped
volumes:
- "hostkeys_internalhost01:/data/hostkeys"
- "/home/awesome/.ssh/authorized_keys:/data/userkeys/awesome_authorized_keys:ro"
networks:
- awesome_network
environment:
SSH_USERNAME: "awesome"
SSH_SHELL: "/bin/bash"
SSH_AUTHORIZED_KEYS: "/data/userkeys/awesome_authorized_keys" # NB: name of mount above
networks:
awesome_network:
volumes:
hostkeys_externalhost01:
hostkeys_internalhost01:
Starting this docker-compose creates 2x docker-containers, where externalhost01
is reachable via localhost on tcp-22222 and internalhost01
that is only reachable
via connectivity to the awesome_network
It is now possible to reach internalhost01
by using the -J
switch to jump
through externalhost01
located at 127.0.0.1:22222
Note in the example provided the SSH_SHELL
has been set on the internalhost01
which provides the shell prompt as shown below.
user@computer ~/$ ssh -J 127.0.0.1:22222 internalhost01
The authenticity of host 'internalhost01 (<no hostip for proxy command>)' can't be established.
ED25519 key fingerprint is SHA256:eoCSPJQMzNcmTZcaE0ge3EL4XbmTW8Y0bGqTZZ0Byk8.
This key is not known by any other names
Are you sure you want to continue connecting (yes/no/[fingerprint])? yes
Warning: Permanently added 'internalhost01' (ED25519) to the list of known hosts.
#
# sshjumphost: refer to documentation
# https://hub.docker.com/r/threatpatrols/sshjumphost
#
# NB: set the SSH_SHELL environment variable to enable a login-shell at the sshjumphost.
#
0bf40e5b195f:~$
The sshjumphost container also provides good visibility to STDOUT making it easy to monitor via your container-environment logging etc.
dev-host01-1 | ssh-keygen: generating new host keys: RSA DSA ECDSA ED25519
dev-host01-1 | hostname: 0bf40e5b195f
dev-host01-1 | ip addr: inet 192.168.112.2/20 brd 192.168.127.255 scope global eth0
dev-host01-1 | username: awesome
dev-host01-1 | sshkey-fingerprints:
dev-host01-1 | 4096 SHA256:BjNltbmmfjBAF6/liSr+7NANEWzbyDvLJ6w7eTA928c (RSA)
dev-host01-1 | hostkey-fingerprints:
dev-host01-1 | 1024 SHA256:eXQcmDnDi2ydn+DAtLW0hGu9uXU5HTDKjNnrptyunFo root@0bf40e5b195f (DSA)
dev-host01-1 | 256 SHA256:8tIwmrhrK8+ZLw34B+OtVvOQe976d9mRByNpgnRR2FM root@0bf40e5b195f (ECDSA)
dev-host01-1 | 256 SHA256:eoCSPJQMzNcmTZcaE0ge3EL4XbmTW8Y0bGqTZZ0Byk8 root@0bf40e5b195f (ED25519)
dev-host01-1 | 3072 SHA256:cOue3Ig4hFIYWaHW1Xm3jv923+tkDAUeICw6Kk/R7gs root@0bf40e5b195f (RSA)
dev-host01-1 | sshd_command: /usr/sbin/sshd -D -e -4 -o PasswordAuthentication=no -o PermitEmptyPasswords=no -o PermitRootLogin=no -o AuthorizedKeysFile=/data/home/.ssh/authorized_keys -o HostKey=/data/hostkeys/ssh_host_dsa_key -o HostKey=/data/hostkeys/ssh_host_ecdsa_key -o HostKey=/data/hostkeys/ssh_host_ed25519_key -o HostKey=/data/hostkeys/ssh_host_rsa_key -o PubkeyAuthentication=yes -o GatewayPorts=no -o PermitTunnel=no -o X11Forwarding=no -o AllowTcpForwarding=yes -o AllowAgentForwarding=yes -o ListenAddress=0.0.0.0 -o Port=22 -o LogLevel=INFO
dev-host01-1 | Server listening on 0.0.0.0 port 22.
- Default:
sshjumphost
Change the username to suit your purposes.
- Default:
/data/userkeys/authorized_keys
Can be either the actual ssh-key string-value -or- set as the pathname to the volume-mounted public-key(s) if you mount the keys at a location other than the default.
Recall that it is possible to set this value with more than one ssh-public key, using one key per line.
- Default:
/bin/dd
By default, the sshjumphost user shell is set to /bin/dd
that establishes a
session that does not terminate unless user-interrupted (eg CONTROL-C). This hence
creates a sshjumphost cannot be repurposed for other usage beyond the intended
jumphost capability.
By setting the SSH_SHELL
environment variable to /bin/ash
it is possible to
gain a login-shell on the sshjumphost if required for some reason.
- Default:
/data/cakeys/trusted_keys
Can be either the actual ca-key string-value -or- set as the pathname to the volume-mounted ca-key if you mount the key at a location other than the default.
The associated AuthorizedPrincipalsFile
is read from /data/cakeys/authorized_principals
and
if this file does not already exist (eg via a volume-mount) then it is populated
with a single entry for the SSH_USERNAME
Using this mechanism it is possible to operate the sshjumphost in a way that accommodates multiple usernames authenticated through their individual user-keys that have been signed by the certificate authority.
- Default:
no
Specifies whether remote hosts are allowed to connect to ports forwarded for the client (ie reverse tunneling)
- Default:
no
Specifies whether tun(4) device forwarding is allowed - NB: this is typically not what you want if you are just looking for TCP port-forwarding.
- Default:
no
Specifies whether X11 forwarding is permitted.
- Default:
yes
Specifies whether TCP forwarding is permitted.
- Default:
yes
Specifies whether ssh-agent forwarding is permitted.
- Default:
no
Specifies whether a root user login is permitted.
- Default:
0.0.0.0
Specifies the local addresses sshd(8) should listen on.
- Default:
22
Specifies the TCP port number that sshd(8) listens on.
- Default:
INFO
Gives the verbosity level that is used when logging messages from sshd(8).
- v2.x to v3.x - moved from Alpine based images to Debian images
- Copyright (c) 2022-2024 Nicholas de Jong ndejong@threatpatrols.com
This project originally forked from binlab/docker-bastion and mostly re-written top to bottom - thanks Mark for the initial base to work from.