Skip to content

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 Per-trigger threshold / window / ban-duration for failed_login, auth_flood, rate_abuse.
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.

Login lockout attributes failures to the real client IP and feeds the failed_login trigger, producing a durable auto-ban.

Fail-safe & exemptions

  • Fail-safe: any config-load or DB error degrades to allow-all (today's behavior) — never lock-all.
  • Always exempt: loopback and RFC1918 private ranges, so kubectl exec / port-forward can never be locked out.
  • /healthz is 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_timeout is 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/ws must 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 — collapsing audit attribution and letting one client's failed logins auto-ban the shared edge IP for everyone.

security:
  trustedProxyCidrs: "10.0.0.0/8"   # your edge's egress CIDR(s), comma-separated

KubeManta only trusts X-Forwarded-For when the socket peer is inside one of these CIDRs, and then takes the rightmost untrusted address — so a direct caller cannot spoof its identity.


Out of scope

MFA and BYO ingress-class passthrough / Gateway API HTTPRoute / attach-to-existing-LB are deferred. For identity-provider login see Enterprise SSO.