2025-10-18 12:32:29 +02:00
2025-10-15 17:41:05 +02:00
2025-10-18 12:32:29 +02:00
2025-10-15 17:41:05 +02:00
2025-10-15 17:41:05 +02:00
2025-10-15 17:41:05 +02:00
2025-10-15 17:41:05 +02:00
2025-10-15 18:02:44 +02:00
2025-10-15 17:41:05 +02:00
2025-10-18 12:32:29 +02:00

Remote Admin (Local Dev)

Lightweight Django + Channels app for live SSH command execution & streaming.

Stack

  • Python 3.12
  • Django 5.x
  • Channels 4 (InMemoryChannelLayer, single-process only)
  • asyncssh for SSH
  • HTMX + vanilla JS (no build toolchain)
  • Tailwind via CDN
  • SQLite

Current Feature Set

  • Hosts: CRUD for SSH target metadata
  • Tasks: Fully editable reusable command tasks (custom only; static registry removed)
  • Batch Scripts: Multi-step scripts with cd directory persistence and per-step progress
  • Web Console:
    • Connect/disconnect via WebSocket
    • Run adhoc command OR select saved task OR run batch
    • Real-time stdout/stderr streaming
    • Batch progress events (STEP X/Y)
    • Cancel running command/batch (graceful termination)
  • Validation & UX safeguards:
    • Empty commands prevented client-side
    • Batch normalization (strip Windows newlines, trim trailing blanks, ignore comments/blank lines)
    • Delete confirmations (hx-confirm)
  • Logs:
    • Status, exit code, duration, output tail
    • Run type (single / batch)
    • Failed step index (for batch failures / errors)
  • Structured error events JSON: {event:"error", type:"ssh|runtime", message:"..."}
  • Auth: Login required for all views
  • Manual: /manual/ in-app searchable documentation
  • Tests: CRUD + WebSocket execution (success, failure, batch success/failure, cancel) 13 passing

Setup

python -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt
cp .env.example .env  # edit values
python manage.py migrate
python manage.py createsuperuser

Run (single process ONLY):

# Development (auto-reload for HTTP only; WS fine for single process)
python manage.py runserver 127.0.0.1:8000

# Production-like single-process (Channels ASGI via daphne)
python -m daphne -b 127.0.0.1 -p 8000 project.config.asgi:application

Visit: http://127.0.0.1:8000/

Environment Variables (.env)

Var Purpose
DJANGO_SECRET_KEY Django secret key
DJANGO_DEBUG 0/1 toggle
ALLOWED_HOSTS Comma list
SSH_KEY_DIR Optional default key base path
KNOWN_HOSTS_PATH Path to known_hosts file
STRICT_HOST_KEY_CHECKING true/false enforce host key verification

SSH Setup (DE)

Ausführliche Anleitung zur Einrichtung einer funktionierenden SSH-Verbindung für die Web-Konsole.

1. Lokalen SSH-Key prüfen oder erzeugen

Prüfen ob bereits ein (modernen) Ed25519 Key existiert:

ls -l ~/.ssh/id_ed25519 ~/.ssh/id_ed25519.pub 2>/dev/null || echo 'kein ed25519 key'

Falls nicht vorhanden erstellen:

ssh-keygen -t ed25519 -f ~/.ssh/id_ed25519 -C "$(whoami)@$(hostname)" -N ''

Sichere Rechte setzen:

chmod 700 ~/.ssh
chmod 600 ~/.ssh/id_ed25519
chmod 644 ~/.ssh/id_ed25519.pub

2. Public Key auf Zielhost hinterlegen

Ersetzt deploy@server1 durch Benutzer & Host.

cat ~/.ssh/id_ed25519.pub | ssh deploy@server1 '\
  mkdir -p ~/.ssh && chmod 700 ~/.ssh && \
  cat >> ~/.ssh/authorized_keys && chmod 600 ~/.ssh/authorized_keys'

Test (direkt):

ssh deploy@server1 'echo OK'

3. Host Key erfassen (bei Strict Checking)

Nur nötig wenn STRICT_HOST_KEY_CHECKING=true (empfohlen Production).

ssh-keyscan -T 5 -t rsa,ecdsa,ed25519 server1 >> ~/.ssh/known_hosts

Bei Key-Änderung (Rotation / Reprovisioning) vorher entfernen:

ssh-keygen -R server1

4. Authentifizierungs-Methode wählen

Methode Wann nutzen Voraussetzungen
ssh_key Fester Schlüsselpfad Privater Key-Dateipfad existiert & Rechte korrekt
agent Temporäre Nutzung / Passphrase-Key ssh-agent läuft & ssh-add ausgeführt

Agent starten (falls nicht aktiv):

eval "$(ssh-agent -s)"
ssh-add ~/.ssh/id_ed25519

5. Host in der Web-App anlegen

Formularfelder:

  • Name: frei, z.B. prod-app-1
  • Hostname: DNS / IP, z.B. 10.10.10.15
  • Port: Standard 22
  • Username: z.B. deploy
  • Auth Method: SSH Key oder Agent
  • Key Path (nur bei SSH Key): Absoluter Pfad, z.B. /home/deploy/.ssh/id_ed25519
  • Strict Host Key Checking: Aktiv lassen außer im lokalen Test-Lab

6. Verbindung testen

In der Console:

  1. Host wählen
  2. Connect klicken (Status-Badge -> CONNECTED)
  3. Ad-hoc Command uname -a eingeben → Run
  4. Ausgabe sollte erscheinen; bei Fehlern → Fehlermeldung lesen (structured error event)

7. Typische Fehler & Lösungen

Meldung Ursache Lösung
Host key not trusted Host Key fehlt / weicht ab Key mit ssh-keyscan sammeln oder Strict deaktivieren (nur dev)
Permission denied Key / User passt nicht User/Key prüfen, Rechte kontrollieren, ggf. Agent laden
Connection lost Netzwerk/Firewall Direkten SSH Versuch testen, Port offen?
Unknown action Falsches WebSocket Payload Seite neu laden

8. Beispiel: Zwei Hosts (Prod & Staging)

Name: prod-api    Host: prod-api.company.internal  User: deploy  Auth: ssh_key  Key Path: /home/deploy/.ssh/id_ed25519
Name: staging-api Host: staging.company.internal   User: deploy  Auth: agent    Key Path: (leer, via ssh-agent)

9. Sicherheit

  • Keine Passwörter im Modell gespeichert
  • Private Keys verbleiben ausschließlich im Dateisystem
  • Strict Checking schützt vor Man-in-the-Middle

Password Auth ist aktuell deaktiviert nutze SSH Key oder Agent.

Console Usage Overview

  1. Select host, Connect.
  2. Choose EITHER a saved task, ad-hoc command, or batch.
  3. Press Run / Run Batch.
  4. Observe live output; stderr in red.
  5. For batch: status badge shows STEP X/Y.
  6. Cancel to terminate; status becomes Canceling then Canceled.
  7. On completion badge shows exit code.
  8. View history under Logs.

Tasks

Custom tasks only (registry removed). Fields:

  • name (unique key)
  • label (display)
  • command (shell executed exactly as typed)
  • description (optional)

Example:

Name: restart_app
Label: Restart App Service
Command: sudo systemctl restart app.service

Batch Scripts

One line per step. Rules:

  • Blank lines & lines starting with # ignored.
  • cd path lines set persistent working directory for subsequent steps.
  • On failure (non-zero exit) batch stops; failed step recorded.

Example:

# Deploy
cd /srv/app
./stop.sh
./deploy.sh
./start.sh

Structured Events (WebSocket)

Event Payload Fields Notes
connected session Initial handshake
started log_id, command Run started
chunk stream, data Output chunk (stdout/stderr)
progress current, total, step Batch step progress
canceling Cancel requested
completed status, exit_code Terminal status
error type, message type = ssh

Status -> final badge mapping:

  • ok -> green
  • failed -> red (shows failed step when batch)
  • error -> red (darker)
  • canceled -> yellow

Logs

Fields now include:

  • run_type (single|batch)
  • failed_step (nullable)
  • duration (derived) & timestamps
  • output_tail (last 32K)

Manual

Accessible at /manual/ or nav link “Manual” contains full user guide & examples (browser searchable).

Tests

pytest -q

Coverage includes model CRUD and WebSocket execution scenarios (success, fail, batch fail, cancel).

Troubleshooting (Quick Table)

Symptom Cause Fix
Host key not trusted Missing known_hosts entry ssh-keyscan >> known_hosts or disable (dev)
Permission denied Wrong user/key / permissions Fix key path, permissions, agent add
Invalid task Name not found Refresh tasks, re-create
Batch stops early Non-zero exit Inspect failed step in log, tail output
No output Command buffers Use unbuffered flags (e.g. python -u)

Security Notes

  • Keys referenced by path only; never stored or uploaded.
  • Use strict host key checking in production.
  • Single-process Channels (in-memory layer) not for multi-worker production.

Roadmap / Future Ideas

  • Redis channel layer for scaling
  • Parameterized tasks (templated inputs)
  • Role-based access control
  • Stream-to-disk full log archiving
  • ANSI color pass-through

Development Aids

Formatting / lint:

ruff check .
black .

Tailwind is loaded via CDN (see remotectl/base.html).


Description
No description provided
Readme 83 KiB
Languages
Python 57.2%
HTML 37.7%
Shell 5.1%