Dockerfile check · medium

SSH server in a Docker image

Installing openssh-server, running sshd or exposing port 22 adds a second way into the container: a listening service with its own credentials, keys and patching needs. Containers don't need it for debugging, since docker exec and kubectl exec open a shell through the runtime with your existing access control. Remove the SSH server and use those instead.

$ explain Dockerfile
! RUN installs an SSH server medium
! EXPOSE 22 suggests an SSH server medium
line 2
fix: see below
$ 

What the free Dockerfile explainer reports on the risky example below.

Why it matters

sshd needs credentials (passwords or authorized keys) that live in the image or are added to it, and anyone who obtains them can log in to every container started from it.

It is another network service to patch and monitor, running alongside the app, often as root, and one more reason the container needs a process manager.

The explainer flags a RUN that installs or starts openssh-server or sshd, a CMD or ENTRYPOINT that runs sshd, and EXPOSE 22.

Risky and safer Dockerfile

RiskyDockerfile
FROM debian:12-slim
RUN apt-get update \
 && apt-get install -y --no-install-recommends openssh-server ca-certificates \
 && rm -rf /var/lib/apt/lists/*
COPY app /usr/local/bin/app
EXPOSE 22 8080
USER 10001
HEALTHCHECK CMD ["/usr/local/bin/app", "health"]
CMD ["/usr/local/bin/app"]
SaferDockerfile
FROM debian:12-slim
RUN apt-get update \
 && apt-get install -y --no-install-recommends ca-certificates \
 && rm -rf /var/lib/apt/lists/*
COPY app /usr/local/bin/app
EXPOSE 8080
USER 10001
HEALTHCHECK CMD ["/usr/local/bin/app", "health"]
CMD ["/usr/local/bin/app"]

How to fix it

  • Remove openssh-server, any sshd start command and EXPOSE 22 from the Dockerfile.
  • Debug with docker exec -it <container> sh or kubectl exec -it <pod> -- sh.
  • For minimal images with no shell, use kubectl debug to attach a temporary debugging container.

When it's acceptable

  • Images whose purpose is to be an SSH endpoint, such as a bastion or an SFTP server. Harden them as you would any SSH host: keys only, no root login, no shared host keys baked into the image.

Check your own Dockerfile

The risky example is loaded below. Paste your own file to check it for this and every other Dockerfile rule. It runs in your browser; nothing is uploaded.

Dockerfile

Parsed in your browser. Nothing is uploaded.

Explanation

1 stage7 instructions2 medium1 low

Stage 0debian:12-slim

  • Docs for FROM (opens in a new tab)

    Starts from the base image debian, tag 12-slim.

    A version tag can be re-pushed to different content; only a digest is immutable. This applies to every tagged FROM in the file.

    Safer: Pin the digest as well (image:tag@sha256:<digest>) and let a bot open pull requests when it changes.

  • Docs for RUN (opens in a new tab)

    Runs apt-get update && apt-get install -y --no-install-recommends openssh-server ca-certificates && rm -rf /var/lib/apt/lists… while building the image and saves the result as a new layer.

    An SSH server adds a long-lived credential and a listening service to the image.

    Safer: Remove it and use docker exec or kubectl exec for debugging.

  • Docs for COPY (opens in a new tab)

    Copies app from your build context to /usr/local/bin/app in the image.

  • Docs for EXPOSE (opens in a new tab)

    Documents that the container listens on 22, 8080. It does not publish them: you still need -p or ports: at run time.

    Port 22 is SSH. An SSH server in a container adds a long-lived credential and a listening service.

    Safer: Drop the SSH server and use docker exec or kubectl exec for debugging.

  • Docs for USER (opens in a new tab)

    Runs later instructions and the container's process as user 10001.

  • Docs for HEALTHCHECK (opens in a new tab)

    Runs /usr/local/bin/app health to check the container is healthy.

  • Docs for CMD (opens in a new tab)

    Sets the default command to /usr/local/bin/app. It runs when the container starts, and anything passed to docker run replaces it.

Questions

Should I run SSH inside a Docker container?

Usually not. docker exec and kubectl exec give you a shell without an extra service, credentials or open port.

How do I get a shell in a running container without SSH?

Use docker exec -it <container> sh (or bash), or kubectl exec -it <pod> -- sh on Kubernetes. For images without a shell, kubectl debug can attach a debugging container.

Why does the explainer flag EXPOSE 22?

Port 22 is SSH, so exposing it suggests an SSH server is running in the container even if the install happens elsewhere, such as in the base image.