155 lines
12 KiB
Markdown
155 lines
12 KiB
Markdown
```yaml
|
|
██████╗ █████╗ ███╗ ██╗ █████╗ ██████╗ ██╗ ██╗
|
|
██╔════╝██╔══██╗████╗ ██║██╔══██╗██╔══██╗╚██╗ ██╔╝
|
|
██║ ███████║██╔██╗ ██║███████║██████╔╝ ╚████╔╝
|
|
██║ ██╔══██║██║╚██╗██║██╔══██║██╔══██╗ ╚██╔╝
|
|
╚██████╗██║ ██║██║ ╚████║██║ ██║██║ ██║ ██║
|
|
╚═════╝╚═╝ ╚═╝╚═╝ ╚═══╝╚═╝ ╚═╝╚═╝ ╚═╝ ╚═╝
|
|
```
|
|
|
|
[](https://github.com/CarterPerez-dev/Cybersecurity-Projects/tree/main/PROJECTS/beginner/canary-token-generator)
|
|
[](https://go.dev)
|
|
[](https://react.dev)
|
|
[](https://www.postgresql.org)
|
|
[](https://redis.io)
|
|
[](https://www.gnu.org/licenses/agpl-3.0)
|
|
[](https://www.docker.com)
|
|
[](https://engage.mitre.org/)
|
|
|
|
[](https://iglowinthedark.com/)
|
|
|
|
> Self-hosted honeytoken generator. Mints seven kinds of tripwire artifacts — invisible web bugs, booby-trapped PDF/DOCX files, fake `.env` and kubeconfig credentials, and a real MySQL wire-protocol decoy — then alerts you on Telegram or a webhook the moment an attacker touches one.
|
|
|
|
*This is a quick overview — security theory, architecture, and full walkthroughs are in the [learn modules](#learn).*
|
|
|
|
## What It Does
|
|
|
|
- Seven token types, each disguised as something an attacker would actually try to use: `webbug`, `slowredirect`, `pdf`, `docx`, `envfile`, `kubeconfig`, and a real `mysql` listener that speaks the MySQL v10 handshake
|
|
- Per-token Telegram or webhook alerts the instant a token fires (HMAC-signed for webhooks)
|
|
- Async notification worker pool with per-channel timeouts and dedup gating (15-minute Redis silence window per `{token, source_ip}` so a curious attacker reloading the page doesn't spam you)
|
|
- GeoIP enrichment via MaxMind GeoLite2 (country, region, city, ASN, ASN org) attached to every event
|
|
- Browser fingerprint capture for `slowredirect` tokens via a 3-second JS-collection interstitial before the redirect resolves
|
|
- Public manage URL (UUID-gated) so you can share a single link with a teammate to view triggers without exposing operator credentials
|
|
- Operator-only admin API (constant-time bearer comparison) for global stats, token listing, and force-disable
|
|
- Cloudflare Turnstile on token creation, dual-window rate limiting (per-minute + per-hour) keyed by browser fingerprint
|
|
- Optional Cloudflare Tunnel overlay — expose the service publicly without opening a port or maintaining a TLS cert
|
|
- Defense-grade observability: OpenTelemetry traces, slog structured logs, `/healthz` liveness, graceful shutdown with load-balancer drain delay
|
|
|
|
## Quick Start
|
|
|
|
```bash
|
|
just init # generates .env, .env.development, randomised ports, operator token
|
|
just dev-up # launches nginx + Vite HMR + Go (Air hot-reload) + Postgres + Redis + Jaeger
|
|
```
|
|
|
|
Open the URL printed by `just init` (typically `http://localhost:22784`). Mint a token, watch the manage page, then trigger it from another tab and refresh.
|
|
or the live demo at [iglowinthedark.com](https://iglowinthedark.com/)
|
|
|
|
> [!TIP]
|
|
> This project uses [`just`](https://github.com/casey/just) as a command runner. Type `just` to see every available recipe grouped by area.
|
|
>
|
|
> Install: `curl -sSf https://just.systems/install.sh | bash -s -- --to ~/.local/bin`
|
|
|
|
## Token Types
|
|
|
|
| Type | Artifact | Trigger Mechanism | Where You'd Plant It |
|
|
|------|----------|-------------------|----------------------|
|
|
| `webbug` | URL to a 1x1 JPEG | Any HTTP GET on the URL | HTML emails, internal wikis, "do-not-touch" docs |
|
|
| `slowredirect` | URL with a delayed redirect | Click-through; runs fingerprint JS before redirecting to a real destination | Phishing-bait links in honeyfile chat threads, fake admin panel URLs |
|
|
| `pdf` | Patched PDF | Acrobat opens, fires `/AA /O /URI` page-open action | `payroll-q3.pdf` on a shared drive, `vpn-creds.pdf` on a workstation |
|
|
| `docx` | Patched Word doc | Word/LibreOffice loads the footer, which contains a remote URI | `customer-list.docx`, `passwords.docx` in `Documents/` |
|
|
| `envfile` | Plain-text `.env` | Attacker `curl`s the fake `INTERNAL_METRICS_ENDPOINT` baked into the file | Repository roots, `~/.config/`, container `/app/` directories |
|
|
| `kubeconfig` | YAML kubeconfig | Attacker runs `kubectl --kubeconfig=stolen.yaml ...` and our server logs the bearer token | `~/.kube/config`, ops engineer laptops, CI runner home dirs |
|
|
| `mysql` | `mysql://...` connection string | Attacker connects with `mysql` CLI; our TCP listener replies with a real MySQL v10 handshake and an `Access denied` packet | `.env` files, `database.yml`, internal wiki snippets |
|
|
|
|
The `envfile` generator is the densest of the bunch. It picks recipes from `aws.go`, `db.go`, `github.go`, and `stripe.go`, shuffles the resulting sections, and buries a single canary line (`INTERNAL_METRICS_ENDPOINT=https://your-host/c/{tokenID}`) among plausible production config. The attacker harvesting the file gets a fistful of fake secrets to chase *and* trips the wire as soon as one of those secrets is touched.
|
|
|
|
## HTTP API
|
|
|
|
Token creation, manage view, and admin are mounted under `/api/`. Trigger routes live at the root so artifacts can carry short URLs.
|
|
|
|
| Method | Path | Auth | Purpose |
|
|
|--------|------|------|---------|
|
|
| `POST` | `/api/tokens` | Turnstile + rate limit | Mint a new token (`type`, `memo`, `alert_channel`, channel config, type-specific metadata) |
|
|
| `GET` | `/api/tokens/types` | Public | List available token types and their metadata schemas |
|
|
| `GET` | `/api/m/{manageId}` | Manage UUID | Token details + paginated event feed + dedup silence counter |
|
|
| `DELETE` | `/api/m/{manageId}` | Manage UUID | Soft-disable the token (events stop, history retained) |
|
|
| `GET` | `/api/admin/stats` | Bearer | Tokens count, events count, breakdowns by type and alert channel |
|
|
| `GET` | `/api/admin/tokens` | Bearer | All tokens (offset paginated) |
|
|
| `POST` | `/api/admin/tokens/{id}/disable` | Bearer | Force-disable any token |
|
|
| `GET` | `/healthz` | Public | Liveness + readiness probe (used by Docker healthchecks) |
|
|
| `GET` | `/c/{tokenID}` | Public | **Trigger route.** Records event, fires notification, returns artifact body (pixel, GIF, HTML interstitial, etc.) |
|
|
| `POST` | `/c/{tokenID}/fingerprint` | Public | Receives JSON fingerprint payload from the `slowredirect` interstitial |
|
|
| `*` | `/k/{tokenID}[/*]` | Public | Kubeconfig trigger — matches `kubectl`'s wildcard API paths |
|
|
|
|
The MySQL listener does not run on HTTP. It's a separate TCP server bound to a configurable address; an attacker using the connection string from the artifact will speak the MySQL wire protocol with our `protocol.go` handshake builder before getting denied.
|
|
|
|
## Stack
|
|
|
|
**Backend:** Go 1.25, chi router, pgx + sqlx, goose migrations, koanf config, slog, validator/v10, OpenTelemetry, pdfcpu, MaxMind GeoLite2, miniredis (tests), testcontainers (integration)
|
|
|
|
**Frontend:** React 19, TypeScript, Vite, TanStack Query, Zod, Axios, Biome, Stylelint
|
|
|
|
**Storage:** PostgreSQL 18 (`tokens`, `events` tables; INET + JSONB columns), Redis 7 (dedup gate + rate-limit token buckets)
|
|
|
|
**Infra:** Docker Compose (dev: nginx + Vite HMR + Air + Postgres + Redis + Jaeger; prod: nginx + Go binary + Postgres + Redis), optional `cloudflared` overlay
|
|
|
|
## Project Layout
|
|
|
|
```
|
|
canary-token-generator/
|
|
├── backend/
|
|
│ ├── cmd/canary/ main.go — wiring, signal handling, MySQL listener spawn, retention loop
|
|
│ └── internal/
|
|
│ ├── token/ Service, repository, handler, generator interface
|
|
│ │ └── generators/
|
|
│ │ ├── webbug/ Embedded JPEG pixel
|
|
│ │ ├── pixel/ Shared 1x1 GIF helper used by every "visited" response
|
|
│ │ ├── pdf/ Byte-exact PDF placeholder substitution (76-byte URL window)
|
|
│ │ ├── docx/ ZIP-aware footer rewrite
|
|
│ │ ├── envfile/ Recipe shuffler + canary line injection
|
|
│ │ │ └── recipes/ aws.go, db.go, github.go, stripe.go
|
|
│ │ ├── kubeconfig/ text/template renderer + wildcard /k/ handler
|
|
│ │ ├── mysql/ protocol.go (handshake + auth + err packets), server.go (TCP), handler.go
|
|
│ │ └── slowredirect/ HTML interstitial + fingerprint POST handler
|
|
│ ├── event/ Event entity, service (geo enrich → insert → dedup → notify), repository
|
|
│ ├── notify/ Worker pool, queue, status writer
|
|
│ │ ├── webhook/ HMAC-signed POSTs with exponential backoff
|
|
│ │ └── telegram/ Bot API client
|
|
│ ├── middleware/ request_id, logging, recovery, realip, fingerprint, ratelimit, turnstile, operator_bearer, headers
|
|
│ ├── geoip/ MaxMind MMDB lookup wrapper (nop when no DB present)
|
|
│ ├── turnstile/ Cloudflare Turnstile siteverify
|
|
│ ├── admin/ Stats, listing, force-disable
|
|
│ ├── core/ DB pool, Redis client, migrations, telemetry, errors, validation, response envelopes
|
|
│ ├── health/ /healthz handler with readiness/shutdown flags
|
|
│ └── server/ chi router shell with graceful shutdown + drain delay
|
|
├── frontend/
|
|
│ └── src/
|
|
│ ├── pages/landing/ Token creation form (type-aware metadata, Turnstile widget, artifact reveal)
|
|
│ └── pages/manage/ Token detail + event table (cursor paginated, GeoIP cells, dedup silence)
|
|
├── infra/
|
|
│ ├── nginx/ prod.nginx, dev.nginx (Vite proxy)
|
|
│ └── docker/ Dockerfiles for prod binary, Air hot-reload, Vite HMR
|
|
├── compose.yml Production stack
|
|
├── dev.compose.yml Dev stack with Jaeger
|
|
├── cloudflared.compose.yml Tunnel overlay
|
|
├── justfile Recipes grouped by frontend / backend / lint / compose / tunnel / dev / util
|
|
└── learn/ You are here
|
|
```
|
|
|
|
## Learn
|
|
|
|
This project includes step-by-step learning materials covering deception theory, token mechanics, system design, and a code walkthrough.
|
|
|
|
| Module | Topic |
|
|
|--------|-------|
|
|
| [00 - Overview](learn/00-OVERVIEW.md) | Prerequisites, quick start, project structure |
|
|
| [01 - Concepts](learn/01-CONCEPTS.md) | Honeytokens, deception defense, Thinkst Canary, MITRE Engage, real breaches |
|
|
| [02 - Architecture](learn/02-ARCHITECTURE.md) | System design, request lifecycle, schema, dedup gate, notification pipeline |
|
|
| [03 - Implementation](learn/03-IMPLEMENTATION.md) | Code walkthrough: generators, trigger handler, event service, MySQL protocol |
|
|
| [04 - Challenges](learn/04-CHALLENGES.md) | Extension ideas — new token types, alert channels, evasion-resistance |
|
|
|
|
## License
|
|
|
|
AGPL 3.0
|