diff --git a/.claude/agents/backend-dev.md b/.claude/agents/backend-dev.md index e716ac3d7..860292ef7 100644 --- a/.claude/agents/backend-dev.md +++ b/.claude/agents/backend-dev.md @@ -28,18 +28,23 @@ Your priority is writing code that is clean, tested, and secure by default. - **Step 1 (The Contract Test)**: Create `internal/api/handlers/your_handler_test.go` FIRST. Write a test that asserts the Handoff Contract (JSON structure). Run the test — it MUST fail. Output "Test Failed as Expected". - **Step 2 (The Interface)**: Define the structs in `internal/models` to fix compilation errors. - **Step 3 (The Logic)**: Implement the handler in `internal/api/handlers`. - - **Step 4 (Lint and Format)**: Run `lefthook run pre-commit` to ensure code quality. - - **Step 5 (The Green Light)**: Run `go test ./...`. **CRITICAL**: If it fails, fix the *Code*, NOT the *Test*. + - **Step 4 (The Green Light)**: Run ONLY the tests for the packages you changed, e.g. `go test ./internal//... -run ` while iterating, then `go test ./internal//...` once for each touched package. **CRITICAL**: If it fails, fix the *Code*, NOT the *Test*. -3. **Verification (Definition of Done)**: - - Run `go mod tidy` and `go fmt ./...`. - - Run `go test ./...` to ensure no regressions. +3. **Verification**: + - Run `go mod tidy` and `go fmt ./...`, then `go build ./...`. - **Conditional GORM Gate**: If task changes include `backend/internal/models/**` or GORM queries, run `./scripts/scan-gorm-security.sh --check` — zero CRITICAL/HIGH findings required. - - **Local Patch Coverage Preflight (MANDATORY)**: Run `bash scripts/local-patch-report.sh`. Verify `test-results/local-patch-report.md` and `test-results/local-patch-report.json` exist. - - **Coverage (MANDATORY)**: Run `scripts/go-test-coverage.sh`. Minimum 85% (`CHARON_MIN_COVERAGE`). Patch coverage must cover 100% of new/modified code. - - Run `lefthook run pre-commit` as final check. + - Write unit tests for all new/modified code (aim for 100% patch coverage), but do NOT measure it — see "Not your job". + + +Do NOT run these; they are expensive and handled elsewhere: +- `go test ./...` (full suite) — CI and `qa-security` run it. +- `scripts/go-test-coverage.sh` and `scripts/local-patch-report.sh` — `qa-security` runs them; if coverage falls short it sends the work back to you with the uncovered files. +- `lefthook run pre-commit` — it runs automatically when the main session commits. If the main session reports a hook failure, fix exactly what it reports. + + + - **`(security)` COMMIT SCOPE**: Use `feat(security): ` / `fix(security): ` only for genuinely security-relevant work — real vulnerability fixes or new protective mechanisms. Do NOT use it for ordinary bug fixes just to gain visibility; that dilutes the category's signal in the What's New changelog. Subject lines must stay vague by design: describe the category of issue and mitigation in general terms, never the specific vulnerability class, attack vector, or exact vulnerable code path (good: "harden input validation in the API layer"; bad: "fix SQL injection in host search filter"). @@ -51,5 +56,7 @@ Your priority is writing code that is clean, tested, and secure by default. - **TERSE OUTPUT**: Do not explain the code. Output ONLY code blocks or command results. - **NO CONVERSATION**: If the task is done, output "DONE". If you need info, ask the specific question. - **USE DIFFS**: When updating large files (>100 lines), use targeted edits rather than rewriting the whole file. -- **FOREGROUND EXECUTION ONLY** (see `CLAUDE.md`): Run `go test`, `go-test-coverage.sh`, `local-patch-report.sh`, `lefthook run pre-commit`, and every other command in the foreground and block until it completes. Never background a long-running command and end your turn to "check back later" — if it needs longer than one call's timeout, re-issue a blocking wait until you have a real result. +- **QUIET OUTPUT**: Never use `go test -v`. Pipe test/build output through `| tail -40` and never paste long logs into your reply or reasoning. +- **RETRY CAP**: After 2 failed attempts to fix the same failure, stop and report the failure and your findings to the main session instead of looping. +- **FOREGROUND EXECUTION ONLY** (see `CLAUDE.md`): Run `go test` and every other command in the foreground and block until it completes. Never background a long-running command and end your turn to "check back later" — if it needs longer than one call's timeout, re-issue a blocking wait until you have a real result. diff --git a/.claude/agents/frontend-dev.md b/.claude/agents/frontend-dev.md index 87aba7a99..7eb006f90 100644 --- a/.claude/agents/frontend-dev.md +++ b/.claude/agents/frontend-dev.md @@ -35,19 +35,24 @@ You are a SENIOR REACT/TYPESCRIPT ENGINEER with deep expertise in: - Add proper error boundaries and loading states. 3. **Testing**: - - **Run local patch preflight first**: `bash scripts/local-patch-report.sh`. Confirm artifacts exist. - - Use the report's uncovered file list to prioritize test additions. - Write unit tests with Vitest and Testing Library. - Cover edge cases and error states. - - Run tests with `npm test` in `frontend/` directory. + - Run ONLY the tests for files you touched: `cd frontend && npx vitest run `. Never run the full `npm test` suite. 4. **Quality Checks**: - - Run `lefthook run pre-commit` to ensure linting and formatting. - Run `cd frontend && npm run type-check` — fix all type errors. - - Run `scripts/frontend-test-coverage.sh` — minimum 85% coverage. - Ensure accessibility with proper ARIA attributes. + + +Do NOT run these; they are expensive and handled elsewhere: +- Full `npm test` — CI and `qa-security` run it. +- `scripts/frontend-test-coverage.sh` and `scripts/local-patch-report.sh` — `qa-security` runs them; if coverage falls short it sends the work back to you with the uncovered files. +- `lefthook run pre-commit` — it runs automatically when the main session commits. If the main session reports a hook failure, fix exactly what it reports. + + + - **`(security)` COMMIT SCOPE**: Use `feat(security): ` / `fix(security): ` only for genuinely security-relevant work — real vulnerability fixes or new protective mechanisms. Do NOT use it for ordinary bug fixes just to gain visibility; that dilutes the category's signal in the What's New changelog. Subject lines must stay vague by design: describe the category of issue and mitigation in general terms, never the specific vulnerability class, attack vector, or exact vulnerable code path (good: "harden input validation in the API layer"; bad: "fix SQL injection in host search filter"). @@ -57,5 +62,7 @@ You are a SENIOR REACT/TYPESCRIPT ENGINEER with deep expertise in: - **TANSTACK QUERY**: All API calls must use TanStack Query hooks. - **TERSE OUTPUT**: Do not explain code. Output diffs or file contents only. - **ACCESSIBILITY**: All interactive elements must be keyboard accessible. -- **FOREGROUND EXECUTION ONLY** (see `CLAUDE.md`): Run `npm test`, `frontend-test-coverage.sh`, `type-check`, `lefthook run pre-commit`, and every other command in the foreground and block until it completes. Never background a long-running command and end your turn to "check back later" — if it needs longer than one call's timeout, re-issue a blocking wait until you have a real result. +- **QUIET OUTPUT**: Pipe test/build output through `| tail -40`; never paste long logs into your reply or reasoning. +- **RETRY CAP**: After 2 failed attempts to fix the same failure, stop and report the failure and your findings to the main session instead of looping. +- **FOREGROUND EXECUTION ONLY** (see `CLAUDE.md`): Run `vitest`, `type-check`, and every other command in the foreground and block until it completes. Never background a long-running command and end your turn to "check back later" — if it needs longer than one call's timeout, re-issue a blocking wait until you have a real result. diff --git a/.claude/agents/planning.md b/.claude/agents/planning.md index f87499f2e..0271b5d67 100644 --- a/.claude/agents/planning.md +++ b/.claude/agents/planning.md @@ -1,7 +1,7 @@ --- name: Planning description: Principal Architect for technical planning and design decisions. Use when a new feature or significant change needs a detailed technical spec written to docs/plans/current_spec.md before implementation begins. Produces API contracts, DB schema, component designs, and commit slicing strategies. -model: sonnet +model: best --- You are a PRINCIPAL ARCHITECT responsible for technical planning and system design. diff --git a/.claude/agents/playwright-dev.md b/.claude/agents/playwright-dev.md index b2abfffd2..cba0d352c 100644 --- a/.claude/agents/playwright-dev.md +++ b/.claude/agents/playwright-dev.md @@ -29,7 +29,7 @@ You do not write production code, strictly tests. If code changes are needed, re ```bash .github/skills/scripts/skill-runner.sh docker-rebuild-e2e ``` - - The container exposes: port 8080 (app), port 2020 (emergency), port 2019 (Caddy admin). + - The container exposes: port 8080 (app), port 2020 (emergency). Port 2019 (Caddy admin) stays inside the container. - Verify container is healthy before proceeding. - **ALWAYS use the skill above to rebuild — never `docker build`/`docker run`/`docker compose up` ad hoc.** The skill is what keeps the test environment consistent across runs (correct compose file, ports, profiles). Standing up a parallel or replacement container via raw commands has repeatedly caused environment drift (e.g. a container missing port 80, needed for WAF/proxy testing) and wasted rebuild cycles. - **If the E2E environment itself needs to change** (ports, env vars, profiles, service definitions), edit `.docker/compose/docker-compose.playwright-local.yml` (and its CI counterpart, `.docker/compose/docker-compose.playwright-ci.yml`, if the change should also apply there) and rebuild via the skill. Do not spin up a separate one-off container to work around a compose file that needs updating — fix the compose file so the change is durable and every future rebuild picks it up. diff --git a/.claude/agents/qa-security.md b/.claude/agents/qa-security.md index 1f55cc5a9..d04272977 100644 --- a/.claude/agents/qa-security.md +++ b/.claude/agents/qa-security.md @@ -1,7 +1,7 @@ --- name: QA Security description: Quality Assurance and Security Engineer for testing and vulnerability assessment. Use after implementation is complete to run security scans (CodeQL, Trivy, GORM), validate test coverage, write E2E tests, and produce QA reports. Must run last in the agent pipeline. -model: haiku +model: best --- You are a QA AND SECURITY ENGINEER responsible for testing and vulnerability assessment. diff --git a/.claude/agents/supervisor.md b/.claude/agents/supervisor.md index db1b01cd6..35e7f6956 100644 --- a/.claude/agents/supervisor.md +++ b/.claude/agents/supervisor.md @@ -1,7 +1,7 @@ --- name: Supervisor description: Code Review Lead for quality assurance and PR review. Use when reviewing a plan in docs/plans/current_spec.md or reviewing an implementation for adherence to project standards, security (OWASP Top 10), test coverage, and best practices. Read-only — does not modify code. -model: sonnet +model: best --- You are a CODE REVIEW LEAD responsible for quality assurance and maintaining code standards. diff --git a/.docker/README.md b/.docker/README.md index 070828d8a..d94b2e245 100644 --- a/.docker/README.md +++ b/.docker/README.md @@ -94,7 +94,7 @@ Configure the application via `docker-compose.yml`: | `CHARON_ENV` | `production` | Set to `development` for verbose logging (`CPM_ENV` supported for backward compatibility). | | `CHARON_HTTP_PORT` | `8080` | Port for the Web UI (`CPM_HTTP_PORT` supported for backward compatibility). | | `CHARON_DB_PATH` | `/app/data/charon.db` | Path to the SQLite database (`CPM_DB_PATH` supported for backward compatibility). | -| `CHARON_CADDY_ADMIN_API` | `http://localhost:2019` | Internal URL for Caddy API (`CPM_CADDY_ADMIN_API` supported for backward compatibility). Must resolve to an internal allowlisted host on port `2019`. | +| `CHARON_CADDY_ADMIN_API` | `http://localhost:2019` | Optional. Leave it out, or set it to `http://localhost:2019`. Charon always uses the Caddy built into this container, whose admin interface listens on the loopback address only. Any other address (such as a service name or another server) stops Charon at startup with an error. If a restart policy is set, the container keeps restarting and the error repeats in `docker logs`. (`CPM_CADDY_ADMIN_API` supported for backward compatibility.) | | `CHARON_CADDY_CONFIG_ROOT` | `/config` | Path to Caddy autosave configuration directory. | | `CHARON_CADDY_LOG_DIR` | `/var/log/caddy` | Directory for Caddy access logs. | | `CHARON_CROWDSEC_LOG_DIR` | `/var/log/crowdsec` | Directory for CrowdSec logs. | @@ -164,8 +164,8 @@ docker compose -f .docker/compose/docker-compose.yml logs app **Debug**: ```bash -# View current Caddy config -curl http://localhost:2019/config/ | jq +# View current Caddy config (run inside the container) +docker exec charon wget -qO- http://127.0.0.1:2019/config/ | jq # Check Charon logs docker compose -f .docker/compose/docker-compose.yml logs app @@ -204,23 +204,22 @@ make docker-build ## Security Considerations -1. **Caddy admin API**: Keep port 2019 internal (not exposed in production compose) +1. **Caddy admin interface**: Port 2019 listens on the loopback address only, so it is reachable only from inside the container. Nothing needs to be exposed. 2. **Management UI**: Add authentication (Issue #7) before exposing to internet 3. **Certificates**: Caddy stores private keys in `caddy_data` - protect this volume 4. **Database**: SQLite file contains all config - backup regularly -## Integration with Existing Caddy +## Migrating from an Existing Caddy Setup -If you already have Caddy running, you can point Charon to it: +Charon bundles its own Caddy, so it does not connect to a Caddy running somewhere else. To move an existing Caddy setup into Charon, use the Caddyfile import described in the [import guide](../docs/import-guide.md). -```yaml -environment: - - CPM_CADDY_ADMIN_API=http://your-caddy-host:2019 -``` +If you upgraded and Charon now stops at startup with this message: -If using a non-localhost internal hostname, add it to `CHARON_SSRF_INTERNAL_HOST_ALLOWLIST`. +```text +CHARON_CADDY_ADMIN_API must point at this container (localhost); using an external proxy is not supported: set it to http://localhost:2019 or remove it +``` -**Warning**: Charon will replace Caddy's entire configuration. Backup first! +Open your `docker-compose.yml`, then either set `CHARON_CADDY_ADMIN_API` to `http://localhost:2019` or delete that line. A service name, a container hostname, or `127.1` in that setting is refused, so use `localhost` instead. ## Performance Tuning diff --git a/.docker/compose/.env.example b/.docker/compose/.env.example index 9956e3b0a..fbda761f8 100644 --- a/.docker/compose/.env.example +++ b/.docker/compose/.env.example @@ -1,8 +1,44 @@ -# Environment for docker-compose.local.yml -# Copy this file to .env in this directory (it is gitignored) and fill in the value. -# Compose reads .env from the compose file's directory automatically. +# Charon Environment Variables # Emergency "break-glass" token used to regain access when an ACL locks you out. # Keep it secret and rotate it periodically. Generate with: # openssl rand -hex 32 CHARON_EMERGENCY_TOKEN= + +# ============================================================================= +# Orthrus Agent (optional, used by the orthrus service in these compose files) +# ============================================================================= +# Plain-English guide: docs/features/orthrus.md + +# User the agent runs as. Leave at 65532 unless you have a reason to change it. +# Never set this to 0 (the administrator account). +PUID=65532 + +# Group the agent runs as. Set it to your Docker socket's group so the agent can read it. +# Find it with: stat -c '%g' /var/run/docker.sock +PGID= + +# Timezone for log timestamps (example: America/New_York) +TZ=UTC + +# Path to the Docker socket on this machine (mounted read-only into the agent) +DOCKER_SOCK=/var/run/docker.sock + +# Rootless Docker ONLY (leave commented out unless you run rootless Docker). +# If you use it, replace the PUID and PGID lines above with these, and set the +# number after /run/user/ to the output of `id -u`. Do NOT use these on normal +# (rootful) Docker: there, PUID=0 means real administrator. +# See docs/features/orthrus.md#rootless-docker +# PUID=0 +# PGID=0 +# ORTHRUS_ALLOW_ROOT=true +# DOCKER_SOCK=/run/user/1000/docker.sock + +# Websocket address of your Charon server, e.g. wss://charon.example.com/api/v1/ws/orthrus/connect +ORTHRUS_SERVER_URL= + +# Agent ID shown in Charon when you register the agent +ORTHRUS_AGENT_ID= + +# One-time auth key from Charon. Leave blank here and paste your own key. Never commit a real key. +ORTHRUS_AUTH_KEY= diff --git a/.docker/compose/docker-compose.dev.yml b/.docker/compose/docker-compose.dev.yml index 7489cd6e2..dbef4daa5 100644 --- a/.docker/compose/docker-compose.dev.yml +++ b/.docker/compose/docker-compose.dev.yml @@ -1,7 +1,7 @@ # Development override - use with: docker-compose -f docker-compose.yml -f docker-compose.dev.yml up services: - app: + charon: # Override for local testing: # CHARON_DEV_IMAGE=ghcr.io/wikid82/charon:dev image: wikid82/charon:dev @@ -13,7 +13,7 @@ services: - "443:443" - "443:443/udp" - "8080:8080" - - "2019:2019" # Caddy admin API (dev only) + # Caddy admin API stays inside the container: docker compose exec app wget -qO- http://127.0.0.1:2019/config/ environment: - CHARON_ENV=development - CPM_ENV=development @@ -23,12 +23,10 @@ services: - CHARON_ENCRYPTION_KEY=your-32-byte-base64-key-here - CHARON_DB_PATH=/app/data/charon.db - CHARON_FRONTEND_DIR=/app/frontend/dist - - CHARON_CADDY_ADMIN_API=http://localhost:2019 - CHARON_CADDY_CONFIG_DIR=/app/data/caddy # Security Services (Optional) # 🚨 DEPRECATED: Use GUI toggle in Security dashboard instead #- CPM_SECURITY_CROWDSEC_MODE=disabled # ⚠️ DEPRECATED - #- CPM_SECURITY_CROWDSEC_API_URL= # ⚠️ DEPRECATED #- CPM_SECURITY_WAF_MODE=disabled #- CPM_SECURITY_RATELIMIT_ENABLED=false #- CPM_SECURITY_ACL_ENABLED=false @@ -37,12 +35,33 @@ services: # to docker-compose.override.yml and set your host's docker GID. volumes: # Dev flow uses direct local discovery. For real deployments Orthrus (docs/features/orthrus.md) is recommended. - - /var/run/docker.sock:/var/run/docker.sock:ro # For local container discovery - crowdsec_data:/app/data/crowdsec # Mount your existing Caddyfile for automatic import (optional) # - ./my-existing-Caddyfile:/import/Caddyfile:ro # - ./sites:/import/sites:ro # If your Caddyfile imports other files + orthrus: + image: wikid82/orthrus:latest + restart: always + container_name: orthrus + environment: + # User and group the agent runs as. PGID must match your Docker socket's group (see docs/features/orthrus.md). + - PUID=${PUID} + - PGID=${PGID} + - TZ=${TZ} + # The websocket URL of your Charon instance. Use wss://charon.example.com when Charon is behind HTTPS (a reverse proxy with a certificate), + # or ws://: (e.g. ws://100.x.y.z:8080) when connecting to Charon directly over HTTP, such as on Tailscale or a Docker network. + # Charon itself does not serve TLS, so wss:// to its plain HTTP port fails with "first record does not look like a TLS handshake". + - ORTHRUS_SERVER_URL=${ORTHRUS_SERVER_URL} + # The agent ID shown when you registered this agent in Charon (Hecate -> Agent -> Add Agent) + - ORTHRUS_AGENT_ID=${ORTHRUS_AGENT_ID} + # The one-time auth key shown when you registered this agent + - ORTHRUS_AUTH_KEY=${ORTHRUS_AUTH_KEY} + volumes: + # Read-only access to the host Docker socket (DOCKER_SOCK in your .env). The agent is read-only by default; + # write access is an explicit per-agent opt-in in Charon (see docs/features/orthrus.md). + - ${DOCKER_SOCK}:/var/run/docker.sock:ro + volumes: crowdsec_data: driver: local diff --git a/.docker/compose/docker-compose.local.yml b/.docker/compose/docker-compose.local.yml index d85dd4d85..b8242ac92 100644 --- a/.docker/compose/docker-compose.local.yml +++ b/.docker/compose/docker-compose.local.yml @@ -20,7 +20,6 @@ services: - CHARON_HTTP_PORT=8080 - CHARON_DB_PATH=/app/data/charon.db - CHARON_FRONTEND_DIR=/app/frontend/dist - - CHARON_CADDY_ADMIN_API=http://localhost:2019 - CHARON_CADDY_CONFIG_DIR=/app/data/caddy - CHARON_CADDY_BINARY=caddy - CHARON_IMPORT_CADDYFILE=/import/Caddyfile @@ -46,7 +45,6 @@ services: - crowdsec_data:/app/data/crowdsec - plugins_data:/app/plugins # Read-write for development/hot-loading # Optional: direct local discovery. Orthrus (docs/features/orthrus.md) is the recommended, safer option. - - /var/run/docker.sock:/var/run/docker.sock:ro # For local container discovery - ./backend:/app/backend:ro # Mount source for debugging # Mount your existing Caddyfile for automatic import (optional) # - :/import/Caddyfile:ro @@ -58,6 +56,28 @@ services: retries: 3 start_period: 40s + orthrus: + image: wikid82/orthrus:latest + restart: always + container_name: orthrus + environment: + # User and group the agent runs as. PGID must match your Docker socket's group (see docs/features/orthrus.md). + - PUID=${PUID} + - PGID=${PGID} + - TZ=${TZ} + # The websocket URL of your Charon instance. Use wss://charon.example.com when Charon is behind HTTPS (a reverse proxy with a certificate), + # or ws://: (e.g. ws://100.x.y.z:8080) when connecting to Charon directly over HTTP, such as on Tailscale or a Docker network. + # Charon itself does not serve TLS, so wss:// to its plain HTTP port fails with "first record does not look like a TLS handshake". + - ORTHRUS_SERVER_URL=${ORTHRUS_SERVER_URL} + # The agent ID shown when you registered this agent in Charon (Hecate -> Agent -> Add Agent) + - ORTHRUS_AGENT_ID=${ORTHRUS_AGENT_ID} + # The one-time auth key shown when you registered this agent + - ORTHRUS_AUTH_KEY=${ORTHRUS_AUTH_KEY} + volumes: + # Read-only access to the host Docker socket (DOCKER_SOCK in your .env). The agent is read-only by default; + # write access is an explicit per-agent opt-in in Charon (see docs/features/orthrus.md). + - ${DOCKER_SOCK}:/var/run/docker.sock:ro + volumes: charon_data: driver: local diff --git a/.docker/compose/docker-compose.playwright-ci.yml b/.docker/compose/docker-compose.playwright-ci.yml index 2463bfaf3..316ce56f7 100644 --- a/.docker/compose/docker-compose.playwright-ci.yml +++ b/.docker/compose/docker-compose.playwright-ci.yml @@ -37,8 +37,7 @@ services: # No .env file is used in CI as it's gitignored and not available. ports: - "8080:8080" # Management UI (Charon) - - "127.0.0.1:2019:2019" # Caddy admin API (IPv4 loopback) - - "[::1]:2019:2019" # Caddy admin API (IPv6 loopback) + # Caddy admin API is not published: read it with docker exec charon-playwright wget -qO- http://127.0.0.1:2019/config/ - "2020:2020" # Emergency tier-2 API (all interfaces for E2E tests) - "80:80" # Caddy proxy (all interfaces for E2E tests) - "443:443" # Caddy proxy HTTPS (all interfaces for E2E tests) @@ -70,7 +69,6 @@ services: - CHARON_DB_PATH=/app/data/charon.db - CHARON_FRONTEND_DIR=/app/frontend/dist # Caddy settings - - CHARON_CADDY_ADMIN_API=http://localhost:2019 - CHARON_CADDY_CONFIG_DIR=/app/data/caddy - CHARON_CADDY_BINARY=caddy # ACME settings (staging for E2E tests) diff --git a/.docker/compose/docker-compose.playwright-local.yml b/.docker/compose/docker-compose.playwright-local.yml index 12cce781c..43441cba5 100644 --- a/.docker/compose/docker-compose.playwright-local.yml +++ b/.docker/compose/docker-compose.playwright-local.yml @@ -27,8 +27,7 @@ services: # that runs `npx playwright test` (see tests/utils/api-helpers.ts). - "8180:80" - "8080:8080" # Management UI (Charon) - E2E tests verify UI/UX here - - "127.0.0.1:2019:2019" # Caddy admin API (read-only status; keep loopback only) - - "[::1]:2019:2019" # Caddy admin API (IPv6 loopback) + # Caddy admin API is not published: read it with docker exec charon-e2e wget -qO- http://127.0.0.1:2019/config/ - "2020:2020" # Emergency tier-2 API (all interfaces for E2E tests) environment: - CHARON_ENV=e2e # E2E ACME/TLS policies without an ACME email; any non-production value also enables CHARON_CHANGELOG_VERSION @@ -47,7 +46,6 @@ services: - CHARON_HTTP_PORT=8080 - CHARON_DB_PATH=/app/data/charon.db - CHARON_FRONTEND_DIR=/app/frontend/dist - - CHARON_CADDY_ADMIN_API=http://localhost:2019 - CHARON_CADDY_CONFIG_DIR=/app/data/caddy - CHARON_CADDY_BINARY=caddy - CHARON_ACME_STAGING=true diff --git a/.docker/compose/docker-compose.remote.yml b/.docker/compose/docker-compose.remote.yml index dcef52f96..fd226f570 100644 --- a/.docker/compose/docker-compose.remote.yml +++ b/.docker/compose/docker-compose.remote.yml @@ -4,18 +4,25 @@ services: # a remote server, or the same server that runs Charon - to let Charon # discover containers there via the Orthrus agent. See # docs/guides/remote-docker-setup.md and docs/features/orthrus.md for setup steps. - orthrus-agent: + # Values come from the .env file next to this file (see .docker/compose/.env.example). + orthrus: image: wikid82/orthrus:latest - container_name: orthrus restart: always + container_name: orthrus environment: - # The websocket URL of your Charon instance, e.g. wss://charon.example.com/api/v1/ws/orthrus/connect - - ORTHRUS_SERVER_URL= - # The agent UUID shown when you registered this agent in Charon (Remote Agents -> Add Agent) - - ORTHRUS_AGENT_ID= + # User and group the agent runs as. PGID must match your Docker socket's group (see docs/features/orthrus.md). + - PUID=${PUID} + - PGID=${PGID} + - TZ=${TZ} + # The websocket URL of your Charon instance. Use wss://charon.example.com when Charon is behind HTTPS (a reverse proxy with a certificate), + # or ws://: (e.g. ws://100.x.y.z:8080) when connecting to Charon directly over HTTP, such as on Tailscale or a Docker network. + # Charon itself does not serve TLS, so wss:// to its plain HTTP port fails with "first record does not look like a TLS handshake". + - ORTHRUS_SERVER_URL=${ORTHRUS_SERVER_URL} + # The agent ID shown when you registered this agent in Charon (Hecate -> Agent -> Add Agent) + - ORTHRUS_AGENT_ID=${ORTHRUS_AGENT_ID} # The one-time auth key shown when you registered this agent - - ORTHRUS_AUTH_KEY= + - ORTHRUS_AUTH_KEY=${ORTHRUS_AUTH_KEY} volumes: - # Give the agent read-only access to the host Docker socket. The agent is read-only by default; + # Read-only access to the host Docker socket (DOCKER_SOCK in your .env). The agent is read-only by default; # write access is an explicit per-agent opt-in in Charon (see docs/features/orthrus.md). - - /var/run/docker.sock:/var/run/docker.sock:ro + - ${DOCKER_SOCK}:/var/run/docker.sock:ro diff --git a/.docker/compose/docker-compose.yml b/.docker/compose/docker-compose.yml index 618ad9671..33308ae21 100644 --- a/.docker/compose/docker-compose.yml +++ b/.docker/compose/docker-compose.yml @@ -36,12 +36,10 @@ services: - CHARON_HTTP_PORT=8080 - CHARON_DB_PATH=/app/data/charon.db - CHARON_FRONTEND_DIR=/app/frontend/dist - - CHARON_CADDY_ADMIN_API=http://localhost:2019 - CHARON_CADDY_CONFIG_DIR=/app/data/caddy - CHARON_CADDY_BINARY=caddy - CHARON_IMPORT_CADDYFILE=/import/Caddyfile - CHARON_IMPORT_DIR=/app/data/imports - - CHARON_SECURITY_CROWDSEC_API_URL=http://localhost:8085 extra_hosts: - "host.docker.internal:host-gateway" volumes: @@ -52,7 +50,7 @@ services: - plugins_data:/app/plugins:ro # Read-only in production for security # Optional: only needed for direct local container discovery. Orthrus (docs/features/orthrus.md) # is the recommended, more secure way to connect Docker; remove this line if you use it. - - /var/run/docker.sock:/var/run/docker.sock:ro # For local container discovery + # - ${DOCKER_SOCK}:/var/run/docker.sock:ro # For local container discovery # Mount your existing Caddyfile for automatic import (optional) # - ./my-existing-Caddyfile:/import/Caddyfile:ro # - ./sites:/import/sites:ro # If your Caddyfile imports other files @@ -63,6 +61,28 @@ services: retries: 3 start_period: 40s + orthrus: + image: wikid82/orthrus:latest + restart: always + container_name: orthrus + environment: + # User and group the agent runs as. PGID must match your Docker socket's group (see docs/features/orthrus.md). + - PUID=${PUID} + - PGID=${PGID} + - TZ=${TZ} + # The websocket URL of your Charon instance. Use wss://charon.example.com when Charon is behind HTTPS (a reverse proxy with a certificate), + # or ws://: (e.g. ws://100.x.y.z:8080) when connecting to Charon directly over HTTP, such as on Tailscale or a Docker network. + # Charon itself does not serve TLS, so wss:// to its plain HTTP port fails with "first record does not look like a TLS handshake". + - ORTHRUS_SERVER_URL=${ORTHRUS_SERVER_URL} + # The agent ID shown when you registered this agent in Charon (Hecate -> Agent -> Add Agent) + - ORTHRUS_AGENT_ID=${ORTHRUS_AGENT_ID} + # The one-time auth key shown when you registered this agent + - ORTHRUS_AUTH_KEY=${ORTHRUS_AUTH_KEY} + volumes: + # Read-only access to the host Docker socket (DOCKER_SOCK in your .env). The agent is read-only by default; + # write access is an explicit per-agent opt-in in Charon (see docs/features/orthrus.md). + - ${DOCKER_SOCK}:/var/run/docker.sock:ro + volumes: cpm_data: driver: local diff --git a/.docker/cscli-hub-mirror.sh b/.docker/cscli-hub-mirror.sh index 039177aec..b3f0cc746 100644 --- a/.docker/cscli-hub-mirror.sh +++ b/.docker/cscli-hub-mirror.sh @@ -11,6 +11,9 @@ # exit status, signal text, budget expiry) exists only here: the image runs cscli under busybox timeout, which reports # a trapped TERM as the command's own exit 1, and the Go side does not classify exit statuses. # +# Every cscli call names its config file with -c. The live file is CHARON_CSCLI_CONFIG, which the entrypoint sets +# (never an operator setting); without it the file is cscli's default, /etc/crowdsec/config.yaml. +# # Internal state, set by this helper and not meant for operators: # CHARON_CSCLI_MIRROR_CONFIG path of the derived config while it exists # CHARON_CSCLI_MIRROR_FIRST "1" once a mirror retry answered: later cscli_hub calls go mirror-first @@ -27,6 +30,19 @@ if ! command -v run_interruptible >/dev/null 2>&1; then } fi +# _hub_live_config +# Prints the live cscli config file. The entrypoint exports CHARON_CSCLI_CONFIG once it has initialised the config +# directory; without it (a script run by hand in the image) cscli's own default is named, so the flag is always there. +_hub_live_config() { + printf '%s' "${CHARON_CSCLI_CONFIG:-/etc/crowdsec/config.yaml}" +} + +# cscli_cfg +# Runs one cscli command on the live config, with no timeout and no mirror retry (quick reads and registration). +cscli_cfg() { + cscli -c "$(_hub_live_config)" "$@" +} + # hub_mirror_template_valid