diff --git a/dev/flux-kustomization.yaml b/dev/flux-kustomization.yaml index fb91860..a052d6f 100644 --- a/dev/flux-kustomization.yaml +++ b/dev/flux-kustomization.yaml @@ -26,3 +26,10 @@ spec: BACKEND_SCOPES: '[]' API_RATE_LIMIT_MINUTE: "60" AUDIT_LOG_ENDPOINT: http://audit-log-collector.observability.svc.cluster.local/logs + REST_HOSTNAME: catalog-dev.sttlab.local + MCP_HOSTNAME: catalog-mcp-dev.sttlab.local + MCP_AUDIENCE: '["mcp-catalog"]' + TOKEN_EXCHANGE_CLIENT_ID: kong-token-exchange-broker + substituteFrom: + - kind: Secret + name: catalog-token-exchange-broker diff --git a/dev/token-exchange-broker-externalsecret.yaml b/dev/token-exchange-broker-externalsecret.yaml new file mode 100644 index 0000000..fcee494 --- /dev/null +++ b/dev/token-exchange-broker-externalsecret.yaml @@ -0,0 +1,17 @@ +apiVersion: external-secrets.io/v1 +kind: ExternalSecret +metadata: + name: catalog-token-exchange-broker + namespace: catalog +spec: + refreshInterval: 1h + secretStoreRef: + kind: ClusterSecretStore + name: vault-kong-v2 + target: + name: catalog-token-exchange-broker + data: + - secretKey: TOKEN_EXCHANGE_CLIENT_SECRET + remoteRef: + key: kong-v2/catalog-api/token-exchange-broker + property: client_secret diff --git a/kong/kustomization.yaml b/kong/kustomization.yaml index dcb6257..60464a9 100644 --- a/kong/kustomization.yaml +++ b/kong/kustomization.yaml @@ -1,10 +1,16 @@ apiVersion: kustomize.config.k8s.io/v1beta1 kind: Kustomization resources: - - service.yaml - - routes.yaml - - kongplugin.yaml - - rate-limiting.yaml - - proxy-cache.yaml - - audit-log.yaml - - pluginbindings.yaml + - rest/service.yaml + - rest/routes.yaml + - rest/kongplugin.yaml + - rest/rate-limiting.yaml + - rest/proxy-cache.yaml + - rest/audit-log.yaml + - rest/pluginbindings.yaml + - mcp/service.yaml + - mcp/routes.yaml + - mcp/consumergroups.yaml + - mcp/ai-mcp-proxy.yaml + - mcp/openid-connect.yaml + - mcp/pluginbindings.yaml diff --git a/kong/mcp/ai-mcp-proxy.yaml b/kong/mcp/ai-mcp-proxy.yaml new file mode 100644 index 0000000..ceec121 --- /dev/null +++ b/kong/mcp/ai-mcp-proxy.yaml @@ -0,0 +1,89 @@ +apiVersion: configuration.konghq.com/v1 +kind: KongPlugin +metadata: + name: ${NAME}-catalog-mcp-ai-mcp-proxy + namespace: ${NAMESPACE} +plugin: ai-mcp-proxy +config: + mode: conversion-listener + include_consumer_groups: true + tools: + - name: list-products + description: Retrieves a paginated list of products in the classic-models catalog. + annotations: + title: List all products + read_only_hint: true + idempotent_hint: true + destructive_hint: false + open_world_hint: false + acl: + allow: + - sttlab-user + - sttlab-admin + method: GET + scheme: http + host: ${REST_HOSTNAME} + path: /classic-models/api/v1/products/ + - name: get-product + description: Retrieves a single product by its product code. + annotations: + title: Get a product + read_only_hint: true + idempotent_hint: true + destructive_hint: false + open_world_hint: false + acl: + allow: + - sttlab-user + - sttlab-admin + method: GET + scheme: http + host: ${REST_HOSTNAME} + path: /classic-models/api/v1/products/{productcode}/ + parameters: + - name: productcode + in: path + required: true + description: The product's unique code. + schema: {} + - name: create-product + description: Creates a new product in the catalog. + annotations: + title: Create a product + read_only_hint: false + idempotent_hint: false + destructive_hint: false + open_world_hint: false + acl: + allow: + - sttlab-admin + method: POST + scheme: http + host: ${REST_HOSTNAME} + path: /classic-models/api/v1/products/ + request_body: + required: true + content: + application/json: + schema: + type: object + properties: + productname: + type: string + productline: + type: string + productscale: + type: string + productvendor: + type: string + productdescription: + type: string + quantityinstock: + type: integer + buyprice: + type: string + msrp: + type: string + required: + - productname + - productline diff --git a/kong/mcp/consumergroups.yaml b/kong/mcp/consumergroups.yaml new file mode 100644 index 0000000..3d21174 --- /dev/null +++ b/kong/mcp/consumergroups.yaml @@ -0,0 +1,23 @@ +apiVersion: configuration.konghq.com/v1beta1 +kind: KongConsumerGroup +metadata: + name: ${NAME}-sttlab-user + namespace: ${NAMESPACE} +spec: + controlPlaneRef: + type: konnectNamespacedRef + konnectNamespacedRef: + name: ${CONTROL_PLANE_REF_NAME} + name: sttlab-user +--- +apiVersion: configuration.konghq.com/v1beta1 +kind: KongConsumerGroup +metadata: + name: ${NAME}-sttlab-admin + namespace: ${NAMESPACE} +spec: + controlPlaneRef: + type: konnectNamespacedRef + konnectNamespacedRef: + name: ${CONTROL_PLANE_REF_NAME} + name: sttlab-admin diff --git a/kong/mcp/openid-connect.yaml b/kong/mcp/openid-connect.yaml new file mode 100644 index 0000000..6f75cc9 --- /dev/null +++ b/kong/mcp/openid-connect.yaml @@ -0,0 +1,27 @@ +apiVersion: configuration.konghq.com/v1 +kind: KongPlugin +metadata: + name: ${NAME}-catalog-mcp-openid-connect + namespace: ${NAMESPACE} +plugin: openid-connect +config: + issuer: ${OIDC_ISSUER} + audience_required: ${API_AUDIENCE} + auth_methods: + - bearer + display_errors: false + ssl_verify: true + consumer_groups_claim: + - realm_access + - roles + client_id: + - ${TOKEN_EXCHANGE_CLIENT_ID} + client_secret: + - ${TOKEN_EXCHANGE_CLIENT_SECRET} + token_exchange: + subject_token_issuers: + - issuer: ${OIDC_ISSUER} + conditions: {} + request: + audience: ${API_AUDIENCE} + scopes: [] diff --git a/kong/mcp/pluginbindings.yaml b/kong/mcp/pluginbindings.yaml new file mode 100644 index 0000000..71dcf06 --- /dev/null +++ b/kong/mcp/pluginbindings.yaml @@ -0,0 +1,35 @@ +apiVersion: configuration.konghq.com/v1alpha1 +kind: KongPluginBinding +metadata: + name: ${NAME}-catalog-mcp-openid-connect-binding + namespace: ${NAMESPACE} +spec: + controlPlaneRef: + type: konnectNamespacedRef + konnectNamespacedRef: + name: ${CONTROL_PLANE_REF_NAME} + pluginRef: + name: ${NAME}-catalog-mcp-openid-connect + targets: + serviceRef: + group: configuration.konghq.com + kind: KongService + name: ${NAME}-catalog-mcp +--- +apiVersion: configuration.konghq.com/v1alpha1 +kind: KongPluginBinding +metadata: + name: ${NAME}-catalog-mcp-ai-mcp-proxy-binding + namespace: ${NAMESPACE} +spec: + controlPlaneRef: + type: konnectNamespacedRef + konnectNamespacedRef: + name: ${CONTROL_PLANE_REF_NAME} + pluginRef: + name: ${NAME}-catalog-mcp-ai-mcp-proxy + targets: + routeRef: + group: configuration.konghq.com + kind: KongRoute + name: ${NAME}-catalog-mcp diff --git a/kong/mcp/routes.yaml b/kong/mcp/routes.yaml new file mode 100644 index 0000000..fd9f3da --- /dev/null +++ b/kong/mcp/routes.yaml @@ -0,0 +1,42 @@ +apiVersion: configuration.konghq.com/v1alpha1 +kind: KongRoute +metadata: + name: ${NAME}-catalog-mcp + namespace: ${NAMESPACE} +spec: + serviceRef: + type: namespacedRef + namespacedRef: + name: ${NAME}-catalog-mcp + name: catalog-mcp + hosts: + - ${MCP_HOSTNAME} + methods: + - POST + - DELETE + paths: + - /catalog-mcp + strip_path: false + tags: + - catalog +--- +apiVersion: configuration.konghq.com/v1alpha1 +kind: KongRoute +metadata: + name: ${NAME}-catalog-mcp-stream + namespace: ${NAMESPACE} +spec: + serviceRef: + type: namespacedRef + namespacedRef: + name: ${NAME}-catalog-mcp + name: catalog-mcp-stream + hosts: + - ${MCP_HOSTNAME} + methods: + - GET + paths: + - /catalog-mcp + strip_path: false + tags: + - catalog diff --git a/kong/mcp/service.yaml b/kong/mcp/service.yaml new file mode 100644 index 0000000..db5330e --- /dev/null +++ b/kong/mcp/service.yaml @@ -0,0 +1,17 @@ +apiVersion: configuration.konghq.com/v1alpha1 +kind: KongService +metadata: + name: ${NAME}-catalog-mcp + namespace: ${NAMESPACE} +spec: + controlPlaneRef: + type: konnectNamespacedRef + konnectNamespacedRef: + name: ${CONTROL_PLANE_REF_NAME} + name: catalog-mcp + host: ${REST_HOSTNAME} + port: 80 + protocol: http + path: / + tags: + - catalog diff --git a/kong/audit-log.yaml b/kong/rest/audit-log.yaml similarity index 100% rename from kong/audit-log.yaml rename to kong/rest/audit-log.yaml diff --git a/kong/kongplugin.yaml b/kong/rest/kongplugin.yaml similarity index 79% rename from kong/kongplugin.yaml rename to kong/rest/kongplugin.yaml index 0aba213..89ff8d6 100644 --- a/kong/kongplugin.yaml +++ b/kong/rest/kongplugin.yaml @@ -6,8 +6,7 @@ metadata: plugin: openid-connect config: issuer: ${OIDC_ISSUER} - audience_required: ${API_AUDIENCE} - scopes_required: ${API_SCOPES_REQUIRED} + audience_required: ${BACKEND_AUDIENCE} auth_methods: - bearer display_errors: false @@ -18,6 +17,10 @@ config: - custom_id - username consumer_optional: true + client_id: + - ${TOKEN_EXCHANGE_CLIENT_ID} + client_secret: + - ${TOKEN_EXCHANGE_CLIENT_SECRET} token_exchange: subject_token_issuers: - issuer: ${OIDC_ISSUER} diff --git a/kong/pluginbindings.yaml b/kong/rest/pluginbindings.yaml similarity index 100% rename from kong/pluginbindings.yaml rename to kong/rest/pluginbindings.yaml diff --git a/kong/proxy-cache.yaml b/kong/rest/proxy-cache.yaml similarity index 100% rename from kong/proxy-cache.yaml rename to kong/rest/proxy-cache.yaml diff --git a/kong/rate-limiting.yaml b/kong/rest/rate-limiting.yaml similarity index 100% rename from kong/rate-limiting.yaml rename to kong/rest/rate-limiting.yaml diff --git a/kong/routes.yaml b/kong/rest/routes.yaml similarity index 100% rename from kong/routes.yaml rename to kong/rest/routes.yaml diff --git a/kong/service.yaml b/kong/rest/service.yaml similarity index 100% rename from kong/service.yaml rename to kong/rest/service.yaml