~/docs

Docker host

Record docker exec, docker attach and docker compose exec on a Linux server that runs Docker, without changing your images.

↗ .mdmcp

On a Linux server that runs Docker, the standard install also records every interactive session into a container: docker exec, docker attach, docker run -it and docker compose exec. Your images and containers don't change.

Install

Install shellz on the server exactly as described in Linux server:

curl -fsSL https://get.shellz.dev | sudo sh -s -- szt_<join token>

shellz finds Docker automatically. Docker isn't restarted and running containers keep running. If you install Docker after shellz, it's picked up within 15 seconds.

Check it:

sudo /usr/local/bin/shellz status
Output
  ...
  Containers recording Docker sessions on /run/docker.sock

Right after install, the line may end with (recording; dockerd moves to /run/shellz/dockerd.sock at its next restart). That's normal: sessions are already recorded, and Docker switches sockets the next time it restarts. A line starting with NOT RECORDED explains what's wrong; see What isn't covered.

How it works

Every Docker command talks to the Docker daemon through the API socket, /run/docker.sock. shellz moves the daemon to a private socket, /run/shellz/dockerd.sock, readable only by root, and serves the original socket path itself with the same owner and permissions. Members of the docker group keep working as before.

shellz forwards every Docker API call to the daemon unchanged. When a call opens a session into a container (exec or attach), shellz records the stream in both directions before passing it on.

The change is made with systemd drop-in files (zz-shellz.conf) for docker.socket and docker.service, which are removed when shellz is removed. If someone deletes or replaces the socket, shellz puts it back within a second and raises a tamper-repaired alert.

What gets recorded

CommandRecorded
docker exec -it <container> bashYes, as a full session
docker exec <container> <command> (no terminal)Yes, as a command session
docker attach <container>Yes
docker run -it <image>Yes, as an attach session
docker compose exec <service> shYes
Docker API calls from tools and SDKs using the socketExec and attach calls, yes
docker logs, docker ps, image builds and pullsNo, these aren't sessions

Each container session shows:

  • the container's ID, name and image, and whether it was exec or attach;
  • the user inside the container;
  • the server account that called Docker, read from the kernel, not from anything the client says;
  • a link to the parent session when the docker command was run from a recorded SSH session.

As with SSH sessions, input typed at hidden prompts isn't recorded.

When recording isn't possible

If shellz can't record a new container session, for example because the spool is full while the network is down, the recording policy decides what happens:

  • Fail closed and buffer: the Docker API returns an error, the client shows shellz: recording unavailable, container session refused, and a session-refused alert is raised.
  • Fail open: the session runs.

What isn't covered

shellz raises an alert when it finds Docker set up in a way it can't record:

AlertCauseFix
docker-tcp-listener (critical)The Docker daemon also accepts API calls on a TCP port. Calls over TCP bypass recording.Remove the -H tcp://... flag or the TCP hosts entry.
unsupported-container-runtime (warning)hosts is set in /etc/docker/daemon.json, so shellz can't move the socket.Move the socket settings into the systemd unit and remove hosts from daemon.json.
unsupported-container-runtime (warning)Docker runs without a docker.service unit, for example from a snap.Use Docker's packages for your distribution.
unsupported-container-runtime (warning)Rootless Docker, Podman, or containerd tools (ctr, nerdctl, crictl) are in use.Add shellz inside the image for those containers.

Removing shellz from a Docker host

When shellz is removed, it deletes its drop-ins and gives the original socket back to Docker. If containers are running, shellz doesn't restart Docker. Instead it prints a note asking you to run this when convenient, so systemd manages the socket again:

sudo systemctl restart docker.socket docker.service