mipo
Guide

Scanner: Linux systemd service

An evaluator-facing overview of what scanner install involves and why the deployment model is the way it is. This is not the operational procedure.

Operators: use the guide in your release bundle

The step-by-step installation procedure ships with every mipo release at docs/operations/scanner-install.md. That file is the authoritative reference for deploying scanners to real environments. Follow it instead of this page.

What scanner install involves

  1. Obtain a one-time provisioning token from the manager UI (Config > Scanners).
  2. Copy the scanner binary to the host and verify its SHA-256 against a trusted value.
  3. Create a dedicated non-privileged system user for the service to run as.
  4. Write an env file at /etc/mipo-scanner.env with the manager URL and the API key received at registration.
  5. Install the systemd unit with sandboxing directives and enable the service.
  6. Confirm the scanner appears in Health > Services > Scanners within about 60 seconds.

An auditable install.sh is available from the manager's Scanner Provisioning modal and performs the same steps in the same order, so evaluators can read exactly what the install does before committing.

Why the model is Linux-only and manual

Linux systemd as the target platform

The scanner targets enterprise Linux hosts running systemd. The binary is statically compiled Go with no runtime dependencies beyond the kernel, so it works on any Linux distribution. macOS and Windows are not supported scanner hosts — the scanner is a network-edge agent, not a workstation tool.

Why the binary is not auto-distributed

The binary is never pushed to hosts automatically. A compromised manager would otherwise be able to push a tampered binary to every scanner in the fleet. Operators obtain the binary from the release bundle, verify its SHA-256, and deploy it with existing tooling (Ansible, Chef, Puppet, or manual copy).

Verify the binary signature (required)

Releases built by a CI with a configured signing key ship an Ed25519 signature (mipo-scanner.sig) beside the binary. Verifying it protects you where the SHA-256 cannot: a compromised manager can serve a tampered binary with a matching hash, but it cannot forge a signature your independently-pinned public key accepts.

Signature verification is mandatory by default — controlled by MIPO_REQUIRE_SIG (default 1). When on, install.sh / upgrade.sh exit with an error if mipo-scanner.sig or /etc/mipo/scanner.pub is absent. Set MIPO_REQUIRE_SIG=0 only for dev, transition, or unsigned-build environments. To set up:

  1. Install scanner.pub from the source repository (an independent channel — not from the manager): sudo install -D -m 0644 scanner.pub /etc/mipo/scanner.pub. Verify against the published fingerprint in docs/security/scanner-signing.md.
  2. Place mipo-scanner.sig (from the release artifacts) next to the binary.
  3. Run install.sh — it verifies fail-closed against the pinned key. Manual check at any time: sh scripts/ops/verify-scanner.sh ./mipo-scanner ./mipo-scanner.sig /etc/mipo/scanner.pub.

Who signs, who verifies: the binary is signed once, at build time, by whoever builds it — for mipo-built releases that is the project's CI, where the private key lives; organizations that build from source sign with their own key (scripts/ops/sign-scanner.sh, no CI required) and become their own signing authority. Scanner hosts only ever need the public key: they verify, never sign. Nobody needs access to the builder's GitLab or build system to verify.

Installer-provenance boundary: the manager also serves install.sh, so a compromised manager could serve a poisoned installer. For maximum assurance, obtain install.sh and scanner.pub from the independent channel (the audited source repo / signed release tag), not from the manager, and verify scanner.pub against the published fingerprint.

Systemd sandboxing

The unit file applies hardening directives (NoNewPrivileges, ProtectSystem=strict, PrivateTmp, RestrictAddressFamilies=AF_INET AF_INET6, and others). The scanner's real runtime needs — outbound TCP connect and HTTP heartbeat — don't conflict with any of them, so there is no operational trade-off.