Keine Beschreibung
  • Dockerfile 60.6%
  • Shell 39.4%
Datei suchen
2026-07-13 22:29:15 +02:00
skel update 2026-05-19 20:50:20 +02:00
.env.example KI update, migrated to wetty 2026-07-13 11:06:08 +02:00
.gitignore KI update, migrated to wetty 2026-07-13 11:06:08 +02:00
debian-ttyd.container KI update, migrated to wetty 2026-07-13 11:06:08 +02:00
debian-ttyd.service KI update, migrated to wetty 2026-07-13 11:06:08 +02:00
Dockerfile KI update, migrated to wetty 2026-07-13 11:06:08 +02:00
entrypoint.sh update container login message 2026-07-13 22:29:15 +02:00
Readme.md update container login message 2026-07-13 22:29:15 +02:00
sshd-wetty-localhost.conf KI update, migrated to wetty 2026-07-13 11:06:08 +02:00
wetty.container KI update, migrated to wetty 2026-07-13 11:06:08 +02:00
wetty.service KI update, migrated to wetty 2026-07-13 11:06:08 +02:00

Docker-Debian: Web Terminals

Two browser terminals for a small host, both rootless Podman managed by systemd user units:

  • Sandbox (debian-ttyd, port 7681): a throwaway Debian shell powered by ttyd. Every session runs in a fresh container — when the last client disconnects, the container is destroyed and a new one starts automatically.
  • Admin terminal (wetty, port 3000): a persistent WeTTY that logs into the real host over SSH.

Sandbox: fresh Debian per session

  1. ttyd runs with --exit-no-conn: it exits as soon as the last browser client disconnects.
  2. The container runs with --rm, so it is removed when ttyd exits.
  3. systemd (Restart=always) immediately starts a brand-new container.

Only data in the data volume (mounted at DATA_DIR, default /data) survives between sessions. A message on every login reminds users of that path.

Quick start (local test)

cp .env.example .env       # then edit at least PASSWORD
podman build -t debian-ttyd .
podman run --rm -p 7681:7681 --env-file=.env -v data:/data debian-ttyd

Open http://localhost:7681 and log in with USERNAME / PASSWORD. Without systemd the container is not recreated automatically — that part is handled by the unit files below.

Configuration (.env)

Variable Default Description
USERNAME user Login user created on container start
PASSWORD Passw0rd Password for that user — change it!
SUDO_OK false true grants passwordless sudo
AUTOLOGIN false true skips the login prompt (login -f)
DATA_DIR /data Persistent mount point (must match the unit's volume)
TZ Europe/Berlin Container timezone

Deployment with systemd (rootless)

Clone the repo to ~/Docker-Debian on the host (or adjust the paths in the unit files); for the sandbox also create .env and build the image as shown above. Each service ships in two variants — install one:

Quadlet (Podman >= 4.4, e.g. Debian 13) — *.container files. Quadlet generates and enables the service on daemon-reload:

mkdir -p ~/.config/containers/systemd
cp debian-ttyd.container ~/.config/containers/systemd/   # and/or wetty.container
systemctl --user daemon-reload
systemctl --user start debian-ttyd.service

Classic unit (Podman < 4.4, e.g. Debian 12) — *.service files:

mkdir -p ~/.config/systemd/user
cp debian-ttyd.service ~/.config/systemd/user/           # and/or wetty.service
systemctl --user daemon-reload
systemctl --user enable --now debian-ttyd.service

In both cases, keep the services running after logout:

loginctl enable-linger "$USER"

Useful commands

systemctl --user status debian-ttyd.service
journalctl _SYSTEMD_USER_UNIT=debian-ttyd.service -e   # works without persistent journal
podman volume rm data            # wipe the sandbox's persistent data

Troubleshooting

  • journalctl --user -u ... prints "No entries": the host has no persistent journal (common on Raspberry Pi OS). Use the _SYSTEMD_USER_UNIT= form above, or enable it once: sudo mkdir -p /var/log/journal, then restart systemd-journald (consider SystemMaxUse=50M in journald.conf on SD-card systems).
  • Sandbox dead after a few quick sessions (systemctl --user status shows start-limit-hit): the unit is missing StartLimitIntervalSec=0. Update it, then run daemon-reload, reset-failed debian-ttyd.service, start debian-ttyd.service.
  • Quadlet unit not found after daemon-reload: Quadlet needs Podman >= 4.4 (podman --version); on older hosts use the classic unit. Debug generation with /usr/lib/systemd/user-generators/podman-user-generator --user --dryrun.

WeTTY: admin access to the real host

Unlike the sandbox, WeTTY gives you a persistent terminal on the host itself, logging in as a real host account. The official wettyoss/wetty image connects out to the host's own sshd over a real SSH connection (--force-ssh is required: the image runs as root, and without it WeTTY would spawn a login shell inside the container instead of using SSH).

The container runs with --network host, so its ssh connection always originates from 127.0.0.1, regardless of Podman version or network backend. That lets sshd allow password auth only for this source address, while every real network connection stays key-based.

Setup, in order:

  1. Configure sshd — requires PasswordAuthentication no in the main sshd_config (Debian's default); the drop-in only adds the loopback exception:

    sudo cp sshd-wetty-localhost.conf /etc/ssh/sshd_config.d/
    sudo sshd -t              # validate config
    sudo systemctl reload ssh
    
  2. Set the login account: replace --ssh-user=CHANGE_ME in wetty.container / wetty.service with the host account to log into. WeTTY prompts for that account's Unix password in the browser on every connection — nothing is stored.

  3. Install the unit as described under Deployment with systemd.

Open http://<host>:3000 and log in with the account's Unix credentials.

Why direct ssh stays key-based: the drop-in allows password auth only for source address 127.0.0.1/::1. A real ssh user@host from anywhere else still hits PasswordAuthentication no and needs a key. Verify with sudo sshd -T | grep -i passwordauthentication (must print no).

Security notes

  • ttyd/WeTTY serve plain HTTP/WS — credentials cross the wire unencrypted. Put a TLS reverse proxy (Caddy, nginx, Traefik) in front of port 7681 (sandbox) / 3000 (WeTTY) and make sure it forwards WebSocket upgrades.
  • SUDO_OK=true gives the user root inside the sandbox container. Fine for a throwaway sandbox, but treat the container as untrusted in that case.
  • The WeTTY container gets full host network access (--network host) to make the loopback-source trick work. It cannot reach anything it couldn't already reach as a process on the host, but it is not network-isolated the way the sandbox container is.

Credits

Sandbox based on https://github.com/colinmurphy1/docker-ttyd