Octelium supports a Kubernetes mode by setting the Service mode to KUBERNETES
. This mode provides:
- Secretless access for authorized Users to Kubernetes cluster APIs.
- Application-layer aware access control where you, for example, allow certain Users to access certain Kubernetes resources in certain namespaces under certain conditions.
- Application-layer aware dynamic configuration such as routing to different upstreams and/or credentials (read more here).
- Application-layer aware visibility where
Secretless Access
In order to provide secretless access to KUBERNETES
Services, you need to set the credentials required for the authentication to the upstream Kubernetes cluster. There are three authentication methods: Kubeconfig files, client certificates and bearer tokens.
Secretless access enables you to provide secretless access for authorized Users to KUBERNETES
-based Service without having to issue and distribute Kubeconfigs or access tokens to Users.
Kubeconfig
First, you need to create a Secret to store the content of the upstream's kubeconfig (read more here) as follows:
octeliumctl create secret --file /PATH/TO/KUBECONFIG kubeconfig-k8s1
Now, you define your Service as follows:
1kind: Service2metadata:3name: k8s14spec:5mode: KUBERNETES6config:7upstream:8url: https://k8s-cluster.example.com:64439kubernetes:10kubeconfig:11fromSecret: kubeconfig-k8s1
You can also choose a specific Kubeconfig context if you have more than one as follows:
1kind: Service2metadata:3name: k8s14spec:5mode: KUBERNETES6config:7upstream:8url: https://k8s-cluster.example.com:64439kubernetes:10kubeconfig:11context: ctx-abcd12fromSecret: kubeconfig-k8s1
Client Certificate
You can also manually set the client certificate credentials without using a kubeconfig. First, you need to create a Secret to store the content of the upstream's client PEM private key (read more here) as follows:
octeliumctl create secret --file /PATH/TO/CLIENT_PRIVATE_KEY.PEM k8s-client-cert
Now, you define your Service as follows:
1kind: Service2metadata:3name: k8s14spec:5mode: KUBERNETES6config:7upstream:8url: https://k8s-cluster.example.com:64439kubernetes:10clientCertificate:11fromSecret: k8s-client-cert12trustedCAs:13- |14-----BEGIN CERTIFICATE-----15MIIDBTCCAe2gAwIBAgIIf811HQrTMBAwDQYJKoZIhvcNAQELBQAwFTETMBEGA1UE16AxMKa3ViZXJuZXRlczAeFw0yNDA4MTkxMTEzNThaFw0zNDA4MTcxMTE4NThaMBUx17EzARBgNVBAMTCmt1YmVybmV0ZXMwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK18AoIBAQCg/Bw/rDlvZE6If1aVGfU324owXgLWsaI8MXThedmauhEp1xSPK9gtPaVR19omG6JwAgyln5YyFWSYDSLn2Pb/jBVPWslkdnTGfd3cDPe22+dpQinF1WsBylNuji20p00gst4rUl8bsxL/9a+3fcl4FBBUsyc6jQGBzHeH8Ilj+pUizIX1dj3oN97h1qiQ21VB0OiX/UNn/BePfoIzPBFtAqBQMKosLQ4aoVru0J4xDf4upDAN90bjWYiuxqMhXX22N49PZVZS8hw0k3zlTjOXxkrcQNCoPOA16a+gHbq8klUVhPoiwEIDpvu9QQ61Ppp/23gUIG98XviG6uPuRNa11zaRrFGcVJAgMBAAGjWTBXMA4GA1UdDwEB/wQEAwICpDAP24BgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBTVR79cj39ezb0jHsS13gPBc1VMzTAV25BgNVHREEDjAMggprdWJlcm5ldGVzMA0GCSqGSIb3DQEBCwUAA4IBAQA9K9tQlqW426vVPdWHb1LngRmvafgtrSN5K8m0yj3cCW3wOAms/zLN7QF6L5x9BwHCIzub3mbJUa27QG+Hgs071q8UdBjWRtosh30L7LEpV0UZkLNPYjzFtbA+OVZH/htfijfAp68xnQ1E28E3nZah06C59InOZHC5zbNdybhbiyVIr+0zbGFbHWrA19tIakM5o34uEnC2QEnkQu29CKu1lpmacjJMiztGgEIq9GSc67hYdrHiy3oThkno/jdeBETCFeWB4TDuUSUIefAp304MjEh8mhH3An8HEmXtfpCxXc5HaFf3gavakAzwshe+zBs5L4CS7/IZvoOzW8P3MN31tGKzn7JqDH2O32-----END CERTIFICATE-----
Bearer Token
You can also manually set a bearer token if required by the upstream cluster. First, you need to create a Secret to store the content of the upstream's bearer token (read more here) as follows:
octeliumctl create secret bearer-token-k8s1
Now, you define your Service as follows:
1kind: Service2metadata:3name: k8s14spec:5mode: KUBERNETES6config:7upstream:8url: https://k8s-cluster.example.com:64439kubernetes:10bearerToken:11fromSecret: bearer-token-k8s112trustedCAs:13- |14-----BEGIN CERTIFICATE-----15MIIDBTCCAe2gAwIBAgIIf811HQrTMBAwDQYJKoZIhvcNAQELBQAwFTETMBEGA1UE16AxMKa3ViZXJuZXRlczAeFw0yNDA4MTkxMTEzNThaFw0zNDA4MTcxMTE4NThaMBUx17EzARBgNVBAMTCmt1YmVybmV0ZXMwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK18AoIBAQCg/Bw/rDlvZE6If1aVGfU324owXgLWsaI8MXThedmauhEp1xSPK9gtPaVR19omG6JwAgyln5YyFWSYDSLn2Pb/jBVPWslkdnTGfd3cDPe22+dpQinF1WsBylNuji20p00gst4rUl8bsxL/9a+3fcl4FBBUsyc6jQGBzHeH8Ilj+pUizIX1dj3oN97h1qiQ21VB0OiX/UNn/BePfoIzPBFtAqBQMKosLQ4aoVru0J4xDf4upDAN90bjWYiuxqMhXX22N49PZVZS8hw0k3zlTjOXxkrcQNCoPOA16a+gHbq8klUVhPoiwEIDpvu9QQ61Ppp/23gUIG98XviG6uPuRNa11zaRrFGcVJAgMBAAGjWTBXMA4GA1UdDwEB/wQEAwICpDAP24BgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBTVR79cj39ezb0jHsS13gPBc1VMzTAV25BgNVHREEDjAMggprdWJlcm5ldGVzMA0GCSqGSIb3DQEBCwUAA4IBAQA9K9tQlqW426vVPdWHb1LngRmvafgtrSN5K8m0yj3cCW3wOAms/zLN7QF6L5x9BwHCIzub3mbJUa27QG+Hgs071q8UdBjWRtosh30L7LEpV0UZkLNPYjzFtbA+OVZH/htfijfAp68xnQ1E28E3nZah06C59InOZHC5zbNdybhbiyVIr+0zbGFbHWrA19tIakM5o34uEnC2QEnkQu29CKu1lpmacjJMiztGgEIq9GSc67hYdrHiy3oThkno/jdeBETCFeWB4TDuUSUIefAp304MjEh8mhH3An8HEmXtfpCxXc5HaFf3gavakAzwshe+zBs5L4CS7/IZvoOzW8P3MN31tGKzn7JqDH2O32-----END CERTIFICATE-----
Access Control
You can control access based on the Kubernetes HTTP request information. Such information are stored in ctx.request.kubernetes
where it contains information such as the resource, namespace, verb, API group, API version and sub-resource if available. Additionally all the HTTP related information that are exposed in the HTTP
mode are also exposed in the variable ctx.request.kubernetes.http
. Here is a detailed example of a inline Policy that controls access based on Kubernetes-specific information:
1kind: Service2metadata:3name: svc14spec:5mode: KUBERNETES6config:7upstream:8url: https://k8s-cluster.example.com:64439kubernetes:10kubeconfig:11context: ctx-abcd12fromSecret: kubeconfig-k8s113authorization:14inlinePolicies:15- spec:16rules:17- effect: ALLOW18condition:19any:20of:21- match: ctx.request.kubernetes.apiGroup in ["k8s.cni.cncf.io", "events.k8s.io"]22- match: ctx.request.kubernetes.apiVersion == "v1"23- match: ctx.request.kubernetes.verb in ["create", "get", "list", "update", "watch", "patch", "delete"]24- match: ctx.request.kubernetes.namespace == "production"25- match: ctx.request.kubernetes.resource in ["pods", "services"]26- match: ctx.request.kubernetes.name == "pod-abcdef"27- match: ctx.request.kubernetes.subresource == "status"28- match: ctx.request.kubernetes.http.headers["x-custom-header"] == "this-value"
Visibility
The Service emits access logs in real time to the audit collector. Each log provides Kubernetes application-layer aware information about the request such as the request's namespace, resource, sub-resource, API version and API prefix. Additionally, all underlying HTTP-based information are also included. Here is an example:
1{2"apiVersion": "core/v1",3"kind": "AccessLog",4"metadata": {5"id": "d7vr-ndfa-w35tl5g9ft3h3u3dcwtnfpja-wfi9-2d4q",6"createdAt": "2025-09-10T22:26:17.479478294Z",7"actorRef": {8"apiVersion": "core/v1",9"kind": "Session",10"uid": "b1bc6aaa-df51-456d-aa37-b77377ea26f0",11"name": "root-1x09ce",12"resourceVersion": "019935ba-ee91-766b-a11e-968c67478387"13},14"targetRef": {15"apiVersion": "core/v1",16"kind": "Service",17"uid": "75a1a442-2e8b-48ea-9608-fa64cb74b506",18"name": "k8s1.default",19"resourceVersion": "019935bc-524e-73dc-8b96-c14b109380fd"20}21},22"entry": {23"common": {24"startedAt": "2025-09-10T22:26:17.216993779Z",25"endedAt": "2025-09-10T22:26:17.479481774Z",26"status": "ALLOWED",27"mode": "KUBERNETES",28"reason": {29"type": "POLICY_MATCH",30"details": {31"policyMatch": {32"inlinePolicy": {33"resourceRef": {34"apiVersion": "core/v1",35"kind": "Session",36"uid": "b1bc6aaa-df51-456d-aa37-b77377ea26f0",37"name": "root-1x09ce",38"resourceVersion": "019935ba-ee91-766b-a11e-968c67478387"39},40"name": "first-session-allow-all"41}42}43}44},45"sessionRef": {46"apiVersion": "core/v1",47"kind": "Session",48"uid": "b1bc6aaa-df51-456d-aa37-b77377ea26f0",49"name": "root-1x09ce",50"resourceVersion": "019935ba-ee91-766b-a11e-968c67478387"51},52"userRef": {53"apiVersion": "core/v1",54"kind": "User",55"uid": "d72a39da-6f1c-43f7-ad75-dbaf76111b10",56"name": "root",57"resourceVersion": "019934c7-3d99-7864-8ccf-abe9eadfe023"58},59"serviceRef": {60"apiVersion": "core/v1",61"kind": "Service",62"uid": "75a1a442-2e8b-48ea-9608-fa64cb74b506",63"name": "k8s1.default",64"resourceVersion": "019935bc-524e-73dc-8b96-c14b109380fd"65},66"namespaceRef": {67"apiVersion": "core/v1",68"kind": "Namespace",69"uid": "2073e6f7-24c2-49d2-b0df-e5cd3636d82c",70"name": "default",71"resourceVersion": "019934c7-6d7a-73cd-a510-dfadfdfa6682"72},73"regionRef": {74"apiVersion": "core/v1",75"kind": "Region",76"uid": "85477de2-67d3-48ed-bda7-6c914489badf",77"name": "default"78},79"isPublic": true80},81"info": {82"kubernetes": {83"http": {84"request": {85"path": "/api/v1/namespaces/octelium/pods",86"userAgent": "kubectl/v1.33.4 (linux/amd64) kubernetes/74cdb42",87"method": "GET",88"uri": "/api/v1/namespaces/octelium/pods?limit=500"89},90"response": {91"code": 200,92"bodyBytes": "25575",93"contentType": "application/json"94},95"httpVersion": "HTTP11"96},97"verb": "list",98"apiPrefix": "api",99"apiVersion": "v1",100"namespace": "octelium",101"resource": "pods"102}103}104}105}