Skip to main content
Glama
DeveshB-1

hostproof-mcp

by DeveshB-1

hostproof

SOC 2 and ISO 27001 evidence from self-hosted Linux servers and Kubernetes clusters, pushed into Sprinto monitors through Sprinto's Upload Evidence API. It ships as a CLI and as an MCP server, so an agent can run the checks and file the evidence too.

Why this exists

Sprinto's cloud integrations cover AWS, GCP and Azure accounts. They can't see inside a bare-metal box in a colo, an on-prem RHEL fleet, or a k3s cluster behind a VPN. For those, Sprinto's docs point to the Upload Evidence API: you collect the proof yourself and POST it to a monitor.

hostproof is the "collect it yourself" part. It runs read-only checks where Sprinto can't reach, maps each one to the controls an auditor asks about, packs the raw output into a zip with a sha256 manifest, and uploads one bundle per monitor.

Sprinto already has an official MCP server. It works on records already inside a Sprinto workspace (controls, risks, policies, evidence). hostproof's MCP server sits on the host and produces new evidence, so the two work side by side.

Related MCP server: compliance-evidence-mcp

What it checks

$ hostproof checks
linux.ssh_root_login         SOC2 CC6.1       ISO A.8.2,A.8.5   SSH root login is disabled
linux.ssh_password_auth      SOC2 CC6.1       ISO A.8.5         SSH password authentication is disabled
linux.firewall               SOC2 CC6.6       ISO A.8.20        Host firewall is active
linux.disk_encryption        SOC2 CC6.1,CC6.7 ISO A.8.24        Data filesystems are encrypted at rest
linux.time_sync              SOC2 CC7.2       ISO A.8.17        Clock is synchronized to NTP
linux.audit_logging          SOC2 CC7.2       ISO A.8.15        Audit logging is enabled and persistent
linux.password_aging         SOC2 CC6.1       ISO A.5.17        Local password maximum age is enforced
linux.uid0_accounts          SOC2 CC6.2,CC6.3 ISO A.8.2         No extra UID 0 accounts
linux.auto_updates           SOC2 CC7.1       ISO A.8.8         Automatic security updates are enabled
k8s.privileged_containers    SOC2 CC6.8       ISO A.8.9         No privileged or host-namespace workload containers
k8s.run_as_non_root          SOC2 CC6.8       ISO A.8.9         Workload containers run as non-root
k8s.network_policies         SOC2 CC6.6       ISO A.8.22        Namespaces default-deny ingress traffic
k8s.cluster_admin_bindings   SOC2 CC6.3       ISO A.8.2         cluster-admin is not granted to people or app service accounts
k8s.image_pinning            SOC2 CC8.1       ISO A.8.32        Workload images are pinned to a tag or digest
k8s.resource_limits          SOC2 A1.1        ISO A.8.6         Workload containers set resource limits

ISO IDs are Annex A of ISO/IEC 27001:2022. The mappings are my suggestions; the auditor has the final say.

A few details that are easy to get wrong:

  • sshd uses the first value it reads, and Include expands in place. On most distros the drop-in directory is included at the top of sshd_config, so a drop-in beats the main file. hostproof follows the same order and reports when a value is the OpenSSH default rather than set explicitly.

  • No root needed. If sshd -T fails without root, hostproof parses the config files instead. Firewall and audit checks read service state through systemctl.

  • Pod specs contain secrets. Env values in pod JSON often hold passwords. Kubernetes evidence stores only the findings (pod and container names), never the raw spec, and Secrets are never listed. A test plants DB_PASSWORD=hunter2... in a fixture pod and checks it never reaches the bundle.

Quickstart

uv sync                        # or: pip install -e '.[mcp]'
uv run hostproof run           # Linux checks on this machine
uv run hostproof run --k8s --kubectl "kubectl --context prod"
uv run hostproof run --bundle evidence.zip
uv run hostproof verify evidence.zip

Pushing to Sprinto:

export SPRINTO_API_TOKEN=...   # your Sprinto Developer API key
uv run hostproof monitors      # pk, type and name of each monitor
cp examples/monitors.json monitors.json   # map monitor pks to check ids
uv run hostproof upload --monitors monitors.json --dry-run
uv run hostproof upload --monitors monitors.json --region in   # us | eu | in | au

MCP server (Claude Desktop, Claude Code, Cursor):

{
  "mcpServers": {
    "hostproof": {
      "command": "uv",
      "args": ["--directory", "/path/to/hostproof", "run", "hostproof-mcp"],
      "env": { "SPRINTO_API_TOKEN": "...", "SPRINTO_REGION": "us", "HOSTPROOF_KUBECTL": "kubectl" }
    }
  }
}

Tools: list_checks, run_compliance_checks, write_evidence_bundle, list_sprinto_monitors, upload_evidence. upload_evidence returns a preview and sends nothing unless it's called again with confirm=true, so the agent has to show a person what's about to land in the audit trail.

Sprinto API details used

From Sprinto's public docs:

  • POST {base}/api/v1/uploadEvidence, multipart/form-data, header api-token, fields monitor_pk and files (repeatable, up to 10). Success is 201 {"success": true, "error": ""}; a bad token is 401 with "api-token not found".

  • Monitor lookup: GraphQL query { monitors { nodes { pk name monitorType } } } on {base}/dev-api/graphql.

  • Rate limit: 10 requests per minute per key and per IP, and going over blocks access for 10 minutes. The client waits 6.5 s between calls, which is why the two-monitor upload below takes 6.6 s.

The evidence bundle is a .zip, one of the formats Sprinto lists for evidence files.

Demo run

These are real runs on my Arch Linux laptop (not a hardened server, so it fails several checks). Output is pasted as it came out, trimmed only where marked ....

$ hostproof run
WARN  linux.ssh_root_login     CC6.1       A.8.2,A.8.5   PermitRootLogin prohibit-password (OpenSSH default): root can still log in with a key
FAIL  linux.ssh_password_auth  CC6.1       A.8.5         PasswordAuthentication yes (OpenSSH default): password logins are accepted
PASS  linux.firewall           CC6.6       A.8.20        host firewall service active: firewalld
FAIL  linux.disk_encryption    CC6.1,CC6.7 A.8.24        unencrypted mounted filesystems: nvme1n1p2 (ext4) on /
PASS  linux.time_sync          CC7.2       A.8.17        system clock is NTP-synchronized
WARN  linux.audit_logging      CC7.2       A.8.15        auditd is not running; only persistent journald logs exist
FAIL  linux.password_aging     CC6.1       A.5.17        PASS_MAX_DAYS 99999 (policy: <= 90)
PASS  linux.uid0_accounts      CC6.2,CC6.3 A.8.2         root is the only UID 0 account
FAIL  linux.auto_updates       CC7.1       A.8.8         no dnf-automatic or unattended-upgrades unit is enabled

4 fail, 3 pass, 2 warn

Kubernetes checks against the cluster fixture in tests/fixtures/k8s (kubectl -o json output for 4 namespaces, 4 pods, 2 NetworkPolicies and 4 ClusterRoleBindings; see "What's mocked"):

$ hostproof run --k8s-fixture tests/fixtures/k8s --only k8s.privileged_containers k8s.run_as_non_root k8s.network_policies k8s.cluster_admin_bindings k8s.image_pinning k8s.resource_limits
FAIL  k8s.privileged_containers   CC6.8       A.8.9         2 privileged or host-namespace findings: web/debug-shell:shell, web/debug-shell (hostNetwork/hostPID)
FAIL  k8s.run_as_non_root         CC6.8       A.8.9         2 container(s) may run as root (no runAsNonRoot / non-zero runAsUser): web/frontend-5c7d8b-qwert:nginx, web/debug-shell:shell
FAIL  k8s.network_policies        CC6.6       A.8.22        namespaces without a default-deny ingress NetworkPolicy: default, web
FAIL  k8s.cluster_admin_bindings  CC6.3       A.8.2         non-system subjects bound to cluster-admin: User/alice@acme.example via alice-admin, ServiceAccount/deployer via ci-deployer
WARN  k8s.image_pinning           CC8.1       A.8.32        2 container(s) on :latest or untagged images: web/frontend-5c7d8b-qwert:nginx (nginx:latest), web/debug-shell:shell (busybox)
WARN  k8s.resource_limits         A1.1        A.8.6         1 container(s) without CPU/memory limits: web/frontend-5c7d8b-qwert:nginx

4 fail, 2 warn

The bundle, and the tamper check:

$ hostproof run --bundle out/evidence.zip    # same table as above
$ hostproof verify out/evidence.zip
manifest OK
$ unzip -l out/evidence.zip
     1273  summary.md
    18366  results.json
     3913  raw/linux.ssh_root_login.txt
     ...   (one raw/<check>.txt per check)
     1273  manifest.json

Upload over real HTTP to the local mock of Sprinto's endpoints (python -m tests.mock_sprinto 18765):

$ hostproof upload --monitors out/monitors.json --dry-run
[dry-run] POST /api/v1/uploadEvidence monitor_pk=7c9e6679-7425-40de-944b-e07fc1f90ae7 files=hostproof-demo-host-2026-10-25.zip (6711 bytes): linux.ssh_root_login=warn, linux.ssh_password_auth=fail
[dry-run] POST /api/v1/uploadEvidence monitor_pk=16fd2706-8baf-433b-82eb-8c7fada847da files=hostproof-demo-host-2026-10-25.zip (1876 bytes): linux.disk_encryption=fail

$ time SPRINTO_API_TOKEN=test-token hostproof upload --monitors out/monitors.json --base-url http://127.0.0.1:18765
uploaded hostproof-demo-host-2026-10-25.zip to monitor 7c9e6679-7425-40de-944b-e07fc1f90ae7: linux.ssh_root_login=warn, linux.ssh_password_auth=fail
uploaded hostproof-demo-host-2026-10-25.zip to monitor 16fd2706-8baf-433b-82eb-8c7fada847da: linux.disk_encryption=fail
real	0m6.657s

$ SPRINTO_API_TOKEN=bad hostproof upload --monitors out/monitors.json --base-url http://127.0.0.1:18765 --only linux.disk_encryption
error: upload to monitor 16fd2706-8baf-433b-82eb-8c7fada847da failed (HTTP 401): api-token not found

The MCP flow, driven by the MCP Python client over stdio (examples/mcp_demo.py):

tools: list_checks, run_compliance_checks, write_evidence_bundle, list_sprinto_monitors, upload_evidence
monitors: ['SSH hardening on servers', 'Encryption at rest']
preview (sent=False):
   linux.ssh_root_login=warn: PermitRootLogin prohibit-password (OpenSSH default): root can still log in with a key
   linux.ssh_password_auth=fail: PasswordAuthentication yes (OpenSSH default): password logins are accepted
confirmed: sent=True response={'success': True, 'error': ''}

Tests

$ uv run pytest -q
.........................                                                [100%]
25 passed in 3.85s

They cover hardened and weak hosts through a fixture executor, sshd Include ordering, the Kubernetes checks, secret redaction, bundle determinism and tamper detection, multipart bytes surviving the upload intact, 400/401 handling, the 10-file cap, rate-limit spacing, and a real stdio session with the MCP server.

What's mocked

  • Sprinto. I don't have a Sprinto workspace or API token, so uploads went to tests/mock_sprinto.py, a local server written from the public docs (header name, field names, the 10-file cap, response shapes). The real call path is the same code with --region instead of --base-url. Two things I couldn't confirm from the docs: whether the GraphQL endpoint takes the same api-token header as the upload endpoint, and whether /api/v1/uploadEvidence lives on the eu., in. and au. hosts too (the docs show it only on app.sprinto.com).

  • Kubernetes. I tried to start k3s in rootless podman for a live cluster, but this machine doesn't delegate the cpuset cgroup to user sessions, so it exited at startup. The K8s checks ran against hand-written kubectl JSON fixtures instead. Point --kubectl at a real cluster and the same code runs.

  • Linux. Not mocked. The demo above is this laptop.

Layout

hostproof/checks/linux.py   9 host checks
hostproof/checks/k8s.py     6 cluster checks
hostproof/runner.py         run checks, build and verify the zip bundle
hostproof/sprinto.py        Sprinto client (upload, monitor lookup, rate limit)
hostproof/cli.py            hostproof run | upload | monitors | verify | checks
hostproof/mcp_server.py     MCP tools
tests/mock_sprinto.py       local stand-in for Sprinto's evidence endpoints

MIT licensed.

Related MCP Connectors

Related MCP Servers

  • A
    license
    Not graded
    quality
    B
    maintenance
    Enables LLMs and AI agents to perform defensive security posture assessments, privilege escalation surface audits, and post-quantum cryptography readiness checks through read-only diagnostic tools.
    MIT
  • A
    license
    B
    quality
    C
    maintenance
    Enables MCP-compatible agents to perform read-only incident investigations by querying logs, building timelines, searching runbooks, validating evidence, and creating ticket drafts only with explicit approval.
    7
    MIT
  • A
    license
    B
    quality
    B
    maintenance
    Enables read-only inspection of local Windows and Linux systems for host, resource, network, service, software, identity, startup, certificate, SSH key metadata, and environment-variable name evidence through MCP tools. It normalizes platform outputs into stable schemas while avoiding arbitrary shell execution, credential reads, secret values, or remediation.
    23
    MIT