Deploy custom plugins with Kong Operator

Deployment Platform
Related Documentation
TL;DR

Build and push a plugin as a container image, then use a KongPluginInstallation to register it with the operator. Reference it in your GatewayConfiguration to make it available in Data Planes and configure its behavior using a KongPlugin resource.

Prerequisites

kubectl create namespace kong --dry-run=client -o yaml | kubectl apply -f -

If you don’t have a Konnect account, you can get started quickly with our onboarding wizard.

  1. The following Konnect items are required to complete this tutorial:
    • Personal access token (PAT): Create a new personal access token by opening the Konnect PAT page and selecting Generate Token.
  2. Set the personal access token as an environment variable:

    export KONNECT_TOKEN='YOUR KONNECT TOKEN'

If you want cert-manager to issue and rotate the admission and conversion webhook certificates, install cert-manager to your cluster and enable cert-manager integration by passing the following argument while installing, in the next step:

   --set global.webhooks.options.certManager.enabled=true

If you do not enable this, the chart will generate and inject self-signed certificates automatically. We recommend enabling cert-manager to manage the lifecycle of these certificates. Kong Operator needs a certificate authority to sign the certificate for mTLS communication between the control plane and the data plane. This is handled automatically by the Helm chart. If you need to provide a custom CA certificate, refer to the certificateAuthority section in the values.yaml of the Helm chart to learn how to create and reference your own CA certificate.

Apply a KongLicense. This assumes that your license is available in ./license.json

echo "
apiVersion: configuration.konghq.com/v1alpha1
kind: KongLicense
metadata:
 name: kong-license
rawLicenseString: '$(cat ./license.json)'
" | kubectl apply -f -
kubectl create namespace kong --dry-run=client -o yaml | kubectl apply -f -
echo '
kind: KonnectAPIAuthConfiguration
apiVersion: konnect.konghq.com/v1alpha1
metadata:
  name: konnect-api-auth
  namespace: kong
spec:
  type: token
  token: "'$KONNECT_TOKEN'"
  serverURL: us.api.konghq.com
' | kubectl apply -f -

This how-to requires some Kubernetes services to be available in your cluster. These services will be used by the resources created in this how-to.

kubectl apply -f https://developer.konghq.com/manifests/kic/echo-service.yaml -n kong

This how-to also requires 1 pre-configured route:

Wait until the pods for the services are ready:

kubectl wait --for=condition=Ready pod --all -n kong --timeout=300s

Plugin distribution using an OCI registry

Kong Operator can install Kong custom plugins packaged as container images. This guide shows how to package, install, and use a custom plugin in Kong Gateway instances managed by the Kong Operator.

Create a custom plugin (Optional)

If you already have a real plugin, you can skip this step.

mkdir myheader
echo 'local MyHeader = {}

MyHeader.PRIORITY = 1000
MyHeader.VERSION = "1.0.0"

function MyHeader:header_filter(conf)
  -- do custom logic here
  kong.response.set_header("myheader", conf.header_value)
end

return MyHeader
' > myheader/handler.lua

echo 'return {
  name = "myheader",
  fields = {
    { config = {
        type = "record",
        fields = {
          { header_value = { type = "string", default = "roar", }, },
        },
    }, },
  }
}
' > myheader/schema.lua

The directory should now look like this:

myheader
├── handler.lua
└── schema.lua

1 directory, 2 files

Build a container image (Optional)

This section is optional. The rest of this guide uses a pre-published image, and the following information is provided if you want to package your own custom plugin.

Plugin-related files should be at the root of the image, so the Dockerfile for the plugin would look like this:

echo 'FROM scratch

COPY myheader /
' > Dockerfile

In this example, myheader is a directory that contains handler.lua and schema.lua.

Build the image:

docker build -t myheader:1.0.0 .

Next, push the image to a public or private registry available to the Kubernetes cluster where Kong Operator is running.

docker tag myheader:1.0.0 $YOUR_REGISTRY_ADDRESS/myheader:1.0.0
docker push $YOUR_REGISTRY_ADDRESS/myheader:1.0.0

In this example, the plugin is available in the public registry (Docker Hub) as kong/plugin-example:1.0.0. The following steps use the same source.

Register the plugin schema in Konnect

To see your custom plugin in Kong Konnect, you need to register the schema with your control plane.

First, get your control plane ID from the KonnectGatewayControlPlane resource Kong Operator created for your Gateway:

CONTROL_PLANE_ID=$(kubectl -n kong get konnectgatewaycontrolplane kong -o jsonpath='{.status.id}')

Run the following command to upload your schema file to your Konnect control plane:

curl -X POST \
  https://us.api.konghq.com/v2/control-planes/$CONTROL_PLANE_ID/core-entities/plugin-schemas \
  --header 'Content-Type: application/json' \
  --header "Authorization: Bearer $KONNECT_TOKEN" \
  --data "{\"lua_schema\": $(jq -Rs . './myheader/schema.lua')}"

Install the plugin

  1. Install the plugin using the KongPluginInstallation resource. This resource makes the plugin available for instances of Kong Gateway resources:

    echo '
    kind: KongPluginInstallation
    apiVersion: gateway-operator.konghq.com/v1alpha1
    metadata:
      name: custom-plugin-myheader
    spec:
      image: kong/plugin-example:1.0.0
    ' | kubectl apply -f -

    Verify that the plugin is fetched and available by examining the status of the KongPluginInstallation resource:

    kubectl get kongplugininstallations.gateway-operator.konghq.com -o jsonpath-as-json='{.items[*].status}'

    The output should look like this:

    [
      {
            "conditions": [
                 {
                      "lastTransitionTime": "2024-10-09T19:39:39Z",
                      "message": "plugin successfully saved in cluster as ConfigMap",
                      "observedGeneration": 1,
                      "reason": "Ready",
                      "status": "True",
                      "type": "Accepted"
                 }
            ],
            "underlyingConfigMapName": "custom-plugin-myheader-hnzf9"
      }
    ]

    In case of problems, the respective conditions or respective resources will provide more information.

    The KongPluginInstallation resource creates a ConfigMap with the plugin content. Additional ConfigMaps are created when a plugin is referenced by other resources. The operator automatically manages the lifecycle of all these ConfigMaps.

  2. Make the plugin available in a Gateway resource by referencing it in the spec.dataPlaneOptions.spec.pluginsToInstall field of the GatewayConfiguration resource, and create a Gateway that uses the configured class. Plugins can be referenced across namespaces without any additional configuration.

    echo '
    kind: GatewayConfiguration
    apiVersion: gateway-operator.konghq.com/v2beta1
    metadata:
      name: kong
      namespace: default
    spec:
      dataPlaneOptions:
         deployment:
            replicas: 2
            podTemplateSpec:
              spec:
                 containers:
                    - name: proxy
                      image: kong/kong-gateway:3.16
         pluginsToInstall:
            - name: custom-plugin-myheader
    ---
    apiVersion: gateway.networking.k8s.io/v1
    kind: GatewayClass
    metadata:
      name: kong
    spec:
      controllerName: konghq.com/gateway-operator
      parametersRef:
         group: gateway-operator.konghq.com
         kind: GatewayConfiguration
         name: kong
         namespace: default
    ---
    apiVersion: gateway.networking.k8s.io/v1
    kind: Gateway
    metadata:
      name: kong
      namespace: kong
    spec:
      gatewayClassName: kong
      listeners:
      - name: proxy
        port: 80
        protocol: HTTP
        allowedRoutes:
          namespaces:
            from: All
    ' | kubectl apply -f -
  3. Deploy an example service and expose it by configuring HTTPRoute with the custom plugin:

    kubectl apply -f https://developer.konghq.com/assets/kubernetes-ingress-controller/examples/echo-service.yaml

    Next, add the HTTPRoute with the custom plugin. The configuration of the plugin is provided with the KongPlugin CRD, where the field plugin is set to the name of the KongPluginInstallation resource.

     echo "
     apiVersion: configuration.konghq.com/v1
     kind: KongPlugin
     metadata:
       name: myheader
       namespace: kong
       annotations:
         kubernetes.io/ingress.class: kong
     plugin: custom-plugin-myheader
     config:
       header_value: my-first-plugin
     " | kubectl apply -f -

    Next, apply the KongPlugin resource by annotating the service resource:

     kubectl annotate -n kong service echo konghq.com/plugins=myheader

Validate your configuration

Ensure that everything is up and running, get the Gateway address, and make a request to the service.

  1. Wait for the Gateway to be programmed:

    kubectl wait --for=condition=Programmed gateway/kong -n kong --timeout=5m
  2. Get the Gateway address:

    export PROXY_IP=$(kubectl get gateway kong -n kong -o jsonpath='{.status.addresses[0].value}')
  3. Make a request to the service

    sleep 5 && for _  in {1..6}; do
    curl "$PROXY_IP/echo" \
         --no-progress-meter --fail-with-body  \
    ; done

    You should see the following header:

    myheader: my-first-plugin
    sleep 5 && for _  in {1..6}; do
    curl "$PROXY_IP/echo" \
         --no-progress-meter --fail-with-body  \
    ; done

    You should see the following header:

    myheader: my-first-plugin

Cleanup

Delete the resources created in this guide. Deleting the kong-system namespace removes the operator:

kubectl delete -n kong --ignore-not-found \
  kongplugininstallation/custom-plugin-myheader \
  gateway/kong
kubectl delete -n default --ignore-not-found gatewayconfiguration/kong
kubectl delete --ignore-not-found gatewayclass/kong
kubectl delete --ignore-not-found namespace kong
helm uninstall kong-operator -n kong-system
kubectl delete --ignore-not-found namespace kong-system

Help us make these docs great!

Kong Developer docs are open source. If you find these useful and want to make them better, contribute today!