diff --git a/app/_data/docs_nav_kic_3.2.x.yml b/app/_data/docs_nav_kic_3.2.x.yml index 0d18259e856c..a3f14a1749d6 100644 --- a/app/_data/docs_nav_kic_3.2.x.yml +++ b/app/_data/docs_nav_kic_3.2.x.yml @@ -175,6 +175,8 @@ items: items: - text: Custom Plugins url: /plugins/custom + - text: Cross-namespace Plugins + url: /plugins/cross-namespace - text: Authentication url: /plugins/authentication - text: ACL diff --git a/app/_data/docs_nav_kic_3.3.x.yml b/app/_data/docs_nav_kic_3.3.x.yml index 7b7d5eefc3c8..f64ea87325a5 100644 --- a/app/_data/docs_nav_kic_3.3.x.yml +++ b/app/_data/docs_nav_kic_3.3.x.yml @@ -175,6 +175,8 @@ items: items: - text: Custom Plugins url: /plugins/custom + - text: Cross-namespace Plugins + url: /plugins/cross-namespace - text: Authentication url: /plugins/authentication - text: ACL diff --git a/app/_includes/md/kic/consumer.md b/app/_includes/md/kic/consumer.md index ef2882c465ac..3d0a006c8e14 100644 --- a/app/_includes/md/kic/consumer.md +++ b/app/_includes/md/kic/consumer.md @@ -1,11 +1,15 @@ {% comment %}This file is intentionally indented as it's included in an
    on multiple pages{% endcomment %} {% assign name = include.name | default: 'kotenok' %} + {%- assign namespace = include.namespace %} {%- assign credName = include.credName %} ```bash echo "apiVersion: configuration.konghq.com/v1 kind: KongConsumer metadata: name: {{ name }} + {% if namespace -%} + namespace: {{ namespace }} + {% endif -%} annotations: kubernetes.io/ingress.class: kong username: {{ name }} diff --git a/app/_includes/md/kic/prerequisites.md b/app/_includes/md/kic/prerequisites.md index c691707af6df..c8cc5cd63cd3 100644 --- a/app/_includes/md/kic/prerequisites.md +++ b/app/_includes/md/kic/prerequisites.md @@ -63,6 +63,9 @@ - name: proxy port: 80 protocol: HTTP + allowedRoutes: + namespaces: + from: All " | kubectl apply -f - ``` diff --git a/app/_src/kubernetes-ingress-controller/plugins/cross-namespace.md b/app/_src/kubernetes-ingress-controller/plugins/cross-namespace.md new file mode 100644 index 000000000000..3203ea705848 --- /dev/null +++ b/app/_src/kubernetes-ingress-controller/plugins/cross-namespace.md @@ -0,0 +1,317 @@ +--- +title: Using plugins across namespaces +type: how-to +purpose: | + Configure plugins in one namespace that interact with resources in another. +--- + +Plugins can interact with resources across different Kubernetes namespaces. +`KongClusterPlugin` resources can be referenced from any +namespace, but require cluster-level permissions and may not be appropriate for +all environments. This guide covers the use of `ReferenceGrant` to +grant selective permissions that allow `KongPlugin` resources in one namespace to +modify resources in another. + +{% include /md/kic/prerequisites.md release=page.release disable_gateway_api=false %} + +The examples provided use `HTTPRoute` from the Gateway API project, but `ReferenceGrant` resources may be used with Ingress too. + +{:.important} +> **Note**: This guide requires {{site.kic_product_name}} version 3.2 or higher +> and the [`ReferenceGrant`](https://gateway-api.sigs.k8s.io/api-types/referencegrant/) +> resource definition from the [Gateway API](https://gateway-api.sigs.k8s.io/). +> `ReferenceGrant` may not be available on all Kubernetes clusters. + +## Example use case + +Alice manages [consumers](/kubernetes-ingress-controller/{{page.release}}/reference/custom-resources/#kongconsumer) +for ACME Inc. They have permission to creates consumers and credentials in the `golf` namespace. + +Bob manages ACME Inc's `httpbin` Service in the `foxtrot` +namespace. Bob wants to apply different rate limits to different consumers, +but they do not have full access to the `golf` namespace, and Alice does not +have access to the `foxtrot` namespace. To manage these limits, Alice can +create `ReferenceGrants` that permit Bob's `KongPlugins` in `foxtrot` to +interact with `KongConsumers` in the `golf` namespace. + +## Create namespaces + +Create the two test namespaces: + +```bash +kubectl create namespace golf +kubectl create namespace foxtrot +``` + +The result should look like: + +```text +namespace/golf created +namespace/foxtrot created +``` + +## Create routes and services + +Create a Service and Deployment in `foxtrot`: + +```bash +kubectl apply -f https://docs.konghq.com/assets/kubernetes-ingress-controller/examples/httpbin-service.yaml -n foxtrot +``` + +The result should look like: + +```text +service/httpbin created +deployment.apps/httpbin created +``` + +Create an HTTPRoute to access the Service: + +```bash +echo " +apiVersion: gateway.networking.k8s.io/v1 +kind: HTTPRoute +metadata: + name: demo-route + namespace: foxtrot + annotations: + konghq.com/strip-path: 'true' +spec: + parentRefs: + - name: kong + namespace: default + rules: + - matches: + - path: + type: PathPrefix + value: /demo-route + backendRefs: + - name: httpbin + kind: Service + port: 80 +" | kubectl apply -f - +``` + +The result should look like: +```text +httproute.gateway.networking.k8s.io/demo-route created +``` + +Create an authentication plugin: + +```bash +echo ' +apiVersion: configuration.konghq.com/v1 +kind: KongPlugin +metadata: + name: httpbin-basic-auth + namespace: foxtrot +config: + hide_credentials: true +plugin: basic-auth +' | kubectl apply -f - +``` + +The result should look like: +```text +kongplugin.configuration.konghq.com/httpbin-basic-auth created +``` +and attach it to the Service: + +```bash +kubectl annotate service httpbin -n foxtrot konghq.com/plugins=httpbin-basic-auth +``` + +The result should look like: +```text +service/httpbin annotated +``` + +## Create consumers and credentials + +1. Create Secrets to add `basic-auth` credentials for two consumers: + + ```bash + echo ' + --- + apiVersion: v1 + kind: Secret + metadata: + name: charlie-basic-auth + namespace: golf + labels: + konghq.com/credential: basic-auth + stringData: + username: charlie + password: charlie-password + --- + apiVersion: v1 + kind: Secret + metadata: + name: david-basic-auth + namespace: golf + labels: + konghq.com/credential: basic-auth + stringData: + username: david + password: david-password + ' | kubectl apply -f - + ``` + + The results should look like: + ```text + secret/charlie-basic-auth created + secret/david-basic-auth created + ``` + +1. Create consumers named `charlie` and `david` that use these credentials: + +{% include /md/kic/consumer.md release=page.release name='charlie' credName='charlie-basic-auth' namespace='golf' %} + +{% include /md/kic/consumer.md release=page.release name='david' credName='david-basic-auth' namespace='golf' %} + +## Grant cross-namespace permissions + +Bob and Alice wants to limit the amount of requests to the `httpbin` Service on +a per-consumer basis. Because they only have access to their own namespaces, +they need to create rules that allow interactions across them. + +By default, resources that support the [`konghq.com/plugins` annotation](/kubernetes-ingress-controller/{{page.release}}/reference/annotations/#konghqcomplugins) +can only attach plugins in their same namespace. `ReferenceGrant` resources can +grant permission to use plugins to resources in other namespaces. + +To allow this cross-namespace interaction, Alice asks Bob to create a +ReferenceGrant in `foxtrot` that allows KongConsumers in `golf` to use +KongPlugins in `foxtrot`: + +```bash +echo ' +apiVersion: gateway.networking.k8s.io/v1beta1 +kind: ReferenceGrant +metadata: + name: golf-rate-limit + namespace: foxtrot +spec: + from: + - group: "configuration.konghq.com" + kind: KongConsumer + namespace: golf + to: + - group: "configuration.konghq.com" + kind: KongPlugin +' | kubectl apply -f - +``` + +The results should look like: +```text +referencegrant.gateway.networking.k8s.io/golf-rate-limit created +``` + +Such ReferenceGrants allow the `from` resource (KongConsumers in the `golf` +namespace) to interact with KongPlugins in the ReferenceGrant's namespace +(`foxtrot`). + +## Create the cross-namespace plugins + +With the ReferenceGrant in place, Bob can create the `rate-limiting` +KongPlugins: + +```bash +echo ' +--- +apiVersion: configuration.konghq.com/v1 +kind: KongPlugin +metadata: + name: rate-limit-charlie + namespace: foxtrot + annotations: + kubernetes.io/ingress.class: kong +config: + minute: 10 + policy: local +plugin: rate-limiting +--- +apiVersion: configuration.konghq.com/v1 +kind: KongPlugin +metadata: + name: rate-limit-david + namespace: foxtrot + annotations: + kubernetes.io/ingress.class: kong +config: + minute: 5 + policy: local +plugin: rate-limiting +' | kubectl apply -f - +``` + +The result should look like: +```text +kongplugin.configuration.konghq.com/rate-limit-charlie created +kongplugin.configuration.konghq.com/rate-limit-david created +``` + +## Attach the plugins to their resources + +With the plugins in place, each namespace administrator can attach them to the +resources in their respective namespaces. + +Alice attaches the plugins to the KongConsumers in `golf`. They use the +`:` format in the plugin name to indicate +that the requested KongPlugin resides in another namespace: + +```bash +kubectl annotate kongconsumer charlie -n golf konghq.com/plugins=foxtrot:rate-limit-charlie +kubectl annotate kongconsumer david -n golf konghq.com/plugins=foxtrot:rate-limit-david +``` + +The result should look like: + +```text +kongconsumer.configuration.konghq.com/charlie annotated +kongconsumer.configuration.konghq.com/david annotated +``` + +Bob attaches the plugins to the `httpbin` Service in `foxtrot`: + +```bash +kubectl annotate service httpbin --overwrite -n foxtrot konghq.com/plugins=httpbin-basic-auth,rate-limit-charlie,rate-limit-david +``` + +Note that this command overrides the entire plugin annotation, so it needs to +include the `httpbin-basic-auth` plugin from earlier in the list, not just the +new rate limiting plugins. + +The result should look like: + +```text +service/httpbin annotated +``` + +## Test the configuration + +Send a request using the `charlie` consumer: + +```bash +curl -sv $PROXY_IP/demo-route/status/200 -u charlie:charlie-password 2>&1 | grep "X-RateLimit-Remaining-Minute" +``` +The results should show the remaining limit expected for `charlie`. Since +`charlie` can send 10 requests per minute, and has sent 1, the response should +indicate there are 9 requests remaining: + +```text +< X-RateLimit-Remaining-Minute: 9 +``` + +Send a request using the `david` consumer: + +```bash +curl -sv $PROXY_IP/demo-route/status/200 -u david:david-password 2>&1 | grep "X-RateLimit-Remaining-Minute" +``` +The results should show the remaining limit expected for `david`. Since +`david` can send 5 requests per minute, and has sent 1, the response should +indicate there are 4 requests remaining: + +```text +< X-RateLimit-Remaining-Minute: 4 +```