Configuration

Learn how to declaratively configure your API.

Introduction

The goal of this section is to provide an introduction to each of Kanali’s configurable resources. More in depth documentation for each resource can be found here.


The ApiProxy Resource

This resource declaratively defines how your upstream services are reached. There are 3 main sections that can be configured for this resource.

source how your proxy will be accessible

target how your upstream service will be reached

plugins how your proxy will be secured

Let’s explore each in more detail.

Source

There are just 2 ways to configure a source, with or without a virtual host. If virtualHost is specified than, that proxy will only be discoverable if that host is being used. If virtualHost is not specified, than that proxy will be discoverable for all hosts. Here are examples of each.

---
apiVersion: kanali.io/v2
kind: ApiProxy
metadata:
  name: example
spec:
  source:
    path: /foo
  target:
    path: /
    backend:
      endpoint: https://foo.bar.com:8443
---
apiVersion: kanali.io/v2
kind: ApiProxy
metadata:
  name: example
spec:
  source:
    path: /bar
    virtualHost: foo.bar.com
  target:
    path: /
    backend:
      endpoint: https://foo.bar.com:8443

Target

There are 3 main sections that can be configured for this resource.

path upstream path

backend backend type

ssl tls configuration

Path

This fields specifies the upstream path that an upstream request will be proxied to. In the below example, if a request is made to /foo/baz, the upstream service will see /bar/baz.

---
apiVersion: kanali.io/v2
kind: ApiProxy
metadata:
  name: example
spec:
  source:
    path: /foo
  target:
    path: /bar
    backend:
      endpoint: https://foo.bar.com:8443
Backend

There are 4 different types of backends that you can configure. Toggle through each type below to learn more.

---
apiVersion: kanali.io/v2
kind: ApiProxy
metadata:
  name: example
spec:
  source:
    path: /example
  target:
    path: /
    backend:
      service:
        name: serviceName
        port: 8080
Statically defining an upstream Kubernetes service is the easiest way define an ApiProxy. Simply specify the name of your upstream Kubernetes service and the port your service is listening on.

The namespace of this ApiProxy must match the namespace of the upstream service.
---
apiVersion: kanali.io/v2
kind: ApiProxy
metadata:
 name: example
spec:
 source:
   path: /example
 target:
   path: /
   backend:
     service:
       port: 8080
       labels:
       - name: key
         value: value
       - name: deploy
         header: x-foo-deployment
Dynamically defining an upstream Kubernetes service provides an easy way to dynamically route traffic. To configure dynamic service discovery, simply specify the port your service is listening on and a set of labels.

Labels work in a similar fashion to that of Kubernetes match labels. The name field of each label will be matched against a metadata label name on Kubernetes services. The second field of each label can either be a value or header. If value is specified, it corresponds directly to the Kubernetes service metadata label value. If header is specified, than the value of the Kubernetes service label will be matched against the value of the HTTP header specified by this label. Let's look at a quick example.

Using the above ApiProxy as an example, If I make an request to /example and include the header x-foo-deployment: bar, Kanali will look for services in the default namespace that have at least the two following metadata labels.

metadata:
 labels:
   key: value
   deploy: bar
If multiple upstream services are found, Kanali will use the first one and add the response header x-kanali-service-cardinality whose value matches the cardinality of the discovered upstream services to aid in troubleshooting.

The namespace of this ApiProxy must match the namespace of the upstream service.
---
apiVersion: kanali.io/v2
kind: ApiProxy
metadata:
 name: example
spec:
 source:
   path: /example
 target:
   path: /
   backend:
     endpoint: https://foo.bar.com:8443
There may be times when you want to add API management to an upstream service that is not deployed to Kubernetes. To prevent you from deploying a different API management gateway to solve this specific use case, Kanali lets you proxy to arbitrary endpoints. To configure an arbitrary endpoint, just specify it as shown above. The value must have the following structure.

<scheme>://<host>

The scheme must either be http or https and if the host does not contain a port, 80 will be used as the default.
---
apiVersion: kanali.io/v2
kind: ApiProxy
metadata:
 name: example
spec:
 source:
   path: /example
 target:
   path: /
   backend:
     mock:
       mockTargetName: mockTargetName
If this upstream type is used, Kanali will not make any request to any upstream service. Instead, it will return a preconfigured response. This response is defined in the MockTarget resource. This resource is explained in detail below.

The namespace of this ApiProxy must match the namespace of the MockTarget resource.
SSL

The presence of the ssl field specifies that tls will be used to secure the connection between Kanali and an upstream service. To configure this option, just specify the secret name containing the tls assets. An example is demonstrated below.

---
apiVersion: kanali.io/v2
kind: ApiProxy
metadata:
  name: example
spec:
  source:
    path: /foo
  target:
    path: /bar
    backend:
      endpoint: https://foo.bar.com:8443
    ssl:
      secretName: my-secret

Let’s assume that the specified secret above is structured like the example below. Note the presence of the kanali.io/enabled annotation. This annotation declares that Kanali is allowed to use this secret (this is due to Kubernetes RBAC limitations).

Note the data fields present in this secret. If your upstream service wants to perform client side validation, the tls certificate/key pair as specified in the tls.crt and tls.key fields will be send to the server.

---
apiVersion: v1
kind: Secret
metadata:
  name: my-secret
  annotations:
    kanali.io/enabled: 'true'
type: Opaque
data:
  tls.crt: 
  tls.key: 
  tls.ca: 

If you want to customize the name of the data keys, you can specify your custom key via an annotation. For example, if you want to use the data key crt.pem instead of tls.crt, you would need to include the annotation kanali.io/cert: ‘crt.pem’. A complete list of override annotations for the data fields are listed below.

Data fieldAnnotation
tls.cakanali.io/ca: ‘custom.ca.value’
tls.crtkanali.io/cert: ‘custom.cert.value’
tls.keykanali.io/key: ‘custom.key.value’

Plugins

Plugins enable the execution of encapsulated logic on a per proxy basis. Plugins are configured as a list of different plugins that you want executed for a specific ApiProxy. Each plugin in the list requires the name of the plugin and an optional config field specifying proxy level configuration items that will be passed to the plugin upon execution.

For a complete list of available plugins and their corresponding documentation, visit the documentation for plugins.

---
apiVersion: kanali.io/v2
kind: ApiProxy
metadata:
  name: example
spec:
  source:
    path: /foo
  target:
    path: /bar
    backend:
      endpoint: https://foo.bar.com:8443
  plugins:
  - name: apikey
    config:
      bindingName: my-binding
  - name: jwt
    config:
      audienceID: abc123
SSL

The presence of the ssl field specifies that tls will be used to secure the connection between Kanali and an upstream service. To configure this option, just specify the secret name containing the tls assets. An example is demonstrated below.

---
apiVersion: kanali.io/v2
kind: ApiProxy
metadata:
  name: example
spec:
  source:
    path: /foo
  target:
    path: /bar
    backend:
      endpoint: https://foo.bar.com:8443
    ssl:
      secretName: my-secret

Let’s assume that the specified secret above is structured like the example below. Note the presence of the kanali.io/enabled annotation. This annotation declares that Kanali is allowed to use this secret (this is due to Kubernetes RBAC limitations).

Note the data fields present in this secret. If your upstream service wants to perform client side validation, the tls certificate/key pair as specified in the tls.crt and tls.key fields will be send to the server.

---
apiVersion: v1
kind: Secret
metadata:
  name: my-secret
  annotations:
    kanali.io/enabled: 'true'
type: Opaque
data:
  tls.crt: 
  tls.key: 
  tls.ca: 

If you want to customize the name of the data keys, you can specify your custom key via an annotation. For example, if you want to use the data key crt.pem instead of tls.crt, you would need to include the annotation kanali.io/cert: ‘crt.pem’. A complete list of override annotations for the data fields are listed below.

Data fieldAnnotation
tls.cakanali.io/ca: ‘custom.ca.value’
tls.crtkanali.io/cert: ‘custom.cert.value’
tls.keykanali.io/key: ‘custom.key.value’

Plugins

Plugins enable the execution of encapsulated logic on a per proxy basis. Plugins are configured as a list of different plugins that you want executed for a specific ApiProxy. Each plugin in the list requires the name of the plugin and an optional config field specifying proxy level configuration items that will be passed to the plugin upon execution.

For a complete list of available plugins and their corresponding documentation, visit the documentation for plugins.

---
apiVersion: kanali.io/v2
kind: ApiProxy
metadata:
  name: example
spec:
  source:
    path: /foo
  target:
    path: /bar
    backend:
      endpoint: https://foo.bar.com:8443
  plugins:
  - name: apikey
    config:
      bindingName: my-binding
  - name: jwt
    config:
      audienceID: abc123

The ApiKey Resource

This resource configures API keys. Note that by itself, an ApiKey resource does not grant permission to any ApiProxy. Permissions are granted via the ApiKeyBinding resource (the next resource we will explore).

Note that this resource is cluster scoped. This means that resources of this kind are unique per cluster, not per namespace.

Each ApiKey resource specifies a list of revisions. A revision is a specific API key value that may either be active or inactive. The value is both rsa encrypted and base64 encoded. This format caters well for API key rotation.

Below is an example of an ApiKey resource.

---
apiVersion: kanali.io/v2
kind: ApiKey
metadata:
  name: example
spec:
  revisions:
  - data: aGVsbG8=
    status: Active
  - data: d29ybGQ=
    status: Inactive

The ApiKeyBinding Resource

This resource is responsible for granting course and fine grained permissions to a list of ApiKey resources. Toggle below to learn more about the different options available to you when granting permissions.

---
apiVersion: kanali.io/v2
kind: ApiKeyBinding
metadata:
  name: example
spec:
  keys:
  - name: key-one
    defaultRule:
      global: true
  - name: key-two
    defaultRule:
      granular:
        verbs:
        - GET
  
Each API key has a default rule. This can be thought of as coarse grained validation for this API key. This default rule can either grant global access or granular access. Global access will grant an API key access to all http methods whereas granular access allows for only a subset of methods to be granted.

‑‑‑
apiVersion: kanali.io/v2
kind: ApiKeyBinding
metadata:
  name: example
spec:
  keys:
  - name: key-one
    defaultRule:
      global: true
    subpaths:
    - path: /foo
      rule:
        global: false
  

Fine-grained access is granted with the subpaths field. This field contains a list of target paths that define a higher priority rule than that of the default rule.

Let’s explore an example using the above ApiKeyBinding resource.

Suppose a request is made with the key-one API key. Let’s also assume that the upstream service will see a request with the path /foo/bar. When Kanali is evaluating which rule is the highest priority, it will do a regular expression match with each path in the subpaths list. If a match is found, that rule is used. If no item in the subpaths list matches, the default rule is used. Hence, in this example, the first item in the subpath list will be a match and hence no access will be granted.

‑‑‑
apiVersion: kanali.io/v2
kind: ApiKeyBinding
metadata:
  name: example
spec:
  keys:
  - name: key-one
    defaultRule:
      global: true
    rate:
      amount: 5
      unit: second
  
NOTE: the implementation for rate limiting is not present in v2.0.0. It will however be present in an upcoming minor release.

It you would like to limit the rate of calls a specific API key is allowed to make to a specific upstream service, simplify configure the rate field.

The MockTarget Resource

This resource allows for the configuration of a fake response that will be sent back to the client if the ApiProxy resource that matches a request specifies that a MockTarget should be used as the upstream service.

To configure a MockTarget resource, configure a list of routes. Think of it as if you were developing a an upstream service and each route in your middleware should represent a route item in a MockTarget resource.

‑‑‑
apiVersion: kanali.io/v2
kind: MockTarget
metadata:
  name: example
spec:
  routes:
  - path: /foo
    status: 200
    methods:
    - GET
    - POST
    headers:
      Content-Type: application/json
    body: |-
      {
        "foo": "bar"
      }