From f84501be11084f940954fe3f981d3324602ca1f9 Mon Sep 17 00:00:00 2001 From: sam Date: Fri, 21 Aug 2026 05:43:26 +0000 Subject: [PATCH] feat: clone-and-go remote agent install kit --- .env.agent.example | 20 ++++++++++ .gitignore | 1 + README.md | 78 +++++++++++++++++++++++++++++++++++++ docker-compose.agent.yml | 84 ++++++++++++++++++++++++++++++++++++++++ 4 files changed, 183 insertions(+) create mode 100644 .env.agent.example create mode 100644 .gitignore create mode 100644 README.md create mode 100644 docker-compose.agent.yml diff --git a/.env.agent.example b/.env.agent.example new file mode 100644 index 0000000..af3d98f --- /dev/null +++ b/.env.agent.example @@ -0,0 +1,20 @@ +# onlyscans remote-agent .env (copy to .env, fill the three AGENT_ values) +# Values come from the central /agents admin page (Sam provisions them). + +# Username of the agent record created at /agents (e.g. chch-1) +AGENT_IDENTITY= + +# The password shown ONCE at agent creation (or after a reset on /agents) +AGENT_PASSWORD= + +# The location NAME this agent scans from (e.g. chch-office) +AGENT_LOCATION= + +# NOT needed — the registry image is publicly pullable. Only fill this if +# Sam tells you the registry has been re-privatized (then also run: +# docker login gitea.noctools.nz -u sam -p ). +# REGISTRY_TOKEN= + +# Optional CVE matching via central API (leave empty unless Sam provides it) +CVE_MATCH_API= +CVE_MATCH_TOKEN= diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..4c49bd7 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +.env diff --git a/README.md b/README.md new file mode 100644 index 0000000..20168a0 --- /dev/null +++ b/README.md @@ -0,0 +1,78 @@ +# Only Scans — remote agent install kit + +This repo installs a **remote scan agent** for the Only Scans platform on a +docker host at your site. The agent is a location-labelled scanner: it logs +into the central instance with its own credentials, picks up scans queued +for your location, and reports results back. + +The agent is inert without centrally-provisioned credentials — this repo +contains no secrets. + +## What you get from Sam (central operator) + +1. An **agent username** (e.g. `chch-1`) +2. The **password** shown once at agent creation +3. Your **location name** (e.g. `chch-office`) + +## Prerequisites + +- Docker + the `docker compose` plugin +- Egress to `scan-api.noctools.nz:443` (the central API, TLS) +- Network reachability to the scan targets for your site — see + "Host-network variant" below if you need ARP-level LAN discovery +- ~512 MB RAM for the container + +## Install + +```bash +git clone https://gitea.noctools.nz/sam/onlyscans-agent-kit.git +cd onlyscans-agent-kit +cp .env.agent.example .env +# edit .env: AGENT_IDENTITY / AGENT_PASSWORD / AGENT_LOCATION +docker compose -f docker-compose.agent.yml up -d +docker logs -f onlyscans-agent +``` + +The worker image pulls anonymously — no `docker login` required. + +**Expected log lines on a healthy start:** +`authenticated` … `agent : AGENT_LOCATION "" validated`. +A rejected password or unknown location name is FATAL by design — +check the values in `.env` and `docker compose -f docker-compose.agent.yml up -d --force-recreate`. + +**Verify centrally:** Sam confirms the agent flips **online** on the +central `/agents` page within ~90 s, then queues a pinned test scan against +your location — `claimed_by` should show the agent username. + +## Upgrades + +The compose file pins an image tag. To upgrade: `git pull` this repo, +bump the tag + `AGENT_VERSION` in `docker-compose.agent.yml` if Sam hasn't +already, then: + +```bash +docker compose -f docker-compose.agent.yml pull +docker compose -f docker-compose.agent.yml up -d +``` + +The agent re-authenticates and resumes polling. + +## Options + +- **Host-network variant** (scan the site LAN directly, ARP-level + discovery, non-routed subnets): swap the service to `network_mode: host` + per the commented block in `docker-compose.agent.yml`. +- **Second agent on the same box:** copy the compose file, change the + project `name:` and `container_name` (and `WORKER_HEALTH_PORT` under + host networking) per the comments in the file. +- **CVE matching** (optional): fill `CVE_MATCH_API` + `CVE_MATCH_TOKEN` + in `.env` only if Sam provides them. Without it, scans complete + normally with the CVE stage skipped. + +## Troubleshooting + +| Symptom | Meaning | +|---|---| +| Container exits ~1 s after start | Bad `AGENT_PASSWORD` or unknown `AGENT_LOCATION` — fatal by design; fix `.env` and recreate | +| Agent shows **offline** centrally (`/agents`) | Container down, or no egress to `scan-api.noctools.nz:443` — check `docker logs onlyscans-agent` | +| Scans stay queued | Agent online but for a different location, or targets unreachable from the container's network — try the host-network variant | diff --git a/docker-compose.agent.yml b/docker-compose.agent.yml new file mode 100644 index 0000000..8f96d6e --- /dev/null +++ b/docker-compose.agent.yml @@ -0,0 +1,84 @@ +# onlyscans REMOTE AGENT install kit — run this ON THE REMOTE SITE's +# docker host. One agent = one location = one vantage. +# +# Prereqs (do these FIRST, on the central instance's web UI): +# 1. Admin → /agents → create the LOCATION for this site (e.g. "chch-office"). +# 2. /agents → New agent: username (e.g. "chch-1") + that location. The +# password is shown ONCE — put it in .env as AGENT_PASSWORD below. +# +# Then on this host: +# cp .env.agent.example .env (fill AGENT_IDENTITY / AGENT_PASSWORD / +# AGENT_LOCATION) → docker compose -f docker-compose.agent.yml up -d +# The worker image pulls anonymously from the public registry — no +# `docker login` needed. (Fallback if the registry is ever re-privatized: +# Sam issues a gitea read:package token → `docker login gitea.noctools.nz +# -u sam -p `.) +# Full walkthrough: README.md in this repo. +# (Running a SECOND agent on the same box: copy this file, change the project +# `name:` and `container_name` below — otherwise compose project + container +# collide with the first agent.) +# +# The agent talks to the central PocketBase over the public tunnel TLS +# (https://scan-api.noctools.nz) — credentials and results are encrypted in +# transit. It needs egress to: scan-api.noctools.nz:443, the scan targets, +# and (only if CVE_MATCH_API is set) that API host. +name: onlyscans-agent +services: + agent: + image: gitea.noctools.nz/sam/onlyscans-worker:20260821-2 + # Bump the tag to track the central worker version (see README.md). + container_name: onlyscans-agent + restart: unless-stopped + cap_add: + - NET_RAW # nmap/masscan raw-packet scanning + - NET_ADMIN + environment: + PB_URL: https://scan-api.noctools.nz + AGENT_IDENTITY: ${AGENT_IDENTITY:?set in .env — the username created on /agents} + AGENT_PASSWORD: ${AGENT_PASSWORD:?set in .env — the shown-once password} + AGENT_LOCATION: ${AGENT_LOCATION:?set in .env — the location NAME from /agents} + AGENT_HEARTBEAT_SECONDS: 30 + # Shown on /agents (agent_version) — bump together with the image tag + # when upgrading so the central UI can confirm the rollout. + AGENT_VERSION: 20260821-2 + # Optional: CVE matching via the central cve-match API. Leave EMPTY unless + # Sam has given you a CVE_MATCH_API URL + token (its public exposure is a + # manual central-side step). Without it, scans still complete — the CVE + # stage is skipped for agents with no local NVD DB. + CVE_MATCH_API: ${CVE_MATCH_API:-} + CVE_MATCH_TOKEN: ${CVE_MATCH_TOKEN:-} + POLL_INTERVAL_MS: 5000 + NMAP_VERSION_INTENSITY: 9 + FINGERPRINT_CONCURRENCY: 4 + healthcheck: + test: ["CMD", "wget", "--spider", "-q", "http://127.0.0.1:8081/healthz"] + interval: 15s + timeout: 5s + retries: 4 + deploy: + resources: + limits: + memory: 512m + +# ── Host-network variant ──────────────────────────────────────────────────── +# The default bridge network can only scan what it routes to. To scan the +# site's LAN directly (ARP-level discovery, non-routed subnets), swap the +# service to host networking. Each agent on a shared host stack needs its OWN +# health port — and the healthcheck `test` below MUST be bumped in tandem +# (it hardcodes the port; a stale probe would hit the FIRST agent's healthz +# and report false-healthy for the others): +# +# services: +# agent: +# image: gitea.noctools.nz/sam/onlyscans-worker:20260821-2 +# network_mode: host # container shares the host's stack +# environment: +# ... +# WORKER_HEALTH_PORT: "8082" # 8083, 8084… for further agents on this box +# healthcheck: +# test: ["CMD", "wget", "--spider", "-q", "http://127.0.0.1:8082/healthz"] +# # …keep matching WORKER_HEALTH_PORT on every copy +# # NO ports:, NO networks: — host mode ignores them +# +# Masscan at high rates can disrupt fragile networks; coordinate rate with +# the profile's masscan_rate on the central instance.