Attestkeep docs

What runs in your cluster

You are being asked to give an admission webhook a say over every pod. This page is the full inventory of what that costs: the workloads, the permissions, the network paths — and the honest answer to what our own infrastructure could do to you if it were compromised.

Workloads

ObjectDefaultWhat it does
Deployment attestkeep2 replicas, anti-affinity preferred across nodes, PDB minAvailable: 1The operator: admission webhook (8443), console and API (8080), metrics (9090). Controller work is serialised through a PostgreSQL advisory lock, so replicas scale without a leader-election lease.
Deployment attestkeep-scanner1 replicaRuns Trivy scans and cosign verification off the admission path. Never receives admission traffic.
StatefulSet attestkeep-postgresql1 replica, 20 GiB, postgres:16.4-alpineThe bundled database. Renders only when you have not set externalDsn — point that at your own PostgreSQL and this object does not exist.

All product containers run as non-root (uid 65532), with a read-only root filesystem, all capabilities dropped, and RuntimeDefault seccomp. Requests default to 100m CPU / 256Mi per product workload. Everything lives in the release namespace; nothing is installed outside it except the two cluster-scoped objects below.

Cluster-scoped objects

RBAC — every permission and its reason

One ServiceAccount, one ClusterRole, one namespaced Role. This is the complete list; there is nothing else.

PermissionScopeWhy
attestkeep.com CRDs: full CRUD + statusClusterPolicies, reports and usage records are CRDs; the operator manages its own resources.
pods: get, list, watch; nodes, namespaces: get, listClusterThe reconciliation sweep has to see what is actually running to compare it with what was admitted.
secrets: get only — deliberately not listClusterRegistry credentials. The operator can read a pull secret whose name it learned from a pod specification; it cannot enumerate what a namespace holds. Credentials are used at scan time and never stored.
pods/eviction: createCluster, only if rbac.allowEnforce: true (default false)Runtime enforcement is armed with two keys: the policy chooses enforce mode and the chart grants the permission. A compromised operator on an audit-only install has nothing to evict with. Eviction, not delete, so PodDisruptionBudgets are honoured.
validatingwebhookconfigurations: updateCluster, restricted by resourceNames to its own configurationPublishing and rotating its own CA bundle. It cannot touch any other webhook in the cluster.
secrets: writeNamespace, restricted by resourceNames to attestkeep-webhook-tls and attestkeep-evidence-keyThe serving certificate and the evidence signing key. The operator creates and rotates exactly these two Secrets in its own namespace. There is no delete verb on Secrets anywhere in the role set.

Every outbound connection

This is the table for your firewall ticket. Nothing else leaves the cluster — there is no analytics, no crash reporting, no telemetry SDK. The one version string that leaves is the operator version on the licence check.

DestinationPortRequired?KnobIf blocked
lic.attestkeep.com443Yeslicense.serverUrlActivation is impossible; an activated install keeps running on its held certificate and degrades to Community when that certificate expires. See Licensing.
ghcr.io (Trivy vulnerability DB)443For scanningtrivy.dbRepository — mirror it into your own registryThe existing database keeps working and ages; attestkeep_vulnerability_db_age_seconds tells you by how much.
Your image registries443For scanningPull secrets resolved from pod specs; scan.insecureRegistries for HTTPScans fail for images there; admission behaviour follows your policy's cold-image setting.
ghcr.io/attestkeep/attestkeep-k8s (kubelet image pull)443At installimage.repository — mirror itPods cannot start.
Sigstore TUF + public Rekor (via cosign)443Noscan.cosignOffline: trueWith keyed signing and offline mode on, verification stays local. See Air-gapped.
Release feed443No — off by default, no default hostupdates.endpointAn installation that was never told where to look never looks. Report-only even when on; nothing is downloaded.
Your OTLP collector4317/4318No — off by defaultOTEL_EXPORTER_OTLP_ENDPOINTExporters are never started.
Your Slack/Teams/webhook URLs, your SMTP server443 / 587 / 465NoConfigured per channel in the consoleSends fail and are logged; admission is never blocked by a notification. Webhook posts refuse loopback and link-local targets at dial time, so a channel URL cannot be pointed at cloud metadata.
Your SSO identity provider443No — Enterprise, config-gatedsso.*That sign-in method fails; local accounts are unaffected.

The chart ships no NetworkPolicy: your perimeter is yours to define, and the table above is exactly what an egress policy for this namespace needs to allow.

If our licence server were compromised

The question a security team should ask about any vendor with a phone-home path. The honest answer, from how the client is built:

The evidence signing key is generated inside your cluster and never issued by the licence server; the server's certificate merely names your key so third parties can tie the two together. See the evidence trust model.