mipo

Scanners

Scanners are stateless Go binaries on customer networks that execute port scans. This page provisions new scanners, manages their lifecycle (enable/disable/delete), configures IP binding, mints one-time provisioning and mTLS enrollment tokens, and governs the fleet-wide mTLS policy. A new scanner gets its identity from install.sh, which runs `mipo-scanner provision` on the host as the mipo-scanner service user: the host generates its own key pair and API key, sends a certificate request plus the API-key hash with the one-time provisioning token, and persists the issued certificate, key, API key and runtime trust in /var/lib/mipo-scanner. No secret is written to /etc/mipo-scanner.env.

Inputs

NameDescriptionAllowed valuesDefault
Name Unique label identifying this scanner Letters, numbers, hyphens and underscores; 1–100 characters; unique across scanners AND resolvers (one identity table)
Description Optional free-text note about the scanner Any text, max 10,000 characters Empty
Binding Mode Restricts which source IP the scanner can connect from ip, subnet, asn, or none none
Rate Limit Maximum probes per second this scanner can fire, reflecting its hardware capacity Integer 0–100,000 0 (unlimited)

Fields & columns

NameDescription
NameEditable inline — the scanner label
DescriptionEditable inline — optional note
StatusPending (record created, host not yet provisioned), Active (provisioned and heartbeating — provisioning itself flips Pending to Active), Offline (no heartbeat for 2+ min), or Disabled (manually stopped)
BindingIP/subnet/ASN restriction for this scanner. Yellow badge shows IP mismatch count.
Created ByUser who provisioned this scanner
Rate LimitEditable inline — max probes per second, or "—" if unlimited
CreatedDate the scanner was provisioned
mTLSCertificate-based mutual TLS state for this scanner — Not Enrolled, Legacy Allowed (API-key only), Enrolling (an enrollment token is minted and unused), or Required (holds an mTLS identity; the legacy API-key path is refused for it). Shows the active certificate's expiry date when one exists.

How to

Provision a New Scanner

  1. Click "Add Scanner", fill in the name, and open the Scanner Provisioning modal for the new record.
  2. On the target host: stage ./mipo-scanner next to where you will run the installer and verify its SHA-256 and Ed25519 signature (the modal's binary steps).
  3. Download install.sh from /scanner/install.sh, compare its SHA-256 with the value the modal shows, then run: sudo MIPO_MANAGER_URL=<manager-url> MIPO_PROVISIONING_TOKEN=<token> MIPO_BINARY_PATH=./mipo-scanner bash install.sh — the token is read only from the environment (there is no flag for it) and install.sh pipes it to `mipo-scanner provision` over stdin. This sudo form shows the token in sudo's argument list for the instant sudo starts, on the host that will hold the identity.
  4. install.sh creates the mipo-scanner system user, installs the binary, and runs `mipo-scanner provision` as that user. The host generates its own key pair and API key, sends a certificate request plus the API-key hash with the one-time token to /scanner/register, verifies the issued certificate, and persists key, certificate, API key and runtime trust under /var/lib/mipo-scanner (0700). /etc/mipo-scanner.env receives only MIPO_MANAGER_URL.
  5. If the manager serves a self-signed or private-CA certificate, also pass MIPO_SERVER_CA_FILE=<ca.pem> — MIPO_INSECURE is refused for provisioning.
  6. Provisioning itself flips the record from Pending to Active; the first heartbeat keeps it Active and the mTLS column shows Required with the certificate expiry. A host that never heartbeats afterwards goes Offline.

Configure IP Binding

  1. Open the row actions menu and click "Configure Binding".
  2. Choose a binding mode (IP, Subnet, or ASN).
  3. Enter the allowed value and save.
  4. Connections from non-matching IPs will be flagged with a yellow mismatch badge.

Troubleshoot an Offline Scanner

  1. Check that the scanner service is running on the target host (systemctl status mipo-scanner).
  2. Verify outbound reachability from the scanner host to the manager on TCP 443 (bootstrap: provisioning, enrollment, the install script; and the runtime path for legacy API-key scanners while the fleet policy is migration) AND on TCP 8443 (the mandatory-mTLS runtime ingress, SCANNER_MTLS_PORT) once the cutover is published.
  3. Read the host logs: journalctl -u mipo-scanner -f — look for TLS handshake failures, a certificate past expiry, or 401/403 responses.
  4. Check the mTLS column: Not Enrolled or Legacy Allowed means the scanner still uses the legacy API-key path on :443 and will be refused once the fleet policy is required; Required means every request must present its certificate on :8443.
  5. If the scanner was recently re-provisioned (Regenerate Token + install.sh), confirm the host completed the new provisioning — the old identity stopped authenticating the moment the token was regenerated.

Rotate an API key without interrupting in-flight scans

  1. Open the row actions menu for the active scanner.
  2. Click "Rotate API Key".
  3. In the modal, click "Rotate Key" to confirm.
  4. Copy the new key from the success screen — it is shown only once.
  5. Legacy (API-key-only) scanner: update MIPO_API_KEY in /etc/mipo-scanner.env on the host and restart the service within 24 hours. The previous key keeps working until then.
  6. Scanner with an mTLS identity: its persisted API key lives inside the active generation under /var/lib/mipo-scanner and there is no environment override once an identity exists. Within the 24-hour grace, run Enroll mTLS again (Re-enroll) passing the NEW key as MIPO_API_KEY — enroll accepts the current key or the previous key still inside its grace and writes a fresh immutable generation holding the new key. Never modify files under /var/lib/mipo-scanner/gen-<N>/ by hand; generations are immutable by contract.
  7. After 24 hours the previous key stops authenticating; a host still presenting it starts logging auth failures and shows as Offline.

Enroll an existing scanner into mTLS

  1. Upgrade the host's binary first with upgrade.sh — a build from before the mTLS release has no `enroll` subcommand.
  2. Open the row actions menu for the scanner and click "Enroll mTLS" (or "Re-enroll mTLS" if it already has a required identity).
  3. Confirm in the modal — this mints a one-time enrollment token (valid 60 minutes, single-use). Copy the command and the token from their separate blocks.
  4. On the host, make sure the state directory exists and belongs to the service user (sudo install -d -o mipo-scanner -g mipo-scanner -m 0700 /var/lib/mipo-scanner), then run enroll AS that user, exactly like install.sh runs provision: sudo runuser -u mipo-scanner -- env -i MIPO_MANAGER_URL=<manager-url> MIPO_STATE_DIR=/var/lib/mipo-scanner MIPO_API_KEY=<the key from /etc/mipo-scanner.env> /usr/local/bin/mipo-scanner enroll
  5. Paste the token at the stdin prompt, or export it as MIPO_ENROLLMENT_TOKEN in that environment — never pass it on the command line.
  6. Make sure the unit file has StateDirectory=mipo-scanner and Environment=MIPO_STATE_DIR=/var/lib/mipo-scanner (an install from before mTLS lacks both — see the Linux service guide's "Enroll an existing installed scanner" section), then sudo systemctl restart mipo-scanner.
  7. A successful enroll sets the scanner to Required immediately (Enrolling only means a minted token is still unused). The host journal shows "Loaded enrolled identity from state (generation N)" — N is whatever number the host reserved, not necessarily 1.

Require mTLS fleet-wide

  1. Open the Fleet mTLS Policy panel above the scanners table and check the readiness list — it lists every blocker (unenrolled scanner, missing certificate, untrusted issuer, missing enforcement, missing CA/trust bundle, unresolvable Public URL) with a remediation hint.
  2. Enroll or re-enroll each listed scanner (or fix the CA/trust-bundle/Public URL issue) until the blocker list is empty. The "Require mTLS for all scanners" button stays disabled while any blocker remains.
  3. Click "Require mTLS for all scanners" and, in the modal, type the literal confirmation string require-mtls exactly (the API body is { policy: "required", confirm: "require-mtls" }).
  4. Confirm — this is step-up gated, so re-prove your identity if prompted.
  5. If the fleet state changed between the panel's preview and the submit (a scanner going offline, losing its cert, etc.), the modal shows the fresh blockers in place instead of closing — resolve them and retry.
  6. The flip is ONE request: the server live-probes, takes the lock, recomputes readiness, persists the zero-blocker snapshot and flips. Three things return 409 and persist nothing: readiness blockers (the response lists them), a revalidation change under the lock (the served identity changed — re-check and retry), or a CA rollover still in progress (ROLLOVER_IN_PROGRESS). Repeating the request once the policy is already required is a harmless 200 with changed: false.

Gotchas

  • Scanners and resolvers share one identity table with a fixed kind: this page lists kind=scanner only. Reverse-DNS workers live under Config › Resolvers and can never receive port-scan work.
  • Regenerating a provisioning token invalidates the previous one — the host must be re-provisioned with install.sh and the new token, which mints a fresh identity and API key.
  • After an install from the mTLS release, /etc/mipo-scanner.env contains only MIPO_MANAGER_URL. The private key, certificate, API key and runtime trust live in /var/lib/mipo-scanner (0700, owned by the service user); uninstall.sh wipes that directory.
  • Two ingress ports: :443 for bootstrap (provisioning, enrollment, the install script) and for legacy API-key runtime traffic while the policy is migration; :8443 (SCANNER_MTLS_PORT) for the mandatory-mTLS runtime once the cutover is published. Allow both from every scanner host.
  • Enroll must run as the mipo-scanner user (exactly as install.sh runs provision). Run as root, it leaves root-owned generation files the service cannot read — chown -R mipo-scanner:mipo-scanner /var/lib/mipo-scanner repairs that.
  • Disabling a scanner prevents it from receiving new scan assignments but does not kill in-progress scans.
  • A scanner marked Offline has simply missed heartbeats; it may still be running but unable to reach mipo.
  • Rate limits interact with subnet and port list limits — the most restrictive (lowest non-zero) value wins. When scanners share a subnet via a scanner group, the subnet rate is divided equally among them.
  • Scans use a 0.5 s TCP connect timeout by default, which is fast and accurate on a LAN. On lossy or high-latency links a lost packet can make an open port look closed (a false "closed" change in a delta scan): set MIPO_SCAN_TIMEOUT in /etc/mipo-scanner.env on that scanner host as a Go duration, e.g. MIPO_SCAN_TIMEOUT=2s (1.5–2 s for WAN, 3.5–5 s for intercontinental links), and restart the mipo-scanner service. An unparseable value such as "1.5 s" silently falls back to 0.5 s. See the Linux service install guide.
  • Rotate Key (24h grace) is different from Regenerate Token (immediate kill). Use Rotate Key for proactive rotation; use Regenerate Token only when you suspect a compromise and need the old key to stop working right now.
  • The new key from a rotation is shown exactly once. If you lose it before deploying it to the scanner, you must rotate again — there is no read-back path by design.
  • An enrollment token is valid for 60 minutes and single-use. If it expires or is lost before the scanner uses it, mint a fresh one — there is no read-back path.
  • Enrolling in mTLS does not retire the API key — the API key continues to authenticate the scanner alongside its certificate; it stays mandatory even after the scanner reaches the Required mTLS state.
  • Re-enrolling a scanner that already has a Required mTLS identity issues a brand-new certificate and revokes the current one as soon as the new one is issued — treat it like a compromise-response action, not routine maintenance.
  • Requiring mTLS fleet-wide is a one-way change — there is no button or API call to flip it back to migration mode. Recovery from a mistaken flip goes through certificate/CA operations (re-enrolling scanners, rotating the trust bundle), not a policy downgrade.
  • The required-policy flip persists a readiness snapshot as evidence (shown as "snapshot #N" in the Fleet mTLS Policy panel once flipped) — it records exactly which scanners were judged ready and why, for audit purposes.
  • A scanner disabled at the time of the flip is excluded from readiness entirely. If you re-enable it afterward, it has no legacy API-key-only runtime to fall back on — it must be (re-)enrolled with mTLS before it can rejoin the fleet.

API calls (25)

MethodPathDescription
GET /api/config/scanners List all scanners
POST /api/config/scanners Create a new scanner
PUT /api/config/scanners/:id Update scanner name or description
PATCH /api/config/scanners/:id Update scanner binding
DELETE /api/config/scanners/:id Delete a scanner — its certificate records go with it (cascade) and its API key stops authenticating immediately
GET /api/config/scanners/:id/binding Get scanner binding details
POST /api/config/scanners/:id/release-binding Release scanner IP binding
POST /api/config/scanners/:id/disable Disable a scanner
POST /api/config/scanners/:id/enable Enable a disabled scanner
POST /api/config/scanners/:id/regenerate-token Regenerate the provisioning token (kills the current API key immediately; the host must be re-provisioned with install.sh)
POST /api/config/scanners/:id/rotate-key Rotate the API key with a 24h grace window. The previous key continues to authenticate for 24 hours so in-flight scans do not fail. Returns the new key once. A legacy scanner takes it via /etc/mipo-scanner.env; a scanner with an mTLS identity takes it by re-enrolling with the new key. 409 if the scanner has no API key yet (use regenerate-token).
POST /api/config/scanners/:id/enrollment-token Mint a one-time mTLS enrollment token (60-minute TTL, single-use) for an already-provisioned scanner. Returns { enrollmentToken, expiresAt, expiresInMinutes } once — the token is stored only hashed and cannot be redisplayed.
GET /api/config/scanner-mtls/policy Get the fleet-wide mTLS enforcement policy singleton (migration allows legacy API-key-only scanners; required rejects them).
PUT /api/config/scanner-mtls/policy Flip the fleet-wide mTLS policy from migration to required in one request (live probe, lock, readiness recompute, snapshot, flip). One-way, step-up gated; body { policy: "required", confirm: "require-mtls" }. 409 with the blocker list, 409 on a revalidation change, 409 ROLLOVER_IN_PROGRESS, 422 POLICY_DOWNGRADE_FORBIDDEN; already required replays as 200 { changed: false }.
GET /api/config/scanner-mtls/readiness Advisory fleet-readiness preview for the mTLS cutover — lists every blocker (scanner-not-enrolled, required-scanner-without-active-cert, scanner-cert-issuer-untrusted, scanner-enforcement-missing, no-issuing-ca, no-active-trust-bundle, issuing-ca-not-trusted, public-url-unresolvable) before flipping the policy to required.
GET /api/config/scanners/binary-hash Get the current scanner binary hash
GET /api/config/scanners/install-script-preview Preview the scanner install script
GET /api/config/scanners/source-code Get scanner source code for review
GET /api/config/scanners/:id Get single scanner configuration
GET /api/config/scanners/filter-values Get available filter values for the scanners table
POST /scanner/register Manager-served (:443) provisioning endpoint used by `mipo-scanner provision`: body is a ProvisionRequest (one-time provisioningToken, a certificate request, the SHA-256 hash of the host-generated API key, hostname/osInfo). Returns the issued certificate chain plus runtime trust once and flips the record to Active. The raw API key never leaves the host.
POST /scanner/enroll Manager-served (:443): exchange a one-time enrollment token plus the scanner's current API key (X-API-Key header; the previous key is accepted while inside its 24h rotation grace) and a certificate request for an mTLS identity — used by `mipo-scanner enroll` on an already-installed scanner. Sets the scanner to Required.
POST /scanner/cert/renew Runtime (:8443, mTLS): the scanner requests a renewed leaf before expiry; it stages the new leaf and activates it after verification.
POST /scanner/cert/activate Runtime (:8443, mTLS): the scanner confirms activation of a renewed leaf; the previous leaf enters its retiring window.
GET /scanner/install.sh Ingest API — serves the static shell install script (no auth required to fetch; integrity via the X-Mipo-Sha256 header). Download it, then run sudo MIPO_MANAGER_URL=… MIPO_PROVISIONING_TOKEN=… MIPO_BINARY_PATH=./mipo-scanner bash install.sh; the script runs `mipo-scanner provision` as the mipo-scanner user, stores the identity in /var/lib/mipo-scanner, writes /etc/mipo-scanner.env with the manager URL only, and installs a hardened systemd unit.

Related

  • Resolvers — The same identity lifecycle, for the reverse-DNS role
  • Scanner Groups — Organize scanners into groups for distributed scanning
  • Subnets — Scanners are assigned to subnets to define which networks they scan
  • Recent Scans — View scan progress and results from scanner executions
  • Scanner Health — Real-time health and heartbeat status for registered scanners
  • Settings — Public URL setting affects scanner provisioning commands
  • Scanner ACL — Exports firewall rules based on registered scanner IPs; both scanner ingress ports (:443 and :8443) must be allowed from scanner hosts
  • Discovery — Discovery lists are assigned to scanners for execution
  • Scanner Binary — Build and deploy the scanner binary before provisioning
  • Scanner Heartbeat — Heartbeat and result submission protocol used by scanners
  • Scanner Job Dispatch — Pull-based job protocol by which scanners receive scan assignments
  • Resource Limits — MAX_SCANNERS cap applies to the total number of registered scanners
  • Rate Limit Hierarchy — Scanner rate limits participate in the three-level slowest-wins calculation
  • Job Errors — Active job error states per scanner — Active Errors column links here
  • Install: Linux service — Full host-install procedure, including enrolling an existing installed scanner into mTLS
  • Scanner CA Rollover — Rotating the issuing CA that signs scanner certificates without fleet downtime