commit f657b9035704c7d97124b860c3c7c3d1f2420829 Author: Torjus HÃ¥kestad Date: Sat Feb 14 16:18:04 2026 +0100 chore: initial commit Add project scaffolding: CLAUDE.md, PLAN.md, README.md, flake.nix, and go.mod. Co-Authored-By: Claude Opus 4.6 diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..19d3fc0 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,19 @@ +# Oubliette + +SSH honeypot written in Go. See `PLAN.md` for full project plan and architecture. + +## Development + +- **Nix devshell:** Always use `nix develop -c` to run commands, e.g. `nix develop -c go test ./...`, `nix develop -c golangci-lint run`. +- **Tech stack:** Go, SQLite, x/crypto/ssh, Go templates + htmx for web UI. Single binary with embedded assets. + +## Guidelines + +- Write tests for all important functionality. +- Update `README.md` when new features are added. Keep entries concise for minor stuff. +- Keep code simple and focused. Refer to `PLAN.md` for design decisions. + +## Git workflow + +- Use **conventional commits** (e.g. `feat:`, `fix:`, `refactor:`, `docs:`, `test:`, `chore:`). +- Check out a **feature branch** before starting work on a new feature. diff --git a/PLAN.md b/PLAN.md new file mode 100644 index 0000000..b2b9efb --- /dev/null +++ b/PLAN.md @@ -0,0 +1,178 @@ +# Oubliette - SSH Honeypot + +A fun SSH honeypot that logs login attempts, presents fake shells to "successful" logins, and tries to detect when a real human is poking around. + +The name comes from the medieval dungeon concept - a place you throw people into and forget about them. + +## Tech Stack + +- **Language:** Go +- **SSH:** golang.org/x/crypto/ssh +- **Database:** SQLite +- **Web UI:** Go templates + htmx +- **Deployment:** Single binary with embedded assets + +## Core Concepts + +### Shell Profiles +Logins that "succeed" are routed to a fake shell. Shells are selected by weighted random from a registry. Each shell implements a common interface, making it easy to add new ones. + +```go +type Shell interface { + Name() string + Description() string + Handle(ctx context.Context, ch ssh.Channel) error +} +``` + +### Smart Storage +To avoid the database growing unbounded on a small VPS: +- **Deduplication:** Store unique (username, password, IP) combinations with a count + first_seen/last_seen timestamps instead of one row per attempt. +- **Retention policy:** Configurable auto-pruning of records older than N days. +- **Aggregation:** Optionally roll up old raw data into daily summary tables before pruning. + +### Human Detection +Score sessions based on signals that distinguish humans from bots: +- Keystroke timing (variable delays vs instant paste) +- Typos and backspace usage +- Tab completion and arrow key usage +- Adaptive behavior (commands that respond to previous output) +- Command diversity +- Session duration + +Sessions crossing a human-likelihood threshold get flagged for review and can trigger webhook notifications. + +### Login Realism +- Don't accept every attempt. Most attempts should fail. Bots commonly try thousands of combinations from a single IP (20k+ is not unusual), so the acceptance threshold should be high and configurable. +- **Credential memory:** When a credential is accepted, store it as a "valid" credential for a configurable TTL (e.g. 24-72 hours). If the same bot returns with the same username/password, it gets in immediately - making the credential appear legitimate and encouraging further interaction. +- Acceptance strategy is configurable: after N failed attempts from an IP, accept the next attempt (whatever the credentials are) and remember that combo. +- Optionally also support a static list of always-accepted credentials for testing. + +--- + +## Phase 1 - Foundation + +Goal: A working SSH honeypot that logs attempts, stores them in SQLite, and can present a basic fake shell. Minimal but functional. + +### 1.1 Project Setup +- Go module, directory structure, basic configuration (YAML or TOML) +- Configuration for: listen address, SSH host key path/auto-generation, database path, web UI listen address +- Nix flake with devshell and package output +- NixOS module for easy deployment (listen address, config path, state directory, etc.) + +### 1.2 SSH Server +- Listen for SSH connections using x/crypto/ssh +- Handle authentication callbacks +- Log all login attempts (username, password, source IP, timestamp) +- Configurable credential list that triggers "successful" login +- Basic login realism: reject first N attempts before accepting + +### 1.3 SQLite Storage +- Schema: login_attempts table with deduplication (username, password, ip, count, first_seen, last_seen) +- Schema: sessions table for successful logins (id, ip, username, shell_name, connected_at, disconnected_at, human_score) +- Schema: session_logs table for command logging (session_id, timestamp, input, output) +- Retention policy: background goroutine that prunes old records on a schedule +- **Database migrations:** Version-tracked migrations using embedded SQL files. Store current schema version in a `schema_version` table, apply pending migrations on startup. Keep it simple - no external migration tool, just sequential numbered `.sql` files embedded in the binary. + +### 1.4 Shell Interface & Registry +- Shell interface definition +- Registry with weighted random selection +- Shells receive a configuration struct with common settings from the config file: hostname, banner/MOTD, fake username, and any shell-specific options +- Basic bash-like shell: + - Prompt that looks like `user@hostname:~$` + - Handful of commands: `ls`, `cd`, `cat`, `pwd`, `whoami`, `uname`, `id`, `exit` + - Fake filesystem with a few interesting-looking files + - Log all input/output to the session_logs table + +### 1.5 Minimal Web UI +- Embedded static assets (Go embed) +- Dashboard: total attempts, attempts over time, unique IPs +- Tables: top usernames, top passwords, top source IPs +- List of active/recent sessions + +--- + +## Phase 2 - Detection & Notification + +Goal: Detect likely-human sessions and make the system smarter. + +### 2.1 Human Detection Scoring +- Keystroke timing analysis +- Track backspace, tab, arrow key usage +- Command diversity scoring +- Compute per-session human score, store in sessions table +- Flag sessions above configurable threshold + +### 2.2 Notifications +- Webhook support (generic HTTP POST, works with Slack/Discord/ntfy) +- Trigger on: human score threshold crossed, new session started, configurable +- Include session details in payload + +### 2.3 Session Replay +- Store keystroke-by-keystroke data with timing information +- Web UI: replay a session in a terminal-like viewer, watching commands play back in real-time +- Filter/sort sessions by human score + +### 2.4 Adaptive Shell Routing +- If early keystrokes suggest a bot, route to basic shell or disconnect +- If keystrokes suggest a human, route to a more interesting shell + +--- + +## Phase 3 - Fun Shells + +Goal: Add the entertaining shell implementations. + +### 3.1 Bash Shell Variations +- **Infinite sudo:** always asks for password, never works, logs every attempt +- **Slow decay:** shell gets progressively slower, commands take longer and longer +- **Haunted:** commands gradually return stranger output, files appear/disappear, `whoami` returns different users +- **Bread crumbs:** fake .bash_history, id_rsa files, database configs pointing to other honeypots + +### 3.2 Cisco IOS Shell +- Realistic `>` and `#` prompts +- Common commands: `show running-config`, `show interfaces`, `enable`, `configure terminal` +- Fake device info that looks like a real router + +### 3.3 Smart Fridge Shell +- Samsung FridgeOS boot banner +- Inventory management commands +- Temperature warnings +- "WARNING: milk expires in 2 days" +- Easter eggs + +### 3.4 Text Adventure +- Zork-style dungeon crawler +- "You are in a dimly lit server room." +- Navigation, items, puzzles +- The dungeon is the oubliette itself + +### 3.5 Other Shell Ideas (Future) +- **Banking TUI:** 80s-style green-on-black bank terminal +- **Nuclear launch terminal:** "ENTER LAUNCH AUTHORIZATION CODE" +- **ELIZA therapist:** every response is a therapy question +- **Pizza ordering terminal:** "Welcome to PizzaNet v2.3" +- **Haiku shell:** every response is a haiku + +--- + +## Phase 4 - Polish + +Goal: Make the web UI great and add operational niceties. + +### 4.1 Enhanced Web UI +- GeoIP lookups and world map visualization of attack sources +- Charts: attempts over time, hourly patterns, credential trends +- Session detail view with full command log +- Filtering and search + +### 4.2 Operational +- Prometheus metrics endpoint +- Structured logging (slog) +- Graceful shutdown +- Systemd unit file / deployment docs + +### 4.3 GeoIP +- Embed a lightweight GeoIP database or use an API +- Store country/city with each attempt +- Aggregate stats by country diff --git a/README.md b/README.md new file mode 100644 index 0000000..7058849 --- /dev/null +++ b/README.md @@ -0,0 +1,9 @@ +# Oubliette + +An SSH honeypot that logs login attempts, presents fake shells to "successful" logins, and tries to detect when a real human is poking around. + +Named after the medieval dungeon - a place you throw people into and forget about them. + +## Status + +Early development. See `PLAN.md` for the roadmap. diff --git a/flake.nix b/flake.nix new file mode 100644 index 0000000..1bf099f --- /dev/null +++ b/flake.nix @@ -0,0 +1,28 @@ +{ + description = "Oubliette - SSH Honeypot"; + + inputs = { + nixpkgs.url = "github:nixos/nixpkgs?ref=nixos-unstable"; + }; + + outputs = { self, nixpkgs }: + let + supportedSystems = [ "x86_64-linux" "aarch64-linux" ]; + forAllSystems = nixpkgs.lib.genAttrs supportedSystems; + in + { + devShells = forAllSystems (system: + let + pkgs = nixpkgs.legacyPackages.${system}; + in + { + default = pkgs.mkShell { + buildInputs = [ + pkgs.go + pkgs.govulncheck + pkgs.golangci-lint + ]; + }; + }); + }; +} diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..0fb32a3 --- /dev/null +++ b/go.mod @@ -0,0 +1,3 @@ +module git.t-juice.club/torjus/oubliette + +go 1.25.5