Octelium supports the HTTP
mode for any HTTP-based upstreams (e.g. HTTP APIs, web-apps, websockets, gRPC, etc...). You can create an HTTP
Service simply as follows:
1kind: Service2metadata:3name: svc14spec:5mode: HTTP6port: 80807config:8upstream:9url: https://example.com
Access Control
You can control access based on the HTTP request information. Such information are stored in ctx.request.http
where it contains the method, path, headers map as well as the scheme values. Here is a detailed example of a inline Policy that controls access based on HTTP-specific information:
1kind: Service2metadata:3name: svc14spec:5mode: HTTP6port: 80807config:8upstream:9url: https://example.com10authorization:11inlinePolicies:12- spec:13rules:14- effect: ALLOW15condition:16all:17of:18- match: ctx.request.http.method in ["GET", "POST", "PUT", "DELETE"]19- match: ctx.request.http.path.startsWith("/apis")20- match: ctx.request.http.headers["x-custom-header"] == "this-value"21- match: ctx.request.http.scheme == "http"
Secret-less Access
Octelium is capable of supporting secret-less access to protected upstreams (read more here) by injecting HTTP-based credentials such as API keys, which are stored and represented in the Cluster as Secrets (read more about Secrets here), on-the-fly to the protected upstream. This eliminates the need to share, mange, distribute and store such typically long-lived, privileged and prone-to-misuse credentials. Octelium supports the following authentication methods:
- Basic authentication
- Bearer authentication
- Custom header authentication
- OAuth2 client credentials flow
Basic Authentication
Here is an example of basic authentication:
1kind: Service2metadata:3name: svc14spec:5mode: HTTP6port: 807config:8upstream:9url: https://api.example.com10http:11auth:12basic:13username: user114password:15fromSecret: password-secret
Bearer Authentication
Here is an example of bearer authentication:
1kind: Service2metadata:3name: svc14spec:5mode: HTTP6port: 807config:8upstream:9url: https://api.example.com10http:11auth:12bearer:13fromSecret: apikey1
Authentication via a Custom Header
You can also set your Secret value to a custom request header to the upstream. Here is an example:
1kind: Service2metadata:3name: svc14spec:5mode: HTTP6port: 807config:8upstream:9url: https://api.example.com10http:11auth:12custom:13header: X-AUTH-114value:15fromSecret: apikey1
OAuth2 Client Credentials
You can also use OAuth2 client credentials authentication flow (read more here). Here is an example:
1kind: Service2metadata:3name: svc14spec:5mode: HTTP6port: 807config:8upstream:9url: https://api.example.com10http:11auth:12oauth2ClientCredentials:13clientID: X-AUTH-114clientSecret:15fromSecret: apikey116tokenURL: https://example.com/auth/v117scopes: ["scope1", "scope2"]
HTTP 2/0
Listening on HTTP 2/0
By default, the Service listens to HTTP 1.1 connections unless TLS is enabled (read more here). However you can force the Service to accept HTTP 2.0 connections as follows:
1kind: Service2metadata:3name: svc14spec:5mode: HTTP6port: 807config:8upstream:9url: https://example.com10http:11listenHTTP2: true
HTTP 2/0 Upstream
A Service by default assumes that the upstream accepts HTTP 1.1 connections unless it's serving TLS connections. You can force the Service to forward the requests over HTTP 2/0 as follows:
1kind: Service2metadata:3name: svc14spec:5mode: HTTP6port: 807config:8upstream:9url: https://example.com10http:11isUpstreamHTTP2: true
Header Manipulation
You can easily manipulate both request and response HTTP headers as follows:
1kind: Service2metadata:3name: svc14spec:5mode: HTTP6port: 807config:8upstream:9url: https://example.com10http:11header:12addRequestHeaders:13- key: X-HEADER-114value: VALUE-115- key: X-HEADER-216value: VALUE-217removeRequestHeaders:18- X-HEADER-319- X-HEADER-420addResponseHeaders:21- key: X-HEADER-522value: VALUE-523- key: X-HEADER-624value: VALUE-625removeResponseHeaders:26- X-HEADER-727- X-HEADER-8
Path Manipulation
You can add a prefix to the request's path as follows:
1kind: Service2metadata:3name: svc14spec:5mode: HTTP6port: 807config:8upstream:9url: https://example.com10http:11path:12addPrefix: /prefix
Now a request with the path /v1
will become /prefix/v1
.
And you can also remove a prefix from the path as follows:
1kind: Service2metadata:3name: svc14spec:5mode: HTTP6port: 807config:8upstream:9url: https://example.com10http:11path:12removePrefix: /prefix
Now a request with the path /prefix/v1
will become /v1
.
And you can replace a prefix by another by combining both removePrefix
and addPrefix
as follows:
1kind: Service2metadata:3name: svc14spec:5mode: HTTP6port: 807config:8upstream:9url: https://example.com10http:11path:12removePrefix: /v113addPrefix: /v2
Now a request with the path /v1/path
will become /v2/path
.
Request Body
Octelium enables you to buffer the entire request body before forwarding it to the upstream for it to be checked and used in your access Policies (read more here) and dynamic configuration (read more here).
1kind: Service2metadata:3name: svc14spec:5mode: HTTP6port: 807config:8upstream:9url: https://example.com10http:11enableRequestBuffering: true
This will include your entire body as base64 string inside the ctx.request.http.body
(read more here) field used by your Policies and dynamic configuration rules.
Request Body Maximum Size
You can set the limit on the maximum number of bytes for a request body as follows:
1kind: Service2metadata:3name: svc14spec:5mode: HTTP6port: 807config:8upstream:9url: https://example.com10http:11enableRequestBuffering: true12body:13maxRequestSize: 100000
JSON Request Body
In many cases such as in REST APIs, the request body is represented by a JSON format. You can enable the body mode as JSON
to verify that the request body is a valid JSON as follows:
1kind: Service2metadata:3name: svc14spec:5mode: HTTP6port: 807config:8upstream:9url: https://example.com10http:11enableRequestBuffering: true12body:13mode: JSON
The JSON
modes lets you to directly use the fields of the JSON structure in your Policies and dynamic configuration rules inside the ctx.request.http.bodyMap
field. Here is an example, for a request body with a schema that looks as following:
1{2"key1": "value1",3"key2": 5,4"key3:": {5"key4": "value4"6}7}
You can set your access Policy rules as follows:
1kind: Service2metadata:3name: svc14spec:5mode: HTTP6port: 80807config:8upstream:9url: https://example.com10authorization:11inlinePolicies:12- spec:13rules:14- effect: ALLOW15condition:16all:17of:18- match: ctx.request.http.bodyMap.key1 == "value1"19- match: ctx.request.http.bodyMap.key2 > 220- match: ctx.request.http.bodyMap.key3.key4 == "value4"
Cross-Origin Resource Sharing (CORS)
You can also enforce CORS rules as follows:
1kind: Service2metadata:3name: svc14spec:5mode: HTTP6port: 807config:8upstream:9url: https://example.com10http:11cors:12allowMethods: "POST, GET, OPTIONS"13allowHeaders: "X-PINGOTHER, Content-Type"14maxAge: "86400"
Dynamic Configuration
You can use dynamic configuration in order to, for example, route to different upstreams and/or setting different different upstream credentials, set different request/response headers, etc... depending on the request's context (read more about dynamic configuration here). Here is a simple example for an API gateway (read more here):
1kind: Service2metadata:3name: my-api4spec:5mode: HTTP6isPublic: true7dynamicConfig:8configs:9- name: v110upstream:11url: https://apiv1.example.com12http:13path:14removePrefix: /v115- name: v216upstream:17url: https://apiv2.example.com18http:19path:20removePrefix: /v221rules:22- condition:23match: ctx.request.http.path.startsWith("/v1")24configName: v125- condition:26match: ctx.request.http.path.startsWith("/v2")27configName: v2
Visibility
The Service emits access logs in real time to the audit collector. Each log provides application-layer aware information about the request such as the request path, method, etc... as well as the response code and body size. Here is an example:
1{2"apiVersion": "core/v1",3"entry": {4"common": {5"endedAt": "2025-03-21T20:20:57.970432905Z",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": "portal.default",42"resourceVersion": "0195b820-357c-7753-a9fb-240102cb642c",43"uid": "f1e03fcc-4a8c-45e5-9771-59a0c5924627"44},45"sessionRef": {46"apiVersion": "core/v1",47"kind": "Session",48"name": "usr1-ver9fi",49"resourceVersion": "0195ba26-805a-76ff-966f-a8ee4158f3d5",50"uid": "e04bb910-9bb7-483f-a3fe-6c0610b7e358"51},52"startedAt": "2025-03-21T20:20:57.917525056Z",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"http": {63"httpVersion": "HTTP11",64"request": {65"method": "GET",66"path": "/services",67"userAgent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/134.0.0.0 Safari/537.36"68},69"response": {70"bodyBytes": "471",71"code": 20072}73}74}75},76"kind": "AccessLog",77"metadata": {78"actorRef": {79"apiVersion": "core/v1",80"kind": "Session",81"name": "usr1-ver9fi",82"resourceVersion": "0195ba26-805a-76ff-966f-a8ee4158f3d5",83"uid": "e04bb910-9bb7-483f-a3fe-6c0610b7e358"84},85"createdAt": "2025-03-21T20:20:57.970426960Z",86"id": "qx24-gtvf-0ow6d4fc4tyrskc2eco6rafs-sne8-xi11",87"targetRef": {88"apiVersion": "core/v1",89"kind": "Service",90"name": "portal.default",91"resourceVersion": "0195b820-357c-7753-a9fb-240102cb642c",92"uid": "f1e03fcc-4a8c-45e5-9771-59a0c5924627"93}94}95}
gRPC Mode
In the GRPC
mode, the Service automatically listens over HTTP 2/0 as required by gRPC protocol. Furthermore, authorization and visibility take advantage of decoding the requests as gRPC requests.
You can control access based on the gRPC request information. Such information are stored in ctx.request.grpc
where it contains the service, method and package values. Additionally all the HTTP related information that are exposed in the HTTP
mode are also exposed in the variable ctx.request.grpc.http
. Here is a detailed example of a inline Policy that controls access based on gRPC-specific information:
1kind: Service2metadata:3name: svc14spec:5mode: GRPC6port: 80807config:8upstream:9url: https://my-grpc-api.example.com10authorization:11inlinePolicies:12- spec:13rules:14- effect: ALLOW15condition:16all:17of:18- match: ctx.request.grpc.method == "GetUser"19- match: ctx.request.grpc.service == "MainService"20- match: ctx.request.grpc.package == "octelium.api.main.core.v1"21- match: ctx.request.grpc.serviceFullName == "octelium.api.main.core.v1.MainService"22- match: ctx.request.grpc.http.path.startsWith("/octelium.api")23- match: ctx.request.grpc.http.headers["x-custom-header"] == "this-value"
As for visibility, the access log contains gRPC-specific information such as the package, service and method in addition to the underlying HTTP information. Here is an example:
1{2"apiVersion": "core/v1",3"entry": {4"common": {5"endedAt": "2025-03-21T20:18:56.435354869Z",6"isAuthenticated": true,7"isAuthorized": true,8"namespaceRef": {9"apiVersion": "core/v1",10"kind": "Namespace",11"name": "octelium-api",12"resourceVersion": "0195b81d-43f8-7197-a231-eff3c78c072b",13"uid": "879cf77c-e94e-4303-a8e6-78a816496cbe"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": "default.octelium-api",42"resourceVersion": "0195b820-35a1-78bd-bf79-e5149b097a70",43"uid": "8a39db66-5d1f-4f8e-b51c-ee7202e06462"44},45"sessionRef": {46"apiVersion": "core/v1",47"kind": "Session",48"name": "usr1-3pt93j",49"resourceVersion": "0195ba59-7cae-7487-b96f-48b1440c7749",50"uid": "e8bea7be-b858-4457-a8ee-a0cb7f6c91da"51},52"startedAt": "2025-03-21T20:18:56.393205783Z",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"grpc": {63"http": {64"httpVersion": "HTTP2",65"request": {66"method": "POST",67"path": "/octelium.api.main.core.v1.MainService/ListService",68"userAgent": "grpc-go/1.69.2"69},70"response": {71"bodyBytes": "3292",72"code": 20073}74},75"method": "ListService",76"package": "octelium.api.main.core.v1",77"service": "MainService",78"serviceFullName": "octelium.api.main.core.v1.MainService"79}80}81},82"kind": "AccessLog",83"metadata": {84"actorRef": {85"apiVersion": "core/v1",86"kind": "Session",87"name": "usr1-3pt93j",88"resourceVersion": "0195ba59-7cae-7487-b96f-48b1440c7749",89"uid": "e8bea7be-b858-4457-a8ee-a0cb7f6c91da"90},91"createdAt": "2025-03-21T20:18:56.435340690Z",92"id": "rwa9-yu4j-zoxzkgh25oegh4ebyyctxi16-kd3p-4z3b",93"targetRef": {94"apiVersion": "core/v1",95"kind": "Service",96"name": "default.octelium-api",97"resourceVersion": "0195b820-35a1-78bd-bf79-e5149b097a70",98"uid": "8a39db66-5d1f-4f8e-b51c-ee7202e06462"99}100}101}
Web App Mode
You can use the WEB
mode to denote that the Service is a web application that can be accessed by HUMAN
Users via their browsers. This mode enables the web Portal to include a "Visit" button to open the Service homepage in a new tab. Here is an example:
1kind: Service2metadata:3name: grafana14spec:5mode: WEB6port: 807isPublic: true8config:9upstream:10url: https://grafana.mycluster.svc
Without explicitly using the WEB
mode, no "Visit" button will be shown in the Portal and Users will have to manually type the https://grafana1.<DOMAIN>
in their browsers to visit the Service homepage.