Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/chukmunnlee/caddy-openapi
A Caddy module to validate HTTP request and response against a OpenAPI spec (V3) file
https://github.com/chukmunnlee/caddy-openapi
caddy golang-package openapi3
Last synced: 2 months ago
JSON representation
A Caddy module to validate HTTP request and response against a OpenAPI spec (V3) file
- Host: GitHub
- URL: https://github.com/chukmunnlee/caddy-openapi
- Owner: chukmunnlee
- License: apache-2.0
- Created: 2020-08-19T08:42:14.000Z (over 4 years ago)
- Default Branch: master
- Last Pushed: 2024-06-05T00:13:24.000Z (7 months ago)
- Last Synced: 2024-08-02T15:48:19.249Z (5 months ago)
- Topics: caddy, golang-package, openapi3
- Language: Go
- Homepage:
- Size: 304 KB
- Stars: 22
- Watchers: 3
- Forks: 7
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- License: LICENSE.txt
Awesome Lists containing this project
README
# caddy-openapi
This middleware validates HTTP request and response against a OpenAPI V3 Specification file
## Installation
Build caddy with caddy-openapi, run `make`. This will build for Linux, Windows and OSX.
You can also build with `xcaddy`
```
xcaddy build \
--with github.com/chukmunnlee/caddy-openapi
```Tested with `go version go1.22.3 linux/amd64` on `Linux 6.5.0-35-generic #35~22.04.1-Ubuntu SMP PREEMPT_DYNAMIC x86_64 x86_64 x86_64 GNU/Linux`
## Usage
### Caddyfile
Load `examples/customer/customer.yaml` file with defaults
```
:8080 {
route /api {
openapi ./examples/customer/customer.yaml
}
}
```One with all the options
```
:8080 {
route /api {
openapi {
spec ./examples/customer/customer.yaml
fall_through
log_error
}
}
}
```Reports any errors as a `{openapi.error}` [placeholder](https://caddyserver.com/docs/caddyfile/concepts#placeholders) which can be used in other [directives](https://caddyserver.com/docs/caddyfile/directives) like [`respond`](https://caddyserver.com/docs/caddyfile/directives/respond)
| Fields | Description |
|--------------------------|-------------|
| `spec ` | The OpenAPI3 YAML file. This attribute is a mandatory |
| `policy_bundle ` | [OPA](https://www.openpolicyagent.org/) policy bundle created with `opa build`. |
| `fall_through` | Toggles fall through when the request does do match the provided OpenAPI spec. Default is `false` |
| `validate_servers` | Enable server validation. Accepts `true`, `false` or just the directive which enables validation. Default is `true`. |
| `log_error` | Toggles error logging. Default is `false` |
| `check` | Enable validation of the request parameters; include one or more of the following directives in the body:`req_params`, `req_body` and `resp_body`. `resp_body` only validates `application/json` payload. Note that validating the request body will implicitly set `req_params` |Errors are reported in the following [placeholders](https://caddyserver.com/docs/caddyfile/concepts#placeholders). You can use them in other [directives](https://caddyserver.com/docs/caddyfile/directives) like [`respond`](https://caddyserver.com/docs/caddyfile/directives/respond)
| Placeholders | Description |
|--------------------------|-------------|
| `openapi.error` | Description of the error |
| `openapi.status_code` | Suggested status code |
| `openapi.response_error` | Resonse error |## Example
The following example validates all request, including query string as well as payloads, to `localhost:8080/api`
against the `./examples/customer/customer.yaml` file. Any non compliant request will be logged to Caddy's console.
Respond to the client with the error `{openapi.error}`.```
:8080 {@api {
path /api/*
}reverse_proxy @api {
to localhost:3000
}route @api {
openapi {
spec ./examples/customer/customer.yaml
policy_bundle ./examples/policy/bundle.tar.gz
check {
req_body
resp_body
}
validate_servers
log_error
}
}handle_errors {
respond @api "Resource: {http.request.orig_uri}. Error: {openapi.error}" {openapi.status_code} {
close
}
}
}
```Try out the `customer.yaml` API by running the accompanying node application.
## Using OpenPolicyAgent
You can enforce policies on routes by adding the `x-policy` field to either the [OpenAPI3 document](https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.1.0.md#schema) level, or the [path item](https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.1.0.md#pathItemObject) level or or the [operation](https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.1.0.md#operationObject) level.
If a `x-policy` field is added at the
- *OpenAPI3 document* then the policy will be applied to all path
- *Path item* then the policy will be applied to all methods specified for that path eg `POST`, `GET` to `/api/v1/customer`
- *Operation* then the policy will only be applied to that operation eg. `GET/api/v1/customer`
`x-policy` attribute nested deeper into theThe 'deeper' a `x-policy` field, the higher its precedence. Since `policy_bundle` is optional, no `x-policy` will be evaluated if no bundle are loaded.
Assume the following OPA policy file
```
package authzdefault allow = false
allow {
lower(input.method) = "get"
array.slice(input.path, 0, 2) = [ "api", "customer" ]
to_number(input.pathParams.custId) >= 100
}
```
has been bundled as `bundle.tar.gz`. Load it with `policy_bundle`The following OpenAPI3 fragment show how you can evaluate `authz.allow` on all `GET /api/customer/`
```
paths:
/api/customer/{custId}:
get:
description: Get customer
operationId: getCustomer
x-policy: authz.allow
parameters:
- name: custId
in: path
required: true
schema:
type: number
```The HTTP request are converted into `input` according to the following table
| Fields | Description |
|--------------------------|-------------|
| `input.scheme` | HTTP or HTTPS |
| `input.host` | Host and port number |
| `input.method` | HTTP method |
| `input.path` | Array of path elements eg. `/api/customer/123` is converted to `[ 'api', 'customer', '123' ]` |
| `input.remoteAddr` | Host and port number of the client |
| `input.queryString` | If a query string is present, the query string will be destructed into a map under `queryString` root. Example `?offset=10&limit=10` will be converted to the following keys: `input.queryString.offset` and `input.queryString.limit`. Query parameters with multiple value will have an array as its value. `queryString` will not be present if the request do not contain any query params |
| `input.pathParams` | Like query string but a map of matched path parameters from the OpenAPI3 spec where parameter type is `in: path`. See above example |
| `input.headers` | Map of all the request headers |
| `input.body` | Access to the request's body. Only supports `application/json` content type. **Not implemented yet** |Assume all values are string
This plugin currently can only work with policies/rules that return true or false.