An SSH jump host in a container. Clients connect through it to SSH servers they cannot reach directly — the usual situation when firewalls, routing or private addressing keep the target hosts off the network the client is on.
The image deliberately offers nothing but forwarding: the single user has no shell, no TTY and no password.
The image is not usable as-is — it needs your public keys at build time:
docker build --build-arg AUTHORIZED_KEYS=./authorized_keys -t ssh-bastion .
AUTHORIZED_KEYS points at a file inside the build context containing one public
key per line. It is installed as /home/bastion/.ssh/authorized_keys.
Host keys are generated during the build (ssh-keygen -A), so every build
produces a distinct host identity. Rebuilding therefore changes the host key and
clients will warn about it — keep a built image around rather than rebuilding
casually, or mount persistent host keys over /etc/ssh/.
docker run -d --name bastion -p 2200:22 ssh-bastion
Connect through it to a host behind the bastion:
ssh -J [email protected]:2200 user@internal-host
-J (ProxyJump) is the modern form. The older equivalent:
ssh -o ProxyCommand='ssh -W %h:%p -p 2200 [email protected]' user@internal-host
Configuring the jump once in ~/.ssh/config avoids repeating it. A pattern can
cover a whole set of hosts:
Host bastion
HostName bastion.example.com
Port 2200
User bastion
Host internal-*
ProxyJump bastion
User me
ssh internal-web then transparently jumps through the bastion.
| Component | Purpose |
|---|---|
openssh-server | The SSH daemon |
dumb-init | PID 1, so signals and zombie processes are handled correctly |
avahi-daemon, libnss-mdns | mDNS name resolution for .local targets |
The bastion user is created with /sbin/nologin as its shell and a disabled
password, so key authentication is the only way in.
There is nothing to configure at runtime. Access is defined entirely by the keys
baked in at build time; to change who has access, rebuild with a different
AUTHORIZED_KEYS file.
The container exposes port 22 and runs sshd -D -e, which keeps it in the
foreground and logs to stderr, so docker logs shows connection attempts.
Two things in this repository do not do what they look like:
sshd_config is not applied. The file in the repository root describes the
intended hardening (ForceCommand /sbin/nologin, PermitTTY no,
AllowUsers bastion, PermitTunnel no, no password auth). The Dockerfile
never copies it, so the image runs with the distribution's default
configuration. Password authentication is still effectively blocked because the
bastion account has no password and no other account is usable, but the
explicit restrictions — no TTY, no tunnelling, forced nologin — are not
in effect. Copy the file into /etc/ssh/sshd_config in a derived image, or add
the COPY here, to get the documented behaviour.find / -perm +6000 is not valid GNU
find syntax; the command fails and is swallowed by || true. The correct
form is -perm /6000.bastion, is shared by all clients. Distinguish users by
which key authenticated, visible in the container log.MIT — see LICENSE.
Content type
Image
Digest
sha256:fa7c4579d…
Size
40.1 MB
Last updated
4 days ago
docker pull ricariel/bastion