Categorygithub.com/chukmunnlee/caddy-openapi
modulepackage
0.11.0
Repository: https://github.com/chukmunnlee/caddy-openapi.git
Documentation: pkg.go.dev

# 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 <name> 6.5.0-35-generic #35~22.04.1-Ubuntu SMP PREEMPT_DYNAMIC <timestamp> 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 which can be used in other directives like respond

FieldsDescription
spec <oas_file>The OpenAPI3 YAML file. This attribute is a mandatory
policy_bundle <bundle>OPA policy bundle created with opa build.
fall_throughToggles fall through when the request does do match the provided OpenAPI spec. Default is false
validate_serversEnable server validation. Accepts true, false or just the directive which enables validation. Default is true.
log_errorToggles error logging. Default is false
checkEnable 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. You can use them in other directives like respond

PlaceholdersDescription
openapi.errorDescription of the error
openapi.status_codeSuggested status code
openapi.response_errorResonse 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 level, or the path item level or or the operation 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 the

The '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 authz

default 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

FieldsDescription
input.schemeHTTP or HTTPS
input.hostHost and port number
input.methodHTTP method
input.pathArray of path elements eg. /api/customer/123 is converted to [ 'api', 'customer', '123' ]
input.remoteAddrHost and port number of the client
input.queryStringIf 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.pathParamsLike query string but a map of matched path parameters from the OpenAPI3 spec where parameter type is in: path. See above example
input.headersMap of all the request headers
input.bodyAccess 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.

# Constants

No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author

# Structs

No description provided by the author
This middleware validates request against an OpenAPI V3 specification.
No description provided by the author