ProxyAuthK8S
CRD reference

ProxyKubeApi

ProxyKubeApi spec.

  • API version: weebo.si.rs/v1
  • Scope: Namespaced
  • Short names: none
  • Guide: Exposer un cluster

ProxyKubeApi spec.

Example

apiVersion: weebo.si.rs/v1
kind: ProxyKubeApi
metadata:
  name: my-proxykubeapi
  namespace: team-a
spec:
  cert:
    SystemRoots: true
  service:
    KubernetesService:
      name: kubernetes
      namespace: default
      port: 443

Spec fields

FieldTypeRequiredDefaultDescription
auth_configobjectnoMain configuration for authentication
certobjectyesCertificate for the Kubernetes API
client_certobjectnoClient certificate presented to the Kubernetes API for mutual TLS Leave unset when the cluster does not require client certificates
dashboard_groupstringnoIf the proxy exposition is accessible via the dashboard the oidc group that allow access to the dashboard, should be unique Default: to the resource namespace + resource name
enabledbooleannotrueEnable or disable the proxy
expose_via_dashboardbooleannofalseIf the proxy exposition should be accessible via the Dashboard Default: false
proxy_groupstringnoThe oidc group required to proxy requests to this cluster Set it to decouple proxy access from dashboard access Default: the dashboard group when expose_via_dashboard is true, otherwise no group is required
security_configobjectnoSecurity configuration
serviceobjectyesService to expose the proxy
upstream_authobjectnoHow the proxy presents the caller's identity to the apiserver (RFC 0014). Absent: Passthrough, the caller's token is relayed.
virtual_apisobject[]no[]Virtual APIs to expose on top of this cluster A virtual API is an API the cluster does not serve, that the proxy synthesises by translating requests and responses

Rules

  • dashboard_group must not be empty when it is set — !has(self.dashboard_group) || self.dashboard_group != ''
  • proxy_group must not be empty when it is set — !has(self.proxy_group) || self.proxy_group != ''
  • service Agent needs the remote apiserver's CA in cert (ConfigMap, Secret or Cert), never SystemRoots — !has(self.service.Agent) || has(self.cert.ConfigMap) || has(self.cert.Secret) || has(self.cert.Cert)
  • client_cert is refused in FrontProxy and Issuer: FrontProxy reads its certificate from the proxy's namespace, and in Issuer it would win over the JWT — !has(self.upstream_auth) || !has(self.upstream_auth.mode) || self.upstream_auth.mode == 'Passthrough' || self.upstream_auth.mode == 'TokenExchange' || !has(self.client_cert)
  • FrontProxy, Issuer and TokenExchange need a validated identity: set auth_config without disable_validation — !has(self.upstream_auth) || !has(self.upstream_auth.mode) || self.upstream_auth.mode == 'Passthrough' || (has(self.auth_config) && !(has(self.auth_config.disable_validation) && self.auth_config.disable_validation))
  • FrontProxy and Issuer cannot validate against Kubernetes: the SelfSubjectReview needs the apiserver to know the IdP — !has(self.upstream_auth) || !has(self.upstream_auth.mode) || self.upstream_auth.mode == 'Passthrough' || self.upstream_auth.mode == 'TokenExchange' || !has(self.auth_config) || (has(self.auth_config.validate_against) && self.auth_config.validate_against != 'Kubernetes')
  • FrontProxy and Issuer require oidc_provider.claim_mappings: preferred_username must not become a trusted identity — !has(self.upstream_auth) || !has(self.upstream_auth.mode) || self.upstream_auth.mode == 'Passthrough' || self.upstream_auth.mode == 'TokenExchange' || !has(self.auth_config) || !has(self.auth_config.validate_against) || self.auth_config.validate_against != 'OidcProvider' || has(self.auth_config.oidc_provider.claim_mappings)
  • TokenExchange needs auth_config.oidc_provider.enabled: it is the client that exchanges — !has(self.upstream_auth) || !has(self.upstream_auth.mode) || self.upstream_auth.mode != 'TokenExchange' || (has(self.auth_config) && has(self.auth_config.oidc_provider) && self.auth_config.oidc_provider.enabled)

spec.auth_config

FieldTypeRequiredDefaultDescription
disable_validationbooleannofalseDisable validation of the token against the configured JWT authenticators, OIDC provider or Kubernetes API If the AuthenticationConfiguration is not provided, does not validate the token against any of the configured JWT authenticators, OIDC provider or Kubernetes API Default : false
jwtobject[]no[]Bounded so the quadratic uniqueness rule above stays inside the apiserver's CEL cost budget — the estimator multiplies this bound by the bound on issuer.url, and an unbounded pair is rejected outright. Still far above any real deployment, which trusts a handful of issuers.

Constraints: maxItems: 8
oidc_providerobjectyesAn enabled provider that cannot be contacted is a cluster that never authenticates anyone, so the apiserver refuses the CR outright.
validate_againststringno"Kubernetes"Validate against the configured JWT authenticators, OIDC provider or Kubernetes API Default : Kubernetes — the fail-closed choice, valid for every cluster. Selecting OidcProvider or JwtAuthenticators is always explicit, so a cluster never silently changes how it authenticates because a field was added elsewhere in the spec.

Values: "OidcProvider", "Kubernetes", "JwtAuthenticators"

Rules

  • validate_against is set to OidcProvider but the OIDC provider is not enabled — self.validate_against != 'OidcProvider' || self.oidc_provider.enabled
  • validate_against is set to JwtAuthenticators but no jwt authenticator is configured — self.validate_against != 'JwtAuthenticators' || size(self.jwt) > 0
  • two jwt authenticators are configured for the same issuer; only the first would ever apply — self.jwt.all(a, self.jwt.exists_one(b, b.issuer.url == a.issuer.url))

spec.auth_config.jwt[]

FieldTypeRequiredDefaultDescription
claim_mappingsobjectyesHow the claims become a username, groups, uid and extra.
claim_validation_rulesobject[]no[]Rules the raw claims must satisfy before any mapping happens.
issuerobjectyesThe token issuer a [super::JWTAuthenticator] trusts, mirroring the apiserver's issuer.
user_validation_rulesobject[]no[]Rules the mapped user must satisfy — e.g. refusing a system: prefix.

spec.auth_config.jwt[].claim_mappings

FieldTypeRequiredDefaultDescription
extraobject[]no[]
groupsobjectnoEither a claim (with an optional prefix) or a CEL expression, never both.

prefix exists to keep an external identity provider from minting a username that collides with a local one; it applies only to the claim form, since an expression can prepend whatever it likes itself.
uidobjectnoEither a claim or a CEL expression, never both.
usernameobjectnoRequired — a token that validates but maps to no username authenticates nobody, so the apiserver requires it too.

Rules

  • claim_mappings.username is required: without it a validated token maps to no user — has(self.username)

spec.auth_config.jwt[].claim_mappings.extra[]

FieldTypeRequiredDefaultDescription
keystringyes
value_expressionstringno""A CEL expression over claims producing the value(s) for key.

spec.auth_config.jwt[].claim_mappings.groups

FieldTypeRequiredDefaultDescription
claimstringnoConstraints: maxLength: 253
expressionstringnoConstraints: maxLength: 4096
prefixstringnoPrepended to the claim's value. Only valid with claim.

Constraints: maxLength: 253

Rules

  • exactly one of claim or expression must be set — has(self.claim) != has(self.expression)
  • prefix may only be set together with claim — !has(self.prefix) || has(self.claim)
  • prefix is required with claim (use "" to opt out of prefixing deliberately) — !has(self.claim) || has(self.prefix)

spec.auth_config.jwt[].claim_mappings.uid

FieldTypeRequiredDefaultDescription
claimstringnoConstraints: maxLength: 253
expressionstringnoConstraints: maxLength: 4096

Rules

  • exactly one of claim or expression must be set — has(self.claim) != has(self.expression)

spec.auth_config.jwt[].claim_mappings.username

FieldTypeRequiredDefaultDescription
claimstringnoConstraints: maxLength: 253
expressionstringnoConstraints: maxLength: 4096
prefixstringnoPrepended to the claim's value. Only valid with claim.

Constraints: maxLength: 253

Rules

  • exactly one of claim or expression must be set — has(self.claim) != has(self.expression)
  • prefix may only be set together with claim — !has(self.prefix) || has(self.claim)
  • prefix is required with claim (use "" to opt out of prefixing deliberately) — !has(self.claim) || has(self.prefix)

spec.auth_config.jwt[].claim_validation_rules[]

FieldTypeRequiredDefaultDescription
claimstringnoThe claim whose value must equal required_value.

Constraints: maxLength: 253
expressionstringnoA CEL expression over claims that must evaluate to true.

Constraints: maxLength: 4096
messagestringno""Shown when expression rejects a token. Required with expression: a rejection nobody can explain is a support ticket, not a security control.

Constraints: maxLength: 1024
required_valuestringno""The value claim must hold. Absent means "the claim must merely be present", which is how the apiserver reads an empty requiredValue.

Constraints: maxLength: 1024

Rules

  • exactly one of claim or expression must be set — has(self.claim) != has(self.expression)
  • required_value may only be set together with claim — self.required_value == '' || has(self.claim)
  • an expression rule requires a message explaining what it rejects — !has(self.expression) || self.message != ''

spec.auth_config.jwt[].issuer

FieldTypeRequiredDefaultDescription
audience_match_policystringno"MatchAny"Values: "MatchAny"
audiencesstring[]no[]Accepted aud values. At least one — an authenticator that accepts any audience is the audience-confusion hole this whole area exists to close.
certificate_authoritystringnoPEM bundle used to verify the issuer's TLS certificate, added to the system trust store (never replacing it). Absent means the system trust store alone. A bundle without any certificate is refused.
discovery_urlstringnoWhere the discovery document lives, when it is not url + /.well-known/openid-configuration.

Constraints: maxLength: 253
egress_selectorstringno"controlplane"Values: "controlplane", "cluster"
urlstringyesThe iss claim every token must carry, and the base for OIDC discovery unless discovery_url overrides it.

Constraints: maxLength: 253

Rules

  • issuer.url must be an https:// URL — self.url.startsWith('https://')
  • issuer.audiences must list at least one audience — size(self.audiences) > 0
  • issuer.discovery_url must be an https:// URL — !has(self.discovery_url) || self.discovery_url.startsWith('https://')

spec.auth_config.jwt[].user_validation_rules[]

FieldTypeRequiredDefaultDescription
expressionstringno""A CEL expression over user that must evaluate to true.

Constraints: maxLength: 4096
messagestringno""Shown when the rule rejects a user.

Constraints: maxLength: 1024

Rules

  • a user validation rule requires both an expression and a message — self.expression != '' && self.message != ''

spec.auth_config.oidc_provider

FieldTypeRequiredDefaultDescription
accept_authorized_partybooleannofalseAccept a token whose aud does not name this service but whose azp/client_id does.

Off by default: azp/client_id identify the client the token was issued to, not the resource, so accepting them weakens audience validation. Enable only for providers (e.g. Keycloak) that mint self-audience tokens carrying the client only in azp.
audiencestringno""Audience the access token must carry (its aud claim) to be accepted.

When empty, client_id is used — correct for providers that put the client id in aud. Set it explicitly when the provider mints tokens for a distinct resource audience, so client_id (who the token is for) and the expected audience (what the token is for) are configured independently.
claim_mappingsobjectnoHow the raw /userinfo claims become the user, the same shape as a jwt authenticator's (RFC 0014). Absent: preferred_username and groups, as before. Required in FrontProxy and Issuer.
client_idstringno""Ignored when the config_from Secret carries a client_id key. See issuer_url for why it is optional.
client_secretstringnoThe OAuth client secret, inline.

Prefer config_from: a value here is readable by anyone with get on this resource, shows up in kubectl get -o yaml, and is copied into every backup of it.
config_fromobjectnoRead the provider block from an external Secret instead of inlining it here.

The Secret's keys are the snake_case field names of this struct — issuer_url, client_id, client_secret, audience, extra_scope — and any key it carries wins over the inline value; keys it does not carry fall back to the inline value, so a partial Secret (the common case: just client_secret) works. Keys outside that set are ignored, since a Secret managed by external-secrets is routinely shared with other consumers.

The policy toggles (enabled, accept_authorized_party, expose_oauth_authorization_server) are deliberately not readable from the Secret: they decide how strictly tokens are validated and who may reach this cluster, so they must stay visible in the CR that admission and review look at.
enabledbooleannofalse
expose_oauth_authorization_serverbooleannofalseExpose a mediated OAuth 2.0 Authorization Server (RFC 6749 + PKCE) for this cluster, advertised at /.well-known/oauth-authorization-server (RFC 8414), like OpenShift does for its own OAuth server.

The proxy itself becomes the authorization server an external caller talks to — issuer, authorization_endpoint, token_endpoint, and jwks_uri are all this cluster's own proxy URLs — mediating the whole exchange with the configured issuer_url server-side. A caller (e.g. oc login, or any RFC-8414-aware tool) never needs to be registered with — or even learn the hostname of — the upstream provider, and no companion oauth2-proxy is needed to serve this document.

Off by default and unauthenticated by nature (discovery, and the authorization/token endpoints it advertises, must be reachable before a caller has a token): enabling it lets anonymous callers learn that this cluster exists and start a login against it. redirect_uri at /oauth/authorize is restricted to loopback addresses (http://localhost/http://127.0.0.1, RFC 8252) since callers are not pre-registered — accepting an arbitrary redirect_uri would be an open redirect. Requires enabled: true.
extra_scopestringno""
issuer_urlstringno""Ignored when the config_from Secret carries an issuer_url key. Optional only so a config_from-only block need not restate it; an enabled provider must end up with a non-empty value from one side or the other.

Rules

  • an enabled OIDC provider requires a non-empty issuer_url and client_id, or a config_from reference supplying them — !self.enabled || has(self.config_from) || (self.issuer_url != '' && self.client_id != '')
  • expose_oauth_authorization_server requires the OIDC provider to be enabled — !self.expose_oauth_authorization_server || self.enabled

spec.auth_config.oidc_provider.claim_mappings

FieldTypeRequiredDefaultDescription
extraobject[]no[]
groupsobjectnoEither a claim (with an optional prefix) or a CEL expression, never both.

prefix exists to keep an external identity provider from minting a username that collides with a local one; it applies only to the claim form, since an expression can prepend whatever it likes itself.
uidobjectnoEither a claim or a CEL expression, never both.
usernameobjectnoRequired — a token that validates but maps to no username authenticates nobody, so the apiserver requires it too.

Rules

  • claim_mappings.username is required: without it a validated token maps to no user — has(self.username)

spec.auth_config.oidc_provider.claim_mappings.extra[]

FieldTypeRequiredDefaultDescription
keystringyes
value_expressionstringno""A CEL expression over claims producing the value(s) for key.

spec.auth_config.oidc_provider.claim_mappings.groups

FieldTypeRequiredDefaultDescription
claimstringnoConstraints: maxLength: 253
expressionstringnoConstraints: maxLength: 4096
prefixstringnoPrepended to the claim's value. Only valid with claim.

Constraints: maxLength: 253

Rules

  • exactly one of claim or expression must be set — has(self.claim) != has(self.expression)
  • prefix may only be set together with claim — !has(self.prefix) || has(self.claim)
  • prefix is required with claim (use "" to opt out of prefixing deliberately) — !has(self.claim) || has(self.prefix)

spec.auth_config.oidc_provider.claim_mappings.uid

FieldTypeRequiredDefaultDescription
claimstringnoConstraints: maxLength: 253
expressionstringnoConstraints: maxLength: 4096

Rules

  • exactly one of claim or expression must be set — has(self.claim) != has(self.expression)

spec.auth_config.oidc_provider.claim_mappings.username

FieldTypeRequiredDefaultDescription
claimstringnoConstraints: maxLength: 253
expressionstringnoConstraints: maxLength: 4096
prefixstringnoPrepended to the claim's value. Only valid with claim.

Constraints: maxLength: 253

Rules

  • exactly one of claim or expression must be set — has(self.claim) != has(self.expression)
  • prefix may only be set together with claim — !has(self.prefix) || has(self.claim)
  • prefix is required with claim (use "" to opt out of prefixing deliberately) — !has(self.claim) || has(self.prefix)

spec.auth_config.oidc_provider.config_from

FieldTypeRequiredDefaultDescription
SecretobjectyesRead the provider block from the keys of a Secret.

spec.auth_config.oidc_provider.config_from.Secret

FieldTypeRequiredDefaultDescription
namestringyes
namespacestringnoDefaults to the ProxyKubeApi's own namespace. Honoured only when PROXYAUTH_ALLOW_CROSS_NS_OIDC is set — see [cross_namespace_oidc_allowed].

spec.cert

FieldTypeRequiredDefaultDescription
Certstringone ofBase64 encoded cert
ConfigMapobjectone ofConfigmap
Insecurebooleanone ofDeprecated, use SystemRoots. Despite its name this never disabled TLS verification: it behaves exactly like SystemRoots. The value is ignored.
Secretobjectone ofUse a cert from a secret
SystemRootsbooleanone ofVerify the target with the system trust store (no custom CA). The value is ignored.

Exactly one of Secret, Cert, ConfigMap, SystemRoots, Insecure.

spec.cert.ConfigMap

FieldTypeRequiredDefaultDescription
keystringyes
namestringyes
namespacestringno

spec.cert.Secret

FieldTypeRequiredDefaultDescription
keystringyes
namestringyes
namespacestringno

spec.client_cert

FieldTypeRequiredDefaultDescription
certobjectyesPEM client certificate chain
keyobjectyesPEM private key matching the certificate

spec.client_cert.cert

FieldTypeRequiredDefaultDescription
Certstringone ofBase64 encoded cert
ConfigMapobjectone ofConfigmap
Insecurebooleanone ofDeprecated, use SystemRoots. Despite its name this never disabled TLS verification: it behaves exactly like SystemRoots. The value is ignored.
Secretobjectone ofUse a cert from a secret
SystemRootsbooleanone ofVerify the target with the system trust store (no custom CA). The value is ignored.

Exactly one of Secret, Cert, ConfigMap, SystemRoots, Insecure.

spec.client_cert.cert.ConfigMap

FieldTypeRequiredDefaultDescription
keystringyes
namestringyes
namespacestringno

spec.client_cert.cert.Secret

FieldTypeRequiredDefaultDescription
keystringyes
namestringyes
namespacestringno

spec.client_cert.key

FieldTypeRequiredDefaultDescription
Certstringone ofBase64 encoded cert
ConfigMapobjectone ofConfigmap
Insecurebooleanone ofDeprecated, use SystemRoots. Despite its name this never disabled TLS verification: it behaves exactly like SystemRoots. The value is ignored.
Secretobjectone ofUse a cert from a secret
SystemRootsbooleanone ofVerify the target with the system trust store (no custom CA). The value is ignored.

Exactly one of Secret, Cert, ConfigMap, SystemRoots, Insecure.

spec.client_cert.key.ConfigMap

FieldTypeRequiredDefaultDescription
keystringyes
namestringyes
namespacestringno

spec.client_cert.key.Secret

FieldTypeRequiredDefaultDescription
keystringyes
namestringyes
namespacestringno

spec.security_config

FieldTypeRequiredDefaultDescription
allowed_resourcesobject[]no[]Allowed resources, limit the access to the proxy to only these resources, if empty all resources are allowed

The bound keeps the estimated cost of the per-item CEL rules within the budget the apiserver allows for a whole CRD schema.

Constraints: maxItems: 128
allowed_ressourcesobject[]no[]Deprecated misspelling of allowed_resources, merged with it

Kept as a real field rather than a serde alias on purpose: the apiserver prunes fields absent from the schema, so an alias alone would silently drop the security configuration of every existing resource. Remove it in the next major, once resources have been migrated.

Constraints: maxItems: 128
enabledbooleannotrueWhether the token is validated beforehand
fail2login_equal_banobjectno{"ban_duration":300,"enabled":false,"exponential_backoff":false,"max_failed_logins":5}Configuration for banning callers after multiple failed authentications
mask_exec_secretsbooleannofalseMask secrets in the command lines the audit records for exec (RFC 0004)

When true, -pSECRET, --password=SECRET, --token=SECRET and the element that follows -p, --password or --token are written as ***. A heuristic, not a guarantee: off by default, so the audit keeps the command exactly as it ran.
per_user_group_rate_limitingobject[]no[]Per group rate limiting configuration This takes precedence over the global rate limiting configuration
rate_limitingobjectno{"enabled":false,"max_requests_per_minute":60}Global rate limiting configuration

spec.security_config.allowed_resources[]

FieldTypeRequiredDefaultDescription
Crdobjectone ofAllowed crd configuration, used in conjunction with the allowed_paths configuration /apis/{group}/{version}/namespaces/{namespace}/{kind}/
Pathobjectone ofAllowed path configuration, used in conjunction with the allowed_paths configuration

The CEL rules mirror [AllowedPathConfiguration::validate] so a malformed rule is refused at admission rather than at reconcile time.

The placeholder regex matches braces through character classes ([{]) rather than backslash escapes, for two independent reasons: CEL string literals reject \{ outright, and the CRD is also shipped as a Helm template where two consecutive { would be read as a Go template action.

Exactly one of Path, Crd.

spec.security_config.allowed_resources[].Crd

FieldTypeRequiredDefaultDescription
allowed_portsstring[]noPorts this rule grants on the port-bearing subresources of a Pod (portforward, {name}:{port}/proxy) or a Service ({name}:{port}/proxy, a Service port, not its targetPort), as single ports ("8080") or inclusive ranges ("9000-9100")

Same meaning as on a Path rule: once any rule a caller is subject to lists ports, a rule without allowed_ports grants no port at all.

Constraints: maxItems: 64
execobjectnoCommands, containers and streams this Pod rule allows on exec and attach (same meaning as on a Path rule; RFC 0004)
groupstringyesThe group of the crd
kindstringyesThe kind of the crd
namespaceobjectyesWether or not the ressource is namespaced, if true, the namespace access rules will be applied to this resource
namespacedbooleannotrueNamespaced or not default: true
pluralstringnoWether or not the kind has a specific plural form, if true, the plural form will be used in the path instead of the kind for example, if the kind is "MyResource" and the plural form is "MyResources" the path will be /apis/{group}/{version}/namespaces/{namespace}/myresources/ instead of /apis/{group}/{version}/namespaces/{namespace}/myresource/ In case of cluster-scoped crd the path will be /apis/{group}/{version}/myresources/ instead of /apis/{group}/{version}/myresource/
verbsstring[]noKubernetes verbs this rule allows on the resource and all its subresources (same values and meaning as on a Path rule)

A Pod rule with patch or update also allows ephemeralcontainers, that is kubectl debug.

Values: "get", "list", "watch", "create", "update", "patch", "delete", "deletecollection", "*"

Constraints: minItems: 1, maxItems: 9
versionstringyesThe version of the crd

Rules

  • allowed_ports only applies to the Pod and Service kinds — !has(self.allowed_ports) || self.kind in ['Pod', 'Service']
  • exec only applies to the Pod kind — !has(self.exec) || self.kind == 'Pod'
  • allowed_ports[]: ports must be within 1-65535 and a range must not be reversed — self.split('-').all(p, int(p) >= 1 && int(p) <= 65535) && (!self.contains('-') || int(self.split('-')[0]) <= int(self.split('-')[1]))
  • verbs: "*" already allows every verb and must stand alone — !self.exists(v, v == '*') || size(self) == 1

spec.security_config.allowed_resources[].Crd.exec

FieldTypeRequiredDefaultDescription
attachbooleannofalseWhether this rule allows pods/{name}/attach (its commands are ignored there) default: false
commandsstring[][]noCommands this rule allows, element by element (["ls", "**"]); absent or empty: none

A pattern starting with a shell or an interpreter and holding a wildcard allows anything, and is reported in status.warnings.

Constraints: maxItems: 64
containersstring[]noContainers this rule allows (* allowed in a pattern); absent: all, empty: none

Under a container restriction, a request must name its container (kubectl exec -c); kubectl always does.

Constraints: maxItems: 32
stdinbooleannofalseWhether stdin may be attached default: false
ttybooleannofalseWhether a tty may be allocated (needs stdin) default: false

Rules

  • tty needs stdin — !(has(self.tty) && self.tty) || (has(self.stdin) && self.stdin)
  • commands[]: "**" may only be the last element of a command — self.all(e, e != '**') || (self[size(self) - 1] == '**' && self.filter(e, e == '**').size() == 1)

spec.security_config.allowed_resources[].Crd.namespace

FieldTypeRequiredDefaultDescription
enabledbooleannofalseIf the feature is enabled default: false
rule_kindobjectyesThe kind of the namespace access rule

spec.security_config.allowed_resources[].Crd.namespace.rule_kind

FieldTypeRequiredDefaultDescription
AllowedNamespacesstring[]one of
DeniedNamespacesstring[]one of
ParametisedRulestringone ofThe parametised rule is a string that can contain the {{username}} parameter which will be replaced by the username of the user making the request, and the {{group}} parameter which will be replaced by the groups of the user making the request allowed parameters are : {{username}} and {{group}}

Exactly one of AllowedNamespaces, DeniedNamespaces, ParametisedRule.

spec.security_config.allowed_resources[].Path

FieldTypeRequiredDefaultDescription
allowed_portsstring[]noPorts this rule grants on the port-bearing paths it matches, as single ports ("8080") or inclusive ranges ("9000-9100")

Port-bearing paths are .../pods/{name}/portforward, .../pods/{name}:{port}/proxy and .../services/{name}:{port}/proxy (a Service port). Once any rule lists ports, every caller is restricted: the ports of the matching rules that list some are combined, and a rule without allowed_ports grants none. An empty list grants no port.

Constraints: maxItems: 64
execobjectnoCommands, containers and streams this rule allows on the pods/{name}/exec and /attach paths it matches (RFC 0004)

Once any rule a caller is subject to declares exec, only the rules that match the path and declare exec allow exec or attach, and a request is accepted when one of them accepts it entirely. A Path rule has no subject: one with exec restricts every caller.
parametisedbooleannofalseWether or not the path is parametised if true, the path will be treated as a template, it either handle wildcard parameters, like "" or "dev-", or mustache-like parameters, like "{{username}}" or "{{group}}" for example, if the path is "/api/v1/namespaces//pods", it will allow all requests to pods in any namespace if the path is "/api/v1/namespaces/dev-/pods", it will allow all requests to pods in namespaces that start with "dev-" It will also try to detect mustache-like parameters, for example, if the path is "/api/v1/namespaces/{{username}}/pods" it will allow all requests to pods in namespaces carrying the username as a parameter if the selected field is an array, like the groups claim, it will try to match any of the values in the array for example, if the groups claim is ["dev-alice", "dev-bob"] and the path is "/api/v1/namespaces/{{group}}/pods", it will allow all requests to pods in namespaces that match either "dev-alice" or "dev-bob" Allowed parameters are : {{username}} and {{group}} A * only matches inside a single path segment; use a ** segment to match any number of remaining segments (needed for subresources such as "/api/v1/namespaces/dev/pods/mypod/log") default: false
pathstringyesThe path to allow, if the request path equals this path, it will be allowed

The length bound is not cosmetic: without it the apiserver assumes an unbounded string and refuses the CEL rule above as too expensive.

Constraints: maxLength: 512
verbsstring[]noKubernetes verbs this rule allows, computed as the apiserver does (get, list, watch, create, update, patch, delete, deletecollection, or * alone)

Once any rule a caller is subject to lists verbs, a rule without verbs only grants get, list and watch. exec, attach and portforward are create; allowed_ports implies create on portforward.

Values: "get", "list", "watch", "create", "update", "patch", "delete", "deletecollection", "*"

Constraints: minItems: 1, maxItems: 9

Rules

  • path must start with '/' — self.path.startsWith('/')
  • a parametised path only accepts the username and group placeholders — !self.parametised || self.path.matches('^[^{}]*(([{][{](username|group)[}][}])[^{}]*)*$')
  • allowed_ports[]: ports must be within 1-65535 and a range must not be reversed — self.split('-').all(p, int(p) >= 1 && int(p) <= 65535) && (!self.contains('-') || int(self.split('-')[0]) <= int(self.split('-')[1]))
  • verbs: "*" already allows every verb and must stand alone — !self.exists(v, v == '*') || size(self) == 1

spec.security_config.allowed_resources[].Path.exec

FieldTypeRequiredDefaultDescription
attachbooleannofalseWhether this rule allows pods/{name}/attach (its commands are ignored there) default: false
commandsstring[][]noCommands this rule allows, element by element (["ls", "**"]); absent or empty: none

A pattern starting with a shell or an interpreter and holding a wildcard allows anything, and is reported in status.warnings.

Constraints: maxItems: 64
containersstring[]noContainers this rule allows (* allowed in a pattern); absent: all, empty: none

Under a container restriction, a request must name its container (kubectl exec -c); kubectl always does.

Constraints: maxItems: 32
stdinbooleannofalseWhether stdin may be attached default: false
ttybooleannofalseWhether a tty may be allocated (needs stdin) default: false

Rules

  • tty needs stdin — !(has(self.tty) && self.tty) || (has(self.stdin) && self.stdin)
  • commands[]: "**" may only be the last element of a command — self.all(e, e != '**') || (self[size(self) - 1] == '**' && self.filter(e, e == '**').size() == 1)

spec.security_config.allowed_ressources[]

FieldTypeRequiredDefaultDescription
Crdobjectone ofAllowed crd configuration, used in conjunction with the allowed_paths configuration /apis/{group}/{version}/namespaces/{namespace}/{kind}/
Pathobjectone ofAllowed path configuration, used in conjunction with the allowed_paths configuration

The CEL rules mirror [AllowedPathConfiguration::validate] so a malformed rule is refused at admission rather than at reconcile time.

The placeholder regex matches braces through character classes ([{]) rather than backslash escapes, for two independent reasons: CEL string literals reject \{ outright, and the CRD is also shipped as a Helm template where two consecutive { would be read as a Go template action.

Exactly one of Path, Crd.

spec.security_config.allowed_ressources[].Crd

FieldTypeRequiredDefaultDescription
allowed_portsstring[]noPorts this rule grants on the port-bearing subresources of a Pod (portforward, {name}:{port}/proxy) or a Service ({name}:{port}/proxy, a Service port, not its targetPort), as single ports ("8080") or inclusive ranges ("9000-9100")

Same meaning as on a Path rule: once any rule a caller is subject to lists ports, a rule without allowed_ports grants no port at all.

Constraints: maxItems: 64
execobjectnoCommands, containers and streams this Pod rule allows on exec and attach (same meaning as on a Path rule; RFC 0004)
groupstringyesThe group of the crd
kindstringyesThe kind of the crd
namespaceobjectyesWether or not the ressource is namespaced, if true, the namespace access rules will be applied to this resource
namespacedbooleannotrueNamespaced or not default: true
pluralstringnoWether or not the kind has a specific plural form, if true, the plural form will be used in the path instead of the kind for example, if the kind is "MyResource" and the plural form is "MyResources" the path will be /apis/{group}/{version}/namespaces/{namespace}/myresources/ instead of /apis/{group}/{version}/namespaces/{namespace}/myresource/ In case of cluster-scoped crd the path will be /apis/{group}/{version}/myresources/ instead of /apis/{group}/{version}/myresource/
verbsstring[]noKubernetes verbs this rule allows on the resource and all its subresources (same values and meaning as on a Path rule)

A Pod rule with patch or update also allows ephemeralcontainers, that is kubectl debug.

Values: "get", "list", "watch", "create", "update", "patch", "delete", "deletecollection", "*"

Constraints: minItems: 1, maxItems: 9
versionstringyesThe version of the crd

Rules

  • allowed_ports only applies to the Pod and Service kinds — !has(self.allowed_ports) || self.kind in ['Pod', 'Service']
  • exec only applies to the Pod kind — !has(self.exec) || self.kind == 'Pod'
  • allowed_ports[]: ports must be within 1-65535 and a range must not be reversed — self.split('-').all(p, int(p) >= 1 && int(p) <= 65535) && (!self.contains('-') || int(self.split('-')[0]) <= int(self.split('-')[1]))
  • verbs: "*" already allows every verb and must stand alone — !self.exists(v, v == '*') || size(self) == 1

spec.security_config.allowed_ressources[].Crd.exec

FieldTypeRequiredDefaultDescription
attachbooleannofalseWhether this rule allows pods/{name}/attach (its commands are ignored there) default: false
commandsstring[][]noCommands this rule allows, element by element (["ls", "**"]); absent or empty: none

A pattern starting with a shell or an interpreter and holding a wildcard allows anything, and is reported in status.warnings.

Constraints: maxItems: 64
containersstring[]noContainers this rule allows (* allowed in a pattern); absent: all, empty: none

Under a container restriction, a request must name its container (kubectl exec -c); kubectl always does.

Constraints: maxItems: 32
stdinbooleannofalseWhether stdin may be attached default: false
ttybooleannofalseWhether a tty may be allocated (needs stdin) default: false

Rules

  • tty needs stdin — !(has(self.tty) && self.tty) || (has(self.stdin) && self.stdin)
  • commands[]: "**" may only be the last element of a command — self.all(e, e != '**') || (self[size(self) - 1] == '**' && self.filter(e, e == '**').size() == 1)

spec.security_config.allowed_ressources[].Crd.namespace

FieldTypeRequiredDefaultDescription
enabledbooleannofalseIf the feature is enabled default: false
rule_kindobjectyesThe kind of the namespace access rule

spec.security_config.allowed_ressources[].Crd.namespace.rule_kind

FieldTypeRequiredDefaultDescription
AllowedNamespacesstring[]one of
DeniedNamespacesstring[]one of
ParametisedRulestringone ofThe parametised rule is a string that can contain the {{username}} parameter which will be replaced by the username of the user making the request, and the {{group}} parameter which will be replaced by the groups of the user making the request allowed parameters are : {{username}} and {{group}}

Exactly one of AllowedNamespaces, DeniedNamespaces, ParametisedRule.

spec.security_config.allowed_ressources[].Path

FieldTypeRequiredDefaultDescription
allowed_portsstring[]noPorts this rule grants on the port-bearing paths it matches, as single ports ("8080") or inclusive ranges ("9000-9100")

Port-bearing paths are .../pods/{name}/portforward, .../pods/{name}:{port}/proxy and .../services/{name}:{port}/proxy (a Service port). Once any rule lists ports, every caller is restricted: the ports of the matching rules that list some are combined, and a rule without allowed_ports grants none. An empty list grants no port.

Constraints: maxItems: 64
execobjectnoCommands, containers and streams this rule allows on the pods/{name}/exec and /attach paths it matches (RFC 0004)

Once any rule a caller is subject to declares exec, only the rules that match the path and declare exec allow exec or attach, and a request is accepted when one of them accepts it entirely. A Path rule has no subject: one with exec restricts every caller.
parametisedbooleannofalseWether or not the path is parametised if true, the path will be treated as a template, it either handle wildcard parameters, like "" or "dev-", or mustache-like parameters, like "{{username}}" or "{{group}}" for example, if the path is "/api/v1/namespaces//pods", it will allow all requests to pods in any namespace if the path is "/api/v1/namespaces/dev-/pods", it will allow all requests to pods in namespaces that start with "dev-" It will also try to detect mustache-like parameters, for example, if the path is "/api/v1/namespaces/{{username}}/pods" it will allow all requests to pods in namespaces carrying the username as a parameter if the selected field is an array, like the groups claim, it will try to match any of the values in the array for example, if the groups claim is ["dev-alice", "dev-bob"] and the path is "/api/v1/namespaces/{{group}}/pods", it will allow all requests to pods in namespaces that match either "dev-alice" or "dev-bob" Allowed parameters are : {{username}} and {{group}} A * only matches inside a single path segment; use a ** segment to match any number of remaining segments (needed for subresources such as "/api/v1/namespaces/dev/pods/mypod/log") default: false
pathstringyesThe path to allow, if the request path equals this path, it will be allowed

The length bound is not cosmetic: without it the apiserver assumes an unbounded string and refuses the CEL rule above as too expensive.

Constraints: maxLength: 512
verbsstring[]noKubernetes verbs this rule allows, computed as the apiserver does (get, list, watch, create, update, patch, delete, deletecollection, or * alone)

Once any rule a caller is subject to lists verbs, a rule without verbs only grants get, list and watch. exec, attach and portforward are create; allowed_ports implies create on portforward.

Values: "get", "list", "watch", "create", "update", "patch", "delete", "deletecollection", "*"

Constraints: minItems: 1, maxItems: 9

Rules

  • path must start with '/' — self.path.startsWith('/')
  • a parametised path only accepts the username and group placeholders — !self.parametised || self.path.matches('^[^{}]*(([{][{](username|group)[}][}])[^{}]*)*$')
  • allowed_ports[]: ports must be within 1-65535 and a range must not be reversed — self.split('-').all(p, int(p) >= 1 && int(p) <= 65535) && (!self.contains('-') || int(self.split('-')[0]) <= int(self.split('-')[1]))
  • verbs: "*" already allows every verb and must stand alone — !self.exists(v, v == '*') || size(self) == 1

spec.security_config.allowed_ressources[].Path.exec

FieldTypeRequiredDefaultDescription
attachbooleannofalseWhether this rule allows pods/{name}/attach (its commands are ignored there) default: false
commandsstring[][]noCommands this rule allows, element by element (["ls", "**"]); absent or empty: none

A pattern starting with a shell or an interpreter and holding a wildcard allows anything, and is reported in status.warnings.

Constraints: maxItems: 64
containersstring[]noContainers this rule allows (* allowed in a pattern); absent: all, empty: none

Under a container restriction, a request must name its container (kubectl exec -c); kubectl always does.

Constraints: maxItems: 32
stdinbooleannofalseWhether stdin may be attached default: false
ttybooleannofalseWhether a tty may be allocated (needs stdin) default: false

Rules

  • tty needs stdin — !(has(self.tty) && self.tty) || (has(self.stdin) && self.stdin)
  • commands[]: "**" may only be the last element of a command — self.all(e, e != '**') || (self[size(self) - 1] == '**' && self.filter(e, e == '**').size() == 1)

spec.security_config.fail2login_equal_ban

FieldTypeRequiredDefaultDescription
ban_durationintegerno300The duration of the ban in seconds default: 300 (5 minutes) 0 means permanent ban

Constraints: minimum: 0
enabledbooleannofalseIf the feature is enabled default: false
exponential_backoffbooleannofalseIf the time is exponentially increased with each failed login attempt default: false
max_failed_loginsintegerno5The number of failed login attempts before the user is banned default: 5

Constraints: minimum: 0

spec.security_config.per_user_group_rate_limiting[]

FieldTypeRequiredDefaultDescription
groupstringyesGroup name
max_requests_per_minuteintegeryesThe maximum number of requests per minute for this group This setting overrides the global rate limiting setting 0 disables the rate limiting for this group

Constraints: minimum: 0

spec.security_config.rate_limiting

FieldTypeRequiredDefaultDescription
enabledbooleannofalseIf the feature is enabled default: false
max_requests_per_minuteintegerno60The maximum number of requests per minute default: 60 0 disables the rate global rate limiting

Constraints: minimum: 0

spec.service

FieldTypeRequiredDefaultDescription
Agentobjectone ofReached through an agent on the remote cluster, which dials out to the proxy: the apiserver accepts no inbound connection (RFC 0012)
ExternalServiceobjectone ofExternal service
KubernetesServiceobjectone ofKubernetes service

Exactly one of KubernetesService, ExternalService, Agent.

spec.service.Agent

FieldTypeRequiredDefaultDescription
bootstrapobjectyesHow an agent proves it serves this cluster before it gets a certificate
epochintegerno0Raise it to revoke every agent certificate issued so far: open tunnels close within 30 s and the agent has to enrol again Default: 0

Constraints: minimum: 0
limitsobjectnoOverrides of the gateway's tunnel bounds, for this cluster only
server_namestringno"kubernetes.default.svc"Name checked against the remote apiserver's certificate Default: kubernetes.default.svc

Constraints: minLength: 1, maxLength: 253

Rules

  • server_name must be a DNS name or an IP address — self.server_name.matches('^[a-z0-9]([-a-z0-9.]*[a-z0-9])?$') || isIP(self.server_name)
  • epoch must not decrease: a lower epoch would make revoked agent certificates valid again — self.epoch >= oldSelf.epoch

spec.service.Agent.bootstrap

FieldTypeRequiredDefaultDescription
ExternalCAobjectone ofNo enrolment: the agent's certificate comes from an issuer of yours (cert-manager, Vault PKI), with the URI SAN proxyauth://<namespace>/<name> of this resource, and chains to the CA named here
OneShotTokenobjectone ofA single-use token the controller writes to the Secret <name>-agent-bootstrap next to this resource, valid one hour; carry it to the remote cluster (External Secrets, Vault)
ServiceAccountTokenobjectone ofA projected ServiceAccount token of the remote cluster, checked against that cluster's public keys: nothing secret goes into git

Exactly one of ServiceAccountToken, OneShotToken, ExternalCA.

spec.service.Agent.bootstrap.ExternalCA

FieldTypeRequiredDefaultDescription
castringnoThe issuing CA, PEM, inline (public)

Constraints: maxLength: 65536
ca_refobjectnoA ConfigMap or Secret holding the issuing CA, PEM, re-read at each connection (same namespace policy as cert)
max_cert_lifetime_hoursintegerno168A longer certificate is refused, and a tunnel closes when its certificate expires: raising epoch only closes the open tunnels, revoke at the issuer and keep certificates short Default: 168 (7 days)

Constraints: minimum: 1, maximum: 8760

Rules

  • set exactly one of ca and ca_ref — has(self.ca) != has(self.ca_ref)

spec.service.Agent.bootstrap.ExternalCA.ca_ref

FieldTypeRequiredDefaultDescription
ConfigMapobjectone of
Secretobjectone of

Exactly one of ConfigMap, Secret.

spec.service.Agent.bootstrap.ExternalCA.ca_ref.ConfigMap

FieldTypeRequiredDefaultDescription
keystringyes
namestringyes
namespacestringnoIf not set, the resource namespace (same policy as cert)

spec.service.Agent.bootstrap.ExternalCA.ca_ref.Secret

FieldTypeRequiredDefaultDescription
keystringyes
namestringyes
namespacestringnoIf not set, the resource namespace (same policy as cert)

spec.service.Agent.bootstrap.OneShotToken

FieldTypeRequiredDefaultDescription
auto_rotatebooleannofalseWrite a new token as soon as the previous one is used or expired; otherwise, annotate this resource with proxyauth.weebo.si.rs/regenerate-bootstrap (any new value) for one Default: false

spec.service.Agent.bootstrap.ServiceAccountToken

FieldTypeRequiredDefaultDescription
audiencestringno"proxyauth"aud of the agent's token Default: proxyauth

Constraints: minLength: 1, maxLength: 253
jwksstringnoThe remote cluster's JWKS, inline (kubectl get --raw /openid/v1/jwks) Frozen: a key rotation needs an edit

Constraints: maxLength: 65536
jwks_fromobjectnoA ConfigMap or Secret holding the JWKS, re-read at enrolment: for an issuer only reachable inside the remote cluster (kubeadm, k3s)
jwks_urlstringnoWhere a public issuer serves its JWKS (EKS, GKE, AKS)

Constraints: maxLength: 2048
subjectstringyessub of the agent's token: system:serviceaccount:<namespace>:<name>

Constraints: maxLength: 316

Rules

  • set exactly one of jwks, jwks_url and jwks_from — [has(self.jwks), has(self.jwks_url), has(self.jwks_from)].filter(x, x).size() == 1
  • jwks_url must be an https:// URL — !has(self.jwks_url) || self.jwks_url.startsWith('https://')
  • subject must be a ServiceAccount (system:serviceaccount:<namespace>:<name>) — self.subject.startsWith('system:serviceaccount:')

spec.service.Agent.bootstrap.ServiceAccountToken.jwks_from

FieldTypeRequiredDefaultDescription
ConfigMapobjectone of
Secretobjectone of

Exactly one of ConfigMap, Secret.

spec.service.Agent.bootstrap.ServiceAccountToken.jwks_from.ConfigMap

FieldTypeRequiredDefaultDescription
keystringyes
namestringyes
namespacestringnoIf not set, the resource namespace (same policy as cert)

spec.service.Agent.bootstrap.ServiceAccountToken.jwks_from.Secret

FieldTypeRequiredDefaultDescription
keystringyes
namestringyes
namespacestringnoIf not set, the resource namespace (same policy as cert)

spec.service.Agent.limits

FieldTypeRequiredDefaultDescription
streams_per_tunnelintegernoStreams one tunnel carries at once, for a cluster with many concurrent clients (an operator driving it through the proxy). Capped by the gateway's maxStreamsPerTunnel, with a warning on the CR Default: the gateway's streamsPerTunnel

Constraints: minimum: 1

spec.service.ExternalService

FieldTypeRequiredDefaultDescription
urlstringyesURL of the external service (e.g. https://example.com)

spec.service.KubernetesService

FieldTypeRequiredDefaultDescription
namestringyesName of the service
namespacestringnoIf not set, will use the resource namespace
portintegernoPort of the service

Constraints: minimum: 0, maximum: 65535
port_namestringnoPort name of the service

spec.upstream_auth

FieldTypeRequiredDefaultDescription
group_prefixstringnoPrepended to every group the apiserver sees.

Constraints: maxLength: 64
issuer_keyobjectnoThe signing key of Issuer, also accepted in Passthrough so the apiserver can be prepared before the switch.
modestringno"Passthrough"Default: Passthrough, the behaviour without this block.

Values: "Passthrough", "FrontProxy", "Issuer", "TokenExchange"
token_exchangeobjectnoThe exchange of TokenExchange (RFC 0010); required in that mode and refused in the others.
user_prefixstringnoPrepended to the username the apiserver sees. No default: a prefix is a collision decision nobody else can take.

Constraints: maxLength: 64

Rules

  • user_prefix and group_prefix are required and non-empty in FrontProxy and Issuer — !has(self.mode) || self.mode == 'Passthrough' || self.mode == 'TokenExchange' || (has(self.user_prefix) && self.user_prefix != '' && has(self.group_prefix) && self.group_prefix != '')
  • issuer_key is for Issuer (or its preparation in Passthrough), not FrontProxy nor TokenExchange — !has(self.issuer_key) || !has(self.mode) || (self.mode != 'FrontProxy' && self.mode != 'TokenExchange')
  • mode Issuer requires issuer_key — !has(self.mode) || self.mode != 'Issuer' || has(self.issuer_key)
  • token_exchange is required in mode TokenExchange and refused in the other modes — (has(self.mode) && self.mode == 'TokenExchange') == has(self.token_exchange)
  • group_prefix: group_prefix must not start with system: nor carry a control character or an edge space — self.matches('[\\x00-\\x1f\\x7f]|^\\s|\\s$') == false && !self.lowerAscii().startsWith('system:')
  • user_prefix: user_prefix must not start with system: nor carry a control character or an edge space — self.matches('[\\x00-\\x1f\\x7f]|^\\s|\\s$') == false && !self.lowerAscii().startsWith('system:')

spec.upstream_auth.issuer_key

FieldTypeRequiredDefaultDescription
issuer_urlstringyesThe issuer the apiserver declares: <public proxy URL>/issuer/<ns>/<name>. Written here, never derived from the environment, so a redeploy or a restore cannot change it.

Constraints: maxLength: 2048
rotation_daysintegerno30Automatic key rotation, 1 to 365 days.

Constraints: minimum: 1, maximum: 365
token_ttl_secondsintegerno300Lifetime of an issued JWT, 60 to 900 seconds.

Constraints: minimum: 60, maximum: 900

Rules

  • issuer_url: issuer_url must be https — self.startsWith('https://')
  • issuer_url: issuer_url must be reachable by the apiserver, not a loopback address — !self.matches('^https://(localhost|127\\.|\\[::1\\])')

spec.upstream_auth.token_exchange

FieldTypeRequiredDefaultDescription
audiencestringno""The audience parameter of the exchange; empty: omitted.

Constraints: maxLength: 512
connector_idstringno""Dex only: the upstream connector that issued the proxy's token; empty: omitted.

Constraints: maxLength: 256
max_cache_secondsintegerno300The longest an exchanged token is reused, 0 to 3600 seconds; 0 exchanges on every request. Bounds how long a group removed at the IdP can go unseen by the apiserver.

Constraints: minimum: 0, maximum: 3600
scopestringno""The scope parameter of the exchange; empty: omitted.

Constraints: maxLength: 512

Rules

  • audience: audience must not carry CR, LF or & — !self.matches('[\\r\\n&]')
  • connector_id: connector_id must not carry CR, LF or & — !self.matches('[\\r\\n&]')
  • scope: scope must not carry CR, LF or & — !self.matches('[\\r\\n&]')

spec.virtual_apis[]

FieldTypeRequiredDefaultDescription
enabledbooleannotrueEnable or disable this virtual API Default: true
kindstringyesWhich virtual API to expose

Values: "OpenShiftProject"
list_fallback_tokenobjectnoA least-privilege bearer token (list only on namespaces, nothing else) used to enumerate candidate namespaces so OpenShiftProject's LIST projects can be filtered to what the caller can individually get, matching OpenShift's per-project visibility model.

Unset by default: LIST projects then behaves as a plain, unfiltered LIST namespaces. Only meaningful for VirtualApiKind::OpenShiftProject.

spec.virtual_apis[].list_fallback_token

FieldTypeRequiredDefaultDescription
Certstringone ofBase64 encoded cert
ConfigMapobjectone ofConfigmap
Insecurebooleanone ofDeprecated, use SystemRoots. Despite its name this never disabled TLS verification: it behaves exactly like SystemRoots. The value is ignored.
Secretobjectone ofUse a cert from a secret
SystemRootsbooleanone ofVerify the target with the system trust store (no custom CA). The value is ignored.

Exactly one of Secret, Cert, ConfigMap, SystemRoots, Insecure.

spec.virtual_apis[].list_fallback_token.ConfigMap

FieldTypeRequiredDefaultDescription
keystringyes
namestringyes
namespacestringno

spec.virtual_apis[].list_fallback_token.Secret

FieldTypeRequiredDefaultDescription
keystringyes
namestringyes
namespacestringno

Build a manifest

Answer the questions and fill in the fields: the YAML preview updates as you type, ready to copy or download and kubectl apply.

ProxyKubeApi Generator

Build a manifest from the live CRD schema, switch variants as needed, and export YAML when the validation panel is clean.

Errors
4
Cert
Secret
Service
KubernetesService

Preset examples

Start from a common setup, then fine-tune the schema-driven fields below.

MetadataRequired2 fields

Resource identity fields are kept outside the CRD-driven spec so you can reuse presets across namespaces.

SpecRequired11 fields
CertRequiredVariant: Secret

Certificate for the Kubernetes API

SecretRequired3 fields

Use a cert from a secret

Namespace is optional.
ServiceRequiredVariant: KubernetesService

Service to expose the proxy

Kubernetes ServiceRequired4 fields

Kubernetes service

Namespace is optional.
Port is optional.
Port name is optional.
Auth config is optional.
Client cert is optional.
Dashboard group is optional.
Enabled is optional.
Expose via dashboard is optional.
Proxy group is optional.
Security config is optional.
Upstream auth is optional.
Virtual apis is optional.
Tip: sections are collapsed by default. Expand only what you need.

Validation

  • metadata.name is required
  • spec.cert.Secret.key cannot be empty
  • spec.cert.Secret.name cannot be empty
  • spec.service.KubernetesService.name cannot be empty

Generated YAML

Preview updates live as you edit the form. Empty optional values are omitted from the manifest.

apiVersion: weebo.si.rs/v1
kind: ProxyKubeApi
metadata:
  name: ""
  namespace: default

On this page

ExampleSpec fieldsspec.auth_configspec.auth_config.jwt[]spec.auth_config.jwt[].claim_mappingsspec.auth_config.jwt[].claim_mappings.extra[]spec.auth_config.jwt[].claim_mappings.groupsspec.auth_config.jwt[].claim_mappings.uidspec.auth_config.jwt[].claim_mappings.usernamespec.auth_config.jwt[].claim_validation_rules[]spec.auth_config.jwt[].issuerspec.auth_config.jwt[].user_validation_rules[]spec.auth_config.oidc_providerspec.auth_config.oidc_provider.claim_mappingsspec.auth_config.oidc_provider.claim_mappings.extra[]spec.auth_config.oidc_provider.claim_mappings.groupsspec.auth_config.oidc_provider.claim_mappings.uidspec.auth_config.oidc_provider.claim_mappings.usernamespec.auth_config.oidc_provider.config_fromspec.auth_config.oidc_provider.config_from.Secretspec.certspec.cert.ConfigMapspec.cert.Secretspec.client_certspec.client_cert.certspec.client_cert.cert.ConfigMapspec.client_cert.cert.Secretspec.client_cert.keyspec.client_cert.key.ConfigMapspec.client_cert.key.Secretspec.security_configspec.security_config.allowed_resources[]spec.security_config.allowed_resources[].Crdspec.security_config.allowed_resources[].Crd.execspec.security_config.allowed_resources[].Crd.namespacespec.security_config.allowed_resources[].Crd.namespace.rule_kindspec.security_config.allowed_resources[].Pathspec.security_config.allowed_resources[].Path.execspec.security_config.allowed_ressources[]spec.security_config.allowed_ressources[].Crdspec.security_config.allowed_ressources[].Crd.execspec.security_config.allowed_ressources[].Crd.namespacespec.security_config.allowed_ressources[].Crd.namespace.rule_kindspec.security_config.allowed_ressources[].Pathspec.security_config.allowed_ressources[].Path.execspec.security_config.fail2login_equal_banspec.security_config.per_user_group_rate_limiting[]spec.security_config.rate_limitingspec.servicespec.service.Agentspec.service.Agent.bootstrapspec.service.Agent.bootstrap.ExternalCAspec.service.Agent.bootstrap.ExternalCA.ca_refspec.service.Agent.bootstrap.ExternalCA.ca_ref.ConfigMapspec.service.Agent.bootstrap.ExternalCA.ca_ref.Secretspec.service.Agent.bootstrap.OneShotTokenspec.service.Agent.bootstrap.ServiceAccountTokenspec.service.Agent.bootstrap.ServiceAccountToken.jwks_fromspec.service.Agent.bootstrap.ServiceAccountToken.jwks_from.ConfigMapspec.service.Agent.bootstrap.ServiceAccountToken.jwks_from.Secretspec.service.Agent.limitsspec.service.ExternalServicespec.service.KubernetesServicespec.upstream_authspec.upstream_auth.issuer_keyspec.upstream_auth.token_exchangespec.virtual_apis[]spec.virtual_apis[].list_fallback_tokenspec.virtual_apis[].list_fallback_token.ConfigMapspec.virtual_apis[].list_fallback_token.SecretBuild a manifest