Octelium already supports native ways to manipulate HTTP request/response headers, validate request JSON body and send direct responses back to the downstreams (read more here). However, in many real-world use cases, such native methods might be not flexible enough.
Octelium provides additional, more advanced "plugins" to manipulate HTTP requests and responses in arbitrarily complex ways that might include calling external SaaS HTTP-based APIs and databases, manipulating JSON body content of requests and responses, etc.... Such plugins can be especially useful for many use cases that can be applied to ZTNA/BeyondCorp, API gateways, AI gateways, MCP gateways, etc.... including:
- Request/response sanitization and manipulation for headers as well as body content, including JSON, which can be used for API/AI gateways.
- Custom identity-based caching, including semantic caching for AI gateways.
- DLP for ZTNA/BeyondCorp.
- Implementing AI guardrails, including calling SaaS APIs to verify prompts.
- Custom identity-based rate limiting.
Currently 2 plugin types are supported: Lua scripts and Envoy's ext_proc filter. Octelium enables you to stack one or more plugins from the same or different types and invoke them at certain phases of the request, as well as to conditionally invoke them via policy-as-code rules on a per-request basis. Plugins are invoked in the same order they are defined.
Lua
Lua plugin invokes a Lua script that includes two functions: onRequest
that is invoked upon receiving the request before proxying it to the upstream, and onResponse
that is invoked upon receiving the response from the upstream. Here is a detailed example:
1kind: Service2metadata:3name: svc14spec:5mode: HTTP6config:7upstream:8url: https://api.example.com9http:10plugins:11- name: my-lua-script12lua:13inline: |14function onRequest(ctx)15octelium.req.setRequestHeader("X-User-Uid", ctx.user.metadata.uid)16octelium.req.deleteRequestHeader("X-Delete")17octelium.req.setQueryParam("user", ctx.user.metadata.name)18octelium.req.deleteQueryParam("param1")19local body = json.decode(octelium.req.getRequestBody())20if strings.contains(strings.toLower(strings.trim(base64.decode(body.strField))), "privileged") then21local res = {22error = "You are not allowed"23}24octelium.req.setResponseBody(json.encode(res))25octelium.req.exit(403)26end2728if strings.hasPrefix(ctx.request.http.path, "/users") then29octelium.req.setPath("/users/"..ctx.user.metadata.uid)30end3132if strings.hasSuffix(ctx.request.http.path, ".php") then33octelium.req.setPath("/users/"..ctx.user.metadata.uid)34end3536if strings.len(body.someStrField) > 1000 then37local c = http.client()38c:setBaseURL("http://my-api.default.svc")39local req = c:request()40req:setBody(json.encode(ctx.user))41req:setHeader("X-User-Uid", ctx.user.metadata.uid)42local resp, err = req:post("/v1/check-user")43if err then44octelium.exit(500)45end4647if resp:code() == 200 then48local apiResp = json.decode(resp:body())49if not apiResp.userIsAllowed then50octelium.exit(403)51end52end53end54end5556function onResponse(ctx)57octelium.req.setResponseHeader("X-Session-Uid", ctx.session.metadata.uid)58octelium.req.deleteResponseHeader("X-Delete")59local resp = octelium.req.getResponseBody()60resp.email = ctx.user.spec.email61octelium.req.setResponseBody(json.encode(resp))62octelium.req.setStatusCode(209)63end
Note that you do not have to define both onRequest
and onResponse
in every Lua script. If any of these functions does not exist, then it is silently skipped.
Envoy Ext Proc
Octelium also supports Envoy's ext_proc. In this plugin type, Octelium's identity-aware proxy, Vigil, behaves like an Envoy instance and acts as an ext_proc gRPC client that sends ProcessingRequest to your ext_proc gRPC compliant server and waits for ProcessingResponse on every request. Here is an example:
1kind: Service2metadata:3name: svc14spec:5mode: HTTP6config:7upstream:8url: https://api.example.com9http:10plugins:11- name: my-ext-proc-svc12extProc:13address: ext-proc.default.svc:8080
Phase
By default, plugins are invoked after the authentication and authorization processes are done. However, you might choose to invoke a plugin before authentication/authorization by explicity setting a phase
value for that plugin. Currently the values available are: POST_AUTH
which is the default behavior when not explicitly set, and PRE_AUTH
to force a pre-auth invocation. PRE_AUTH
plugins might be useful to do pre-authentication manipulation or even completely dropping the requests before proceeding further to the authentication/authorization process.
Note that PRE_AUTH
plugins must be in the default
Service configuration. In other words, you can not have a PRE_AUTH
in a named dynamic config (read more here). Here is an example:
1kind: Service2metadata:3name: svc14spec:5mode: HTTP6config:7upstream:8url: https://api.example.com9http:10plugins:11- name: check-body-size12phase: PRE_AUTH13lua:14inline: |15function onRequest(ctx)16local body = octelium.req.getRequestBody()17if strings.len(body) > 10000 then18octelium.req.exit(400)19end20end
Rules
By default, a plugin in the chosen Service config is invoked on all requests. You can, however, change that behavior and set per-request conditions for the plugin invocation. Here is an example that only invokes the plugin for paths whose prefix equals to /users
:
1kind: Service2metadata:3name: svc14spec:5mode: HTTP6config:7upstream:8url: https://api.example.com9http:10plugins:11- name: my-ext-proc-svc12rules:13- condition:14match: ctx.request.http.request.startsWith("/users")15effect: ENFORCE16- condition:17matchAny: true18effect: IGNORE19extProc:20address: ext-proc.default.svc:8080
Disabling a Plugin
You can disable plugin as follows:
1kind: Service2metadata:3name: svc14spec:5mode: HTTP6config:7upstream:8url: https://api.example.com9http:10plugins:11- name: my-plugin12isDisabled: true13lua:14inline: |15function onRequest(ctx)16end