Octelium supports a Kubernetes mode by setting the Service mode to KUBERNETES
. This mode provides:
- Secret-less 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
Secret-less Access
In order to provide secret-less 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.
Kubeconfig
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 manually set the client certificate credentials 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 as follows:
1kind: Service2metadata:3name: k8s14spec:5mode: KUBERNETES6config:7upstream:8url: https://k8s-cluster.example.com:64439kubernetes:10bearerToken:11fromSecret: kubeconfig-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"entry": {4"common": {5"endedAt": "2025-03-21T23:09:59.918594707Z",6"isAuthenticated": true,7"isAuthorized": true,8"namespaceRef": {9"apiVersion": "core/v1",10"kind": "Namespace",11"name": "default",12"resourceVersion": "0195b81d-43ec-7ba7-81f7-9b77202b3612",13"uid": "bff0768e-eec8-4044-a972-9f7af81b79d8"14},15"reason": {16"details": {17"policyMatch": {18"inlinePolicy": {19"name": "allow-all",20"resourceRef": {21"apiVersion": "core/v1",22"kind": "Group",23"name": "g1",24"resourceVersion": "0195b821-209b-76f7-bde9-b078e870621c",25"uid": "972e1808-7248-468a-ba7c-8da43d33c626"26}27}28}29},30"type": "POLICY_MATCH"31},32"regionRef": {33"apiVersion": "core/v1",34"kind": "Region",35"name": "default",36"uid": "6e233d12-2fd6-4670-a82d-8c712e1f7374"37},38"serviceRef": {39"apiVersion": "core/v1",40"kind": "Service",41"name": "k8s1.default",42"resourceVersion": "0195baf7-8941-78ce-a54b-52dbaa7b9a1d",43"uid": "6a2d8b0f-a0fe-4544-927f-7b3094bad578"44},45"sessionRef": {46"apiVersion": "core/v1",47"kind": "Session",48"name": "usr1-3pt93j",49"resourceVersion": "0195baf5-206b-7587-8c41-cf5766602111",50"uid": "e8bea7be-b858-4457-a8ee-a0cb7f6c91da"51},52"startedAt": "2025-03-21T23:09:59.871329180Z",53"userRef": {54"apiVersion": "core/v1",55"kind": "User",56"name": "usr1",57"resourceVersion": "0195b821-228d-79b2-92f3-4bb458944f97",58"uid": "e0f251b2-6952-4a46-a771-2712a45c47f5"59}60},61"info": {62"kubernetes": {63"apiPrefix": "api",64"apiVersion": "v1",65"http": {66"httpVersion": "HTTP11",67"request": {68"method": "GET",69"path": "/api/v1/namespaces/octelium/pods?limit=500",70"userAgent": "kubectl/v1.32.3 (linux/amd64) kubernetes/32cc146"71},72"response": {73"bodyBytes": "18559",74"code": 20075}76},77"namespace": "octelium",78"resource": "pods",79"verb": "list"80}81}82},83"kind": "AccessLog",84"metadata": {85"actorRef": {86"apiVersion": "core/v1",87"kind": "Session",88"name": "usr1-3pt93j",89"resourceVersion": "0195baf5-206b-7587-8c41-cf5766602111",90"uid": "e8bea7be-b858-4457-a8ee-a0cb7f6c91da"91},92"createdAt": "2025-03-21T23:09:59.918580014Z",93"id": "hg47-2zve-u8x5vh20ri132e2zptiv5jad-v1bx-51u7",94"targetRef": {95"apiVersion": "core/v1",96"kind": "Service",97"name": "k8s1.default",98"resourceVersion": "0195baf7-8941-78ce-a54b-52dbaa7b9a1d",99"uid": "6a2d8b0f-a0fe-4544-927f-7b3094bad578"100}101}102}