Access & Exposure¶
Two coupled hardening controls, both Free on every install: an in-product IP access layer (allowlist / denylist / auto-ban) with a sliding idle timeout, and a service-only exposure mode so you can front KubeManta with your own edge/WAF.
Defense in depth, not a WAF
App-level IP filtering has no L7 pattern detection, no DDoS absorption, and no geo-fencing. For internet-facing production, put a real edge/WAF in front — service-only mode (below) is designed to make that easy.
In-product IP access control¶
IP access control runs before authentication, so a denied IP is rejected with a flat 403 before any login work happens. It resolves the real client IP (honoring security.trustedProxyCidrs) and evaluates it against a policy that is stored encrypted at rest in-cluster.
Policy¶
| Field | Meaning |
|---|---|
allowlist |
If non-empty, only these IPs/CIDRs are allowed. Empty = allow all. |
denylist |
Always wins — a denylisted IP is blocked even if allowlisted. |
auto_ban |
Threshold / window / ban-duration for auth_flood, the one automatic IP ban. |
session_idle_timeout |
Sliding idle timeout in seconds (see below). |
Live bans¶
Auto-ban hits and manual bans are recorded durably, each with a reason, a hit count, and an expiry (a ban with no expiry is permanent). Bans survive pod restarts.
Only auth_flood bans an address automatically, and it responds to volumetric abuse rather than to wrong passwords. A policy saved by an older install may still list failed_login or rate_abuse; those are inert and are left in place rather than rejected, so an upgrade is not broken by a setting that no longer does anything. The admin UI renders only the live trigger.
Brute force is handled separately and does not ban an address: repeated failures are counted against the pair of account and IP, so one person failing to sign in cannot lock out everyone else behind the same office egress or VPN.
Fail-safe & exemptions¶
- Fail-safe: a config-load or database error degrades to allow-all rather than lock-all, so a mistake in this policy cannot lock you out of your own cluster. Enforcement against volumetric abuse belongs at your edge.
- Loopback is always exempt, so
kubectl execandport-forwardcan never be locked out. RFC1918 private ranges are exempt by default and can be switched off withexempt_private_rangesonce you are sure your edge sets the real client IP. /healthzis always skipped;/auth/*is not (so a banned IP can't even reach the login form).
Admin API¶
| Endpoint | Purpose |
|---|---|
GET/PUT /admin/access-control |
Read / write the policy. |
POST /admin/access-control/bans |
Add a manual ban. |
DELETE /admin/access-control/bans/{ip} |
Lift a ban. |
All admin-gated, and surfaced in Admin → Access Control.
Anti-lockout escape hatch¶
If you lock yourself out, a CLI escape hatch ships day one:
kubectl exec -n kubemanta-system deploy/kubemanta-agent -- python3 -m app.clear_bans
# add --reset-policy to also reset access_control back to allow-all
Sliding idle timeout¶
Sessions carry a last-activity marker, so the idle timeout is enforced on every request:
- A session that has been idle longer than
session_idle_timeoutis rejected (401); an active session is transparently extended (sliding). - The absolute 24-hour session lifetime is the hard ceiling — both checks apply, the stricter one wins.
Seed it with security.sessionIdleTimeout (default 1800); it's runtime-editable in Admin → Access Control (the stored value wins after first load).
sessionIdleTimeout: 0 disables the idle check
0 must render as SESSION_IDLE_TIMEOUT="0" (disabled) — the absolute 24-hour TTL still applies. A client-side inactivity timer in the UI is the UX layer; this server-side check is the backstop.
Service-only exposure (ingress.enabled=false)¶
Set ingress.enabled=false to render only the agent + UI Services — no Ingress, BackendConfig, or ManagedCertificate. Point your own ingress / load balancer / Gateway / WAF at them.
ingress:
enabled: false
service:
type: ClusterIP # or NodePort | LoadBalancer
annotations: # e.g. internal-only LB
networking.gke.io/load-balancer-type: "Internal"
ui:
service:
type: ClusterIP
annotations: {}
Routing when you front it yourself:
- The terminal WebSocket path
/api/agent/terminal/wsmust reach the agent Service directly. - All other traffic goes to the UI Service.
You MUST set security.trustedProxyCidrs
Service-only mode is only correct if security.trustedProxyCidrs is set to your edge's egress CIDR(s). Otherwise KubeManta attributes every request to the proxy IP, which collapses audit attribution and makes every allowlist and denylist entry mean the edge rather than the person behind it.
Set this to your edge and nothing wider — a range that covers your pod network would let any workload in the cluster choose the address recorded against it. Spoofed X-Forwarded-For from outside the trusted ranges is rejected.
Out of scope¶
BYO ingress-class passthrough, Gateway API HTTPRoute and attach-to-existing-LB are outside this page. For identity-provider login see Enterprise SSO; for multi-factor authentication see Users & Access.