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 3 plugin types are supported:
- Lua scripts.
- Direct plugin that enables you to directly return a response without proceeding to the upstream.
- 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)26return27end2829if strings.hasPrefix(ctx.request.http.path, "/users") then30octelium.req.setPath("/users/"..ctx.user.metadata.uid)31end3233if strings.hasSuffix(ctx.request.http.path, ".php") then34octelium.req.setPath("/users/"..ctx.user.metadata.uid)35end3637if strings.len(body.someStrField) > 1000 then38local c = http.client()39c:setBaseURL("http://my-api.default.svc")40local req = c:request()41req:setBody(json.encode(ctx.user))42req:setHeader("X-User-Uid", ctx.user.metadata.uid)43local resp, err = req:post("/v1/check-user")44if err then45octelium.req.exit(500)46return47end4849if resp:code() == 200 then50local apiResp = json.decode(resp:body())51if not apiResp.userIsAllowed then52octelium.req.exit(403)53return54end55end56end57end5859function onResponse(ctx)60octelium.req.setResponseHeader("X-Session-Uid", ctx.session.metadata.uid)61octelium.req.deleteResponseHeader("X-Delete")62local resp = octelium.req.getResponseBody()63resp.email = ctx.user.spec.email64octelium.req.setResponseBody(json.encode(resp))65octelium.req.setStatusCode(209)66end
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.
Direct
The direct plugin enables you to directly return a response without proceeding to the upstream. This acts as a much more performant, yet less flexible, way compared to Lua and Envoy ext_proc plugins to, for example, drop unwanted requests that contain undesirable request paths, body content, etc.... Here is an example:
1kind: Service2metadata:3name: my-nginx4spec:5mode: WEB6isPublic: true7config:8upstream:9container:10image: nginx11port: 8012http:13plugins:14- name: drop-php15direct:16condition:17match: ctx.request.http.path.toLower().endsWith(".php")18body:19inline: This is not a PHP server!20statusCode: 40421headers:22X-Custom-Header: some-value23X-Another-Header: another-value
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)19return20end21end
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 with a /users
prefix:
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.path.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