Nautilus

Lab setup

How to start the local Docker LAN, connect to the learner host, and set up WireGuard for direct IP access.

Quick start

All lab hosts are defined in lab/compose.yml. Start them from the repository root:

git clone https://github.com/janschupke/nautilus
cd lab
docker compose up -d

Then SSH in via the published port on your local machine:

ssh -p 2222 isopod@127.0.0.1

Credentials — user: isopod / password: isopod. Exercise fixtures are seeded under /home/isopod/labs inside the container.

Network architecture

Containers share a bridge network at 10.78.8.0/24. Each host has a fixed IP and a role:

IPHostPurpose
10.78.8.10clientSSH entry point; learner host with exercise fixtures and Apache logs
10.78.8.11lan-clientSimulates background user traffic (cron HTTP requests to webserver every 5 min)
10.78.8.20webserverPHP site, REST API, login, PostgreSQL integration
10.78.8.30dbPostgreSQL — database for webserver exercises
10.78.8.40exchange-ftpFTP service for file-transfer exercises
10.78.8.41exchange-smtpSMTP / Mailpit — email exercises; web UI at http://127.0.0.1:8025
10.78.8.50forensics-boxForensics analysis host for incident response and triage exercises
10.78.8.2vpnWireGuard gateway — routes your machine into the 10.78.8.0/24 LAN

Ports published to your host machine: 2222→22 (SSH), 8000→80 (client Apache), 8080→80 (webserver), 8025→8025 (Mailpit), and 51820/udp (WireGuard).

WireGuard — direct LAN access

On Linux the Docker bridge is directly reachable from the host, so you can ssh isopod@10.78.8.10 without WireGuard. On macOS and Windows Docker Desktop runs containers inside a hidden VM, so the 10.78.8.0/24 subnet is not accessible from your machine by default. The vpn container bridges that gap via WireGuard.

1 — Extract the peer config

After docker compose up -d, the WireGuard container generates a peer config for the isopod peer and writes it to a named volume. Print it:

docker compose exec vpn cat /config/peer_isopod/peer_isopod.conf

Or copy the file to your working directory:

docker compose cp vpn:/config/peer_isopod/peer_isopod.conf ./isopod.conf

The config contains a private key, the server endpoint, and the allowed IPs route (10.78.8.0/24). Keep it local — it grants LAN access.

2 — Import and activate

Open the WireGuard app (macOS: App Store; Windows: wireguard.com). Click Import tunnel from file and select isopod.conf, or use the CLI:

# macOS (after installing wireguard-tools via Homebrew)
wg-quick up ./isopod.conf

# Windows (admin PowerShell)
& "C:\Program Files\WireGuard\wireguard.exe" /installtunnelservice isopod.conf

Activate the tunnel in the app (toggle it on), or the wg-quick up command does it immediately. You only need to do this once per session — the tunnel stays up until you deactivate it.

3 — Verify connectivity

With the tunnel active, the full LAN is reachable by IP:

ping 10.78.8.10
ssh isopod@10.78.8.10        # no port flag needed
curl http://10.78.8.20/      # webserver
curl http://10.78.8.20/api/  # REST API

Re-generating the config

If you rebuild the lab with docker compose down -v the WireGuard volume is wiped and a new key pair is generated on the next up. You will need to re-extract and re-import the config.

Troubleshooting SSH on port 2222

ssh -p 2222 isopod@127.0.0.1 refusing the connection even when the containers appear to be running is one of the most common setup problems. Work through these checks in order.

1 — Confirm the container is healthy

docker compose ps

The client service must show running or Up, not restarting or exited. If it is not running, read the logs:

docker compose logs client

2 — Check what is actually listening on port 2222

The container may be running but the port binding may have failed silently (e.g. another process already held the port at compose-up time).

# macOS / Linux
lsof -i :2222

# Linux alternative
ss -tlnp | grep 2222

# Quick reachability test (exits 0 if something answers)
nc -zv 127.0.0.1 2222

If Docker (or com.docker) owns port 2222, the binding is correct — the problem is inside the container; continue to step 3. If a different process owns it, that process grabbed the port before Docker could — see step 4. If nothing appears, Docker never bound the port at all; re-check step 1 and look at docker compose logs client for startup errors.

3 — Wait for sshd to finish starting

docker compose up -d returns as soon as the container process starts, not after the init system inside has finished launching sshd. On a cold build this can take a few seconds. Run:

docker compose exec client ps aux | grep sshd

If sshd is absent, wait five seconds and try again. If it never appears, the Dockerfile entrypoint may have failed:

docker compose logs --tail 50 client

4 — Port conflict — another process grabbed 2222 first

Find and kill the conflicting process, then re-create the container so Docker can bind the port:

# Find the PID holding port 2222
lsof -ti :2222

# Kill it (replace <PID>)
kill <PID>

# Re-create the client container
docker compose up -d --force-recreate client

Common culprits: a previous container that was not fully stopped, a local SSH proxy, or another lab stack. Running docker compose down before up -d avoids this.

5 — Stale SSH host-key warning

If you previously connected and then rebuilt the lab volumes, the host key changes and SSH refuses with a REMOTE HOST IDENTIFICATION HAS CHANGED error. Clear the stale entry:

ssh-keygen -R "[127.0.0.1]:2222"

Then reconnect — SSH will prompt you to accept the new fingerprint.

6 — macOS / Windows: Docker Desktop not running

Docker Desktop must be running (macOS menu bar / Windows system tray) for port forwarding to work. If you started the containers in a previous session and Docker Desktop was restarted since then, the port mappings may have been dropped. Bring everything back up:

docker compose down
docker compose up -d

Published services

These ports are forwarded to 127.0.0.1 on your host while the lab is running:

  • ssh -p 2222 isopod@127.0.0.1 — learner shell
  • curl http://127.0.0.1:8000/ — client Apache
  • curl http://127.0.0.1:8080/ — webserver API
  • http://127.0.0.1:8025/ — Mailpit web UI
  • 51820/udp — WireGuard (see above)

Teardown

Stop and remove all containers without deleting data volumes:

docker compose down

Full reset — stop containers and wipe all volumes (database, WireGuard keys, exercise data):

docker compose down -v

After down -v the next up -d rebuilds from scratch: fresh database seed, new WireGuard key pair (re-import the peer config), and clean exercise fixtures.