Security Model
mcp-unifi is designed to run on a trusted home or homelab LAN, behind your existing network boundary. This page covers the threat model, the hardening that’s baked into the image, and how to verify the supply chain.
Threat model
Section titled “Threat model”- Trusted boundary: the MCP server is not authenticated. Anyone who can reach
http://<host>:3714/mcpcan call every tool. Put it on a trusted LAN, behind a reverse proxy with auth, or behind a Tailscale ACL. - Local-network only: the server talks to your UniFi gateway over the local API (X-API-Key header to
https://<gateway>/proxy/network/...). It does not call out to any Ubiquiti cloud endpoint, does not require a UI account, and does not require Site Manager / Cloud Console enrollment. - Self-signed gateway certs: most home gateways present a self-signed certificate.
UNIFI_VERIFY_SSLdefaults tofalsefor that reason. Set it totrueonce you’ve installed a real certificate.
Secret handling
Section titled “Secret handling”UNIFI_API_KEYand all per-controllerapi_keyvalues are wrapped in Pydantic’sSecretStr. Reading the cleartext requires.get_secret_value();repr()and structured logging never echo the raw key.- The startup
safe_repr()log line includes only anapi_key_set: true/falseboolean per controller, never the key itself. - The structured logger has a redactor that scrubs known sensitive keys (
api_key,passphrase,x_passphrase,password,secret) from any log record. WLAN passphrases are scrubbed[REDACTED]from every tool response, even in stub mode. - The audit log applies the same scrub to tool kwargs before writing.
Container hardening
Section titled “Container hardening”The published image (ghcr.io/pete-builds/mcp-unifi) ships with:
- Non-root: runs as UID 1000, no shell, no home directory.
- Read-only root filesystem: enforced via Docker / Helm.
/tmpistmpfsfor ephemeral writes (audit log, runtime caches). no-new-privileges: prevents setuid escalation paths.- Capabilities dropped: all Linux capabilities dropped at the container boundary.
- Hash-pinned deps: Python dependencies installed with
pip --require-hashesfrom a hash-lockedrequirements.lock. The base image is pinned by digest. - Trivy scan: CI fails the build on any HIGH or CRITICAL vulnerability finding. Current status: zero findings.
- Multi-arch:
linux/amd64andlinux/arm64from one release.
Supply-chain provenance
Section titled “Supply-chain provenance”cosign keyless signature
Section titled “cosign keyless signature”Every published image is signed via cosign keyless OIDC. The signing identity is the GitHub Actions workflow that built the image; verification confirms the image came from that workflow on this repo, not from someone with a stolen GHCR token.
cosign verify ghcr.io/pete-builds/mcp-unifi:latest \ --certificate-identity-regexp 'https://github.com/pete-builds/mcp-unifi' \ --certificate-oidc-issuer https://token.actions.githubusercontent.comA successful verification prints the signature payload (certificate subject, issuer, GitHub workflow ref). Failure means the tag does not have a valid signature from this repo.
Each release attaches a CycloneDX SBOM generated by Syft:
# Download from the GitHub release assetscurl -L -o sbom.cdx.json \ https://github.com/pete-builds/mcp-unifi/releases/download/v0.5.0-rc.2/sbom.cdx.jsonThe SBOM lists every Python package, OS package, and file digest baked into the image. Useful for vulnerability scanning, license auditing, or comparing across releases.
Provenance attestation
Section titled “Provenance attestation”The release workflow uses docker/build-push-action with provenance: true, attaching a build attestation to each image. Inspect it with:
docker buildx imagetools inspect \ ghcr.io/pete-builds/mcp-unifi:latest \ --format '{{ json .Provenance }}'What’s intentionally not in scope
Section titled “What’s intentionally not in scope”- No auth on the MCP endpoint. Out of scope. Use Tailscale, a reverse proxy with mTLS, or a NetworkPolicy.
- No remote-control of the audit log. The log is local. Ship it to your SIEM with whatever you already use (Fluent Bit, syslog forwarder, etc).
- No tenant isolation. One server instance = one tenant. Multi-tenant SaaS deployment is a post-v1.0 question.
For vulnerability reports, see SECURITY.md.