diff --git a/README.md b/README.md index b67f620..80c7e83 100644 --- a/README.md +++ b/README.md @@ -48,13 +48,11 @@ The journey can start [here](WALKTHROUGH.md) for a quickstart with a global over ```shell . ├── api-gateway # Traefik Hub API Gateway tutorials -│   ├── 1-getting-started -│   ├── 2-secure-applications +│   ├── 1-getting-started # API Gateway Quick Start Guide +│   ├── 2-expose +│   ├── 3-secure-applications ├── api-management # Traefik Hub API Management tutorials -│   ├── 1-getting-started -│   ├── 2-access-control -│   ├── 3-api-lifecycle-management -│   └── 4-protect-api-infrastructure (WIP) +│   ├── 1-getting-started # API Management Quick Start Guide └── src    ├── api-server # API server source code    └── manifests # Yaml to deploy all apps diff --git a/WALKTHROUGH.md b/WALKTHROUGH.md deleted file mode 100644 index 58c5864..0000000 --- a/WALKTHROUGH.md +++ /dev/null @@ -1,626 +0,0 @@ -# Walkthrough - -This document covers a complete journey: Traefik Proxy => Traefik Hub API Gateway => Traefik Hub API Management - -## Deploy Kubernetes - -For this tutorial, we deploy Traefik Hub API Gateway on a [k3d](https://k3d.io/) cluster. It's possible to use alternatives such as [kind](https://kind.sigs.k8s.io), cloud providers, and others. - -First, clone the GitHub repository dedicated to tutorials: - -```shell -git clone https://github.com/traefik/hub.git -cd hub -``` - -### Using k3d - -```shell -k3d cluster create traefik-hub --port 80:80@loadbalancer --port 443:443@loadbalancer --port 8000:8000@loadbalancer --k3s-arg "--disable=traefik@server:0" -``` - -### Using Kind - -kind requires some configuration to use an IngressController on localhost. See the following example: - -
- -Create the cluster - -Ports need to be mapped for HTTP and HTTPS for kind with this config: - -```yaml -kind: Cluster -apiVersion: kind.x-k8s.io/v1alpha4 -name: traefik-hub -nodes: -- role: control-plane - extraPortMappings: - - containerPort: 30000 - hostPort: 80 - protocol: TCP - - containerPort: 30001 - hostPort: 443 - protocol: TCP -``` - -```shell -kind create cluster --config=src/kind/config.yaml -kubectl cluster-info -kubectl wait --for=condition=ready nodes traefik-hub-control-plane -``` - -And add a load balancer (LB) to it: - -```shell -kubectl apply -f src/kind/metallb-native.yaml -kubectl wait --namespace metallb-system --for=condition=ready pod --selector=app=metallb --timeout=90s -kubectl apply -f src/kind/metallb-config.yaml -``` - -
- -## Step 1: Deploy an API with Traefik Proxy - -First, we will install Traefik Proxy with Helm: - -```shell -# Add the Helm repository -helm repo add --force-update traefik https://traefik.github.io/charts -# Create a namespace -kubectl create namespace traefik -# Install the Helm chart -helm install traefik -n traefik --wait \ - --version v34.4.1 \ - --set ingressClass.enabled=false \ - --set ingressRoute.dashboard.enabled=true \ - --set ingressRoute.dashboard.matchRule='Host(`dashboard.docker.localhost`)' \ - --set ingressRoute.dashboard.entryPoints={web} \ - --set ports.web.nodePort=30000 \ - --set ports.websecure.nodePort=30001 \ - traefik/traefik -``` - -Once it's installed, we can access the local dashboard: http://dashboard.docker.localhost/ - -![Local Traefik Proxy Dashboard](./src/images/dashboard.png) - -Without Traefik Hub, an API can be deployed with an `Ingress`, an `IngressRoute` or a `HTTPRoute`. - -This tutorial implements APIs using a JSON server in Go; the source code is [here](../../src/api-server/). - -Let's deploy a [weather app](../../src/manifests/weather-app.yaml) exposing an API. - -```shell -kubectl apply -f src/manifests/apps-namespace.yaml -kubectl apply -f src/manifests/weather-app.yaml -``` - -It should create the public app: - -```shell -namespace/apps created -configmap/weather-data created -deployment.apps/weather-app created -service/weather-app created -``` - -It can be exposed with an `IngressRoute`: - -```yaml :src/manifests/walkthrough/weather-app-no-auth.yaml ---- -apiVersion: traefik.io/v1alpha1 -kind: IngressRoute -metadata: - name: walkthrough-weather-api-no-auth - namespace: apps -spec: - entryPoints: - - web - routes: - - match: Host(`walkthrough.docker.localhost`) && PathPrefix(`/no-auth`) - kind: Rule - services: - - name: weather-app - port: 3000 - middlewares: - - name: stripprefix-weather -``` - -```shell -kubectl apply -f src/manifests/walkthrough/weather-app-no-auth.yaml -``` - -```shell -ingressroute.traefik.io/walkthrough-weather-api created -``` - -This API can be accessed using curl: - -```shell -curl -s http://walkthrough.docker.localhost/no-auth/weather | jq -``` - -```json -[ - {"city":"GopherTown","id":"0","weather":"Cloudy"}, - {"city":"City of Gophers","id":"1","weather":"Sunny"}, - {"city":"GopherRocks","id":"2","weather":"Cloudy"} -] -``` - -With Traefik Proxy, we can secure the access to this API using the Basic Authentication. To create an encoded _user_:_password_ pair, we can use `htpasswd` with `openssl` to encode it. - -So let's do it: - -```shell -htpasswd -nb foo bar | openssl base64 -``` - -```shell -Zm9vOiRhcHIxJDJHR0RyLjJPJDdUVXJlOEt6anQ1WFFOUGRoby5CQjEKCg== -``` - -```diff :hack/diff.sh -r -a "-Nau src/manifests/walkthrough/weather-app-no-auth.yaml src/manifests/walkthrough/weather-app-basic-auth.yaml" ---- src/manifests/walkthrough/weather-app-no-auth.yaml -+++ src/manifests/walkthrough/weather-app-basic-auth.yaml -@@ -1,17 +1,38 @@ - --- -+apiVersion: v1 -+kind: Secret -+metadata: -+ name: basic-auth -+ namespace: apps -+data: -+ users: | -+ Zm9vOiRhcHIxJDJHR0RyLjJPJDdUVXJlOEt6anQ1WFFOUGRoby5CQjEKCg== -+ -+--- -+apiVersion: traefik.io/v1alpha1 -+kind: Middleware -+metadata: -+ name: basic-auth -+ namespace: apps -+spec: -+ basicAuth: -+ secret: basic-auth -+ -+--- - apiVersion: traefik.io/v1alpha1 - kind: IngressRoute - metadata: -- name: walkthrough-weather-api-no-auth -+ name: walkthrough-weather-api-basic-auth - namespace: apps - spec: - entryPoints: - - web - routes: -- - match: Host(`walkthrough.docker.localhost`) && PathPrefix(`/no-auth`) -- kind: Rule -- services: -- - name: weather-app -- port: 3000 -- middlewares: -- - name: stripprefix-weather -+ - match: Host(`walkthrough.docker.localhost`) && PathPrefix(`/basic-auth`) -+ kind: Rule -+ services: -+ - name: weather-app -+ port: 3000 -+ middlewares: -+ - name: stripprefix-weather -+ - name: basic-auth -``` - -Let's apply it: - -```shell -kubectl apply -f src/manifests/walkthrough/weather-app-basic-auth.yaml -``` - -```shell -secret/basic-auth created -middleware.traefik.io/basic-auth created -ingressroute.traefik.io/walkthrough-weather-api-basic-auth created -``` - -And now, we can confirm it's secured using BASIC Authentication : - -```shell -# This call is not authorized => 401 -curl -i http://walkthrough.docker.localhost/basic-auth/weather -# This call is allowed => 200 -curl -su foo:bar http://walkthrough.docker.localhost/basic-auth/weather | jq -``` - -[Basic Authentication](https://datatracker.ietf.org/doc/html/rfc7617) worked and was widely used in the early days of the web. However, it also has a security risk: -credentials can be visible to any observer when using HTTP. It uses hard-coded credentials, potentially giving more authorization -than required for a specific use case. - -API keys are a convenient and more secure way to protect endpoints. The API Key can be required in an HTTP header, cookie, or query parameter. - -For more advanced use cases, [JSON Web Tokens (JWT)](https://datatracker.ietf.org/doc/html/rfc7519) can be used. A JWT can be cryptographically verified, -it detaches authentication from user credentials and has an issue and expiration date. - -API Key and JWT can be used with Traefik Hub API Gateway, so let's upgrade our setup to Traefik Hub. - -## Step 2: Upgrade Traefik Proxy to Traefik Hub API Gateway - -Log in to the [Traefik Hub Online Dashboard](https://hub.traefik.io), open the page to [generate a new Hub API Gateway](https://hub.traefik.io/gateways/new). - -**:warning: Do not install the Hub API Gateway, but copy the token.** - -Now, open a terminal and run these commands to create the secret for Traefik Hub. - -```shell -export TRAEFIK_HUB_TOKEN= -``` - -```shell -kubectl create secret generic traefik-hub-license --namespace traefik --from-literal=token=$TRAEFIK_HUB_TOKEN -``` - -Then, upgrade Traefik Proxy to Traefik Hub using the same Helm chart: - -```shell -helm upgrade traefik -n traefik --wait \ - --version v34.4.1 \ - --reuse-values \ - --set hub.token=traefik-hub-license \ - --set image.registry=ghcr.io \ - --set image.repository=traefik/traefik-hub \ - --set image.tag=v3.14.1 \ - traefik/traefik -``` - -Traefik Hub is 100% compatible with Traefik Proxy v3. - -The dashboard is still reachable (http://dashboard.docker.localhost/). One can notice now the Traefik Hub API Gateway logo on the top left corner. - -![Local Traefik Hub Dashboard](./src/images/hub-dashboard.png) - -And also confirm _Basic Auth_ is still here: - -```shell -# This call is not authorized => 401 -curl -i http://walkthrough.docker.localhost/basic-auth/weather -# This call is allowed => 200 -curl -su foo:bar http://walkthrough.docker.localhost/basic-auth/weather | jq -``` - -Let's secure the weather API with an API Key. - -With Traefik Hub, we can use API Key as a middleware. First, we'll need to generate hash of our password. It can be done with `htpasswd` : - -```shell -htpasswd -nbs "" "Let's use API Key with Traefik Hub" | cut -c 2- -{SHA}dhiZGvSW60OMQ+J6hPEyJ+jfUoU= -``` - -```shell -{SHA}dhiZGvSW60OMQ+J6hPEyJ+jfUoU= -``` - -We can now put this password in the API Key middleware: - -```diff :hack/diff.sh -r -a "-Nau src/manifests/walkthrough/weather-app-no-auth.yaml src/manifests/walkthrough/weather-app-apikey.yaml" ---- src/manifests/walkthrough/weather-app-no-auth.yaml -+++ src/manifests/walkthrough/weather-app-apikey.yaml -@@ -1,17 +1,42 @@ - --- -+apiVersion: v1 -+kind: Secret -+metadata: -+ name: walkthrough-apikey-auth -+ namespace: apps -+stringData: -+ secretKey: "{SHA}dhiZGvSW60OMQ+J6hPEyJ+jfUoU=" -+ -+--- -+apiVersion: traefik.io/v1alpha1 -+kind: Middleware -+metadata: -+ name: walkthrough-apikey-auth -+ namespace: apps -+spec: -+ plugin: -+ apiKey: -+ keySource: -+ header: Authorization -+ headerAuthScheme: Bearer -+ secretValues: -+ - urn:k8s:secret:walkthrough-apikey-auth:secretKey -+ -+--- - apiVersion: traefik.io/v1alpha1 - kind: IngressRoute - metadata: -- name: walkthrough-weather-api-no-auth -+ name: walkthrough-weather-api-api-key - namespace: apps - spec: - entryPoints: - - web - routes: -- - match: Host(`walkthrough.docker.localhost`) && PathPrefix(`/no-auth`) -- kind: Rule -- services: -- - name: weather-app -- port: 3000 -- middlewares: -- - name: stripprefix-weather -+ - match: Host(`walkthrough.docker.localhost`) && PathPrefix(`/api-key`) -+ kind: Rule -+ services: -+ - name: weather-app -+ port: 3000 -+ middlewares: -+ - name: stripprefix-weather -+ - name: walkthrough-apikey-auth -``` - -Let's apply it: - -```shell -kubectl apply -f src/manifests/walkthrough/weather-app-apikey.yaml -``` - -```shell -secret/walkthrough-apikey-auth created -middleware.traefik.io/walkthrough-apikey-auth created -ingressroute.traefik.io/walkthrough-weather-api-api-key created -``` - -And test it: - -```shell -# This call is not authorized => 401 -curl -i http://walkthrough.docker.localhost/api-key/weather -# Let's set the token -export API_KEY=$(echo -n "Let's use API Key with Traefik Hub" | base64) -# This call with the token is allowed => 200 -curl -s -H "Authorization: Bearer $API_KEY" http://walkthrough.docker.localhost/api-key/weather | jq -``` - -The API is now secured. - -It's possible to handle users with an Identity Provider, but what if we want to cover _internal_ and _external_ use cases? To protect API on HTTP _verb_ level? Or to test a new version with part of the production traffic? - -We'll need Traefik Hub with API Management! - -## Step 3: Manage an API with Traefik Hub API Management - -First, we enable API Management on Traefik Traefik Hub using the same Helm chart: - -```shell -helm upgrade traefik -n traefik --wait \ - --version v34.4.1 \ - --reuse-values \ - --set hub.apimanagement.enabled=true \ - traefik/traefik -``` - -Traefik Hub API Management is 100% compatible with Traefik Proxy v3 and Traefik Hub API Gateway. - -The dashboard is still reachable on http://dashboard.docker.localhost/ - -![Local Traefik Hub Dashboard](./src/images/hub-dashboard.png) - -And also confirm that the API is still secured using an API Key: - -```shell -# This call is not authorized => 401 -curl -i http://walkthrough.docker.localhost/api-key/weather -# This call with the token is allowed => 200 -curl -s -H "Authorization: Bearer $API_KEY" http://walkthrough.docker.localhost/api-key/weather | jq -``` - -Now, let's try to manage it with Traefik Hub using `API` and `APIAccess` resources: - -```yaml :src/manifests/walkthrough/api.yaml -s 1 -e 23 ---- -apiVersion: hub.traefik.io/v1alpha1 -kind: API -metadata: - name: walkthrough-weather-api - namespace: apps -spec: - openApiSpec: - path: /openapi.yaml - override: - servers: - - url: http://api.walkthrough.docker.localhost - ---- -apiVersion: hub.traefik.io/v1alpha1 -kind: APIAccess -metadata: - name: walkthrough-weather-api - namespace: apps -spec: - apis: - - name: walkthrough-weather-api - everyone: true -``` - -We'll need to reference this API in the `IngressRoute` with an annotation: - -```yaml :src/manifests/walkthrough/api.yaml -s 25 -e 41 ---- -apiVersion: traefik.io/v1alpha1 -kind: IngressRoute -metadata: - name: walkthrough-weather-api - namespace: apps - annotations: - hub.traefik.io/api: walkthrough-weather-api # <=== Link to the API using its name -spec: - entryPoints: - - web - routes: - - match: Host(`api.walkthrough.docker.localhost`) && PathPrefix(`/weather`) - kind: Rule - services: - - name: weather-app - port: 3000 -``` - -:information_source: We've also removed the API Key authentication middleware, as we'll use Traefik Hub's built-in identity provider for user and credential management. The API is still secured, as we'll see it shortly. - -Let's apply it: - -```shell -kubectl apply -f src/manifests/walkthrough/api.yaml -``` - -It will create `API`, `APIAccess` and link `IngressRoute` to this API. - -```shell -api.hub.traefik.io/walkthrough-weather-api created -apiaccess.hub.traefik.io/walkthrough-weather-api created -ingressroute.traefik.io/walkthrough-weather-api created -``` - -Now, we can confirm this API is not publicly exposed: - -```shell -curl -i http://api.walkthrough.docker.localhost/weather -``` - -It returns the expected 401 Unauthorized HTTP code: - -```shell -HTTP/1.1 401 Unauthorized -Date: Mon, 06 May 2024 12:09:56 GMT -Content-Length: 0 -``` - -## Step 4: Create a user for this API - -Users are created in the [Traefik Hub Online Dashboard](https://hub.traefik.io/users): - -![Create user admin](./api-management/1-getting-started/images/create-user-admin.png) - -## Step 5: Deploy the API Portal - -The user created previously will connect to an API Portal to generate an API key, so let's deploy the API Portal! - -```yaml :src/manifests/walkthrough/api-portal.yaml ---- -apiVersion: hub.traefik.io/v1alpha1 -kind: APIPortal -metadata: - name: walkthrough-apiportal - namespace: apps -spec: - title: API Portal - description: "Apps Developer Portal" - trustedUrls: - - http://api.walkthrough.docker.localhost - ---- -apiVersion: networking.k8s.io/v1 -kind: Ingress -metadata: - name: walkthrough-apiportal - namespace: traefik - annotations: - # This annotation link this Ingress to the API Portal using @ format. - hub.traefik.io/api-portal: walkthrough-apiportal@apps -spec: - rules: - - host: api.walkthrough.docker.localhost - http: - paths: - - path: / - pathType: Prefix - backend: - service: - name: apiportal - port: - number: 9903 -``` - -:information_source: This API Portal is routed with the internal _ClusterIP_ `Service` named apiportal. - -```shell -kubectl apply -f src/manifests/walkthrough/api-portal.yaml -sleep 60 -``` - -```shell -apiportal.hub.traefik.io/walkthrough-apiportal created -ingress.networking.k8s.io/walkthrough-apiportal created -``` - -The API Portal should be reachable on http://api.walkthrough.docker.localhost - -We log in with the admin user. - -![API Portal Log in](./api-management/1-getting-started/images/api-portal-login.png) - -And create a token for this user: - -![API Portal Create Token](./api-management/1-getting-started/images/api-portal-create-token.png) - -```shell -export ADMIN_TOKEN="XXX" -``` - -Request the API with this token: :tada: - -```shell -curl -s -H "Authorization: Bearer $ADMIN_TOKEN" http://api.walkthrough.docker.localhost/weather | jq -``` - -```json -[ - {"city":"GopherTown","id":"0","weather":"Cloudy"}, - {"city":"City of Gophers","id":"1","weather":"Sunny"}, - {"city":"GopherRocks","id":"2","weather":"Cloudy"} -] -``` - -:information_source: If it fails with 401, wait one minute and try again. The token needs to be sync before it can be accepted by Traefik Hub. - -We can see the API available in the `apps` namespace in the portal. We advise every API to come with an OpenAPI specification (OAS): - -![API Portal with OAS](./api-management/1-getting-started/images/api-portal-with-oas.png) - -However, it's still possible not setting an OAS, but it severely hurts getting started with API consumption. - -This time, we won't specify any OAS in the API _CRD_: - -```yaml :src/manifests/walkthrough/forecast.yaml -s 1 -e 7 ---- -apiVersion: hub.traefik.io/v1alpha1 -kind: API -metadata: - name: walkthrough-weather-api-forecast - namespace: apps -spec: {} -``` - -The other resources are built on the same model, as we can see in [the complete file](https://github.com/traefik/hub/blob/main/api-management/1-getting-started/manifests/forecast.yaml). Let's apply it: - -```shell -kubectl apply -f src/manifests/weather-app-forecast.yaml -kubectl apply -f src/manifests/walkthrough/forecast.yaml -``` - -```shell -api.hub.traefik.io/walkthrough-weather-api-forecast created -apiaccess.hub.traefik.io/walkthrough-weather-api-forecast created -ingressroute.traefik.io/walkthrough-weather-api-forecast created -``` - -Request the API with the token: - -```shell -curl -H "Authorization: Bearer $ADMIN_TOKEN" http://api.walkthrough.docker.localhost/forecast/weather -``` - -And that's it! This time, we have documentation built from the OpenAPI specification, and we can also interactively try the API with the Try Out functionality. - -![API Portal without OAS](./api-management/1-getting-started/images/api-portal-without-oas.png) diff --git a/api-management/1-getting-started/README.md b/api-management/1-getting-started/README.md index 05ca0ba..a9bb4c4 100644 --- a/api-management/1-getting-started/README.md +++ b/api-management/1-getting-started/README.md @@ -1,397 +1,3 @@ # Getting Started -For this tutorial, we deploy Traefik Hub API Gateway on a [k3d](https://k3d.io/) cluster. It's possible to use alternatives such as [kind](https://kind.sigs.k8s.io), cloud providers, and others. - -First, clone the GitHub repository dedicated to tutorials: - -```shell -git clone https://github.com/traefik/hub.git -cd hub -``` - -## Deploy Kubernetes - -### Using k3d - -```shell -k3d cluster create traefik-hub --port 80:80@loadbalancer --port 443:443@loadbalancer --port 8000:8000@loadbalancer --k3s-arg "--disable=traefik@server:0" -``` - -### Using Kind - -kind requires some configuration to use an IngressController on localhost. See the following example: - -
- -Create the cluster - -Ports need to be mapped for HTTP and HTTPS for kind with this config: - -```yaml -kind: Cluster -apiVersion: kind.x-k8s.io/v1alpha4 -name: traefik-hub -nodes: -- role: control-plane - extraPortMappings: - - containerPort: 30000 - hostPort: 80 - protocol: TCP - - containerPort: 30001 - hostPort: 443 - protocol: TCP -``` - -```shell -kind create cluster --config=src/kind/config.yaml -kubectl cluster-info -kubectl wait --for=condition=ready nodes traefik-hub-control-plane -``` - -Now, add a load balancer (LB) to it: - -```shell -kubectl apply -f src/kind/metallb-native.yaml -kubectl wait --namespace metallb-system --for=condition=ready pod --selector=app=metallb --timeout=90s -kubectl apply -f src/kind/metallb-config.yaml -``` - -
- -## Step 1: Install Traefik Hub - -First, log in to the [Traefik Hub Online Dashboard](https://hub.traefik.io) and open the page to [create a new gateway](https://hub.traefik.io/gateways/new). - -**:warning: Do not install the gateway, but copy the token.** - -Then, open a terminal and run these commands to create the required secret: - -```shell -export TRAEFIK_HUB_TOKEN= -``` - -```shell -kubectl create namespace traefik -kubectl create secret generic traefik-hub-license --namespace traefik --from-literal=token=$TRAEFIK_HUB_TOKEN -``` - -Now, install Traefik Hub with Helm: - -```shell -# Add the Helm repository -helm repo add --force-update traefik https://traefik.github.io/charts -# Install the Helm chart -helm install traefik -n traefik --wait \ - --version v34.4.1 \ - --set hub.token=traefik-hub-license \ - --set hub.apimanagement.enabled=true \ - --set ingressClass.enabled=false \ - --set ingressRoute.dashboard.enabled=true \ - --set ingressRoute.dashboard.matchRule='Host(`dashboard.docker.localhost`)' \ - --set ingressRoute.dashboard.entryPoints={web} \ - --set image.registry=ghcr.io \ - --set image.repository=traefik/traefik-hub \ - --set image.tag=v3.14.1 \ - --set ports.web.nodePort=30000 \ - --set ports.websecure.nodePort=30001 \ - traefik/traefik -``` - -**If** Traefik Hub is **already** installed, we can instead upgrade the Traefik Hub instance: - -```shell -# Upgrade CRDs -kubectl apply --server-side --force-conflicts -k https://github.com/traefik/traefik-helm-chart/traefik/crds/ -# Update the Helm repository -helm repo add --force-update traefik https://traefik.github.io/charts -# Upgrade the Helm chart -helm upgrade traefik -n traefik --wait \ - --version v34.4.1 \ - --set hub.token=traefik-hub-license \ - --set hub.apimanagement.enabled=true \ - --set ingressClass.enabled=false \ - --set ingressRoute.dashboard.enabled=true \ - --set ingressRoute.dashboard.matchRule='Host(`dashboard.docker.localhost`)' \ - --set ingressRoute.dashboard.entryPoints={web} \ - --set image.registry=ghcr.io \ - --set image.repository=traefik/traefik-hub \ - --set image.tag=v3.14.1 \ - --set ports.web.nodePort=30000 \ - --set ports.websecure.nodePort=30001 \ - traefik/traefik -``` - -Now, we can access the local dashboard at http://dashboard.docker.localhost/. - -## Step 2: Deploy an API as an Ingress - -:information_source: This tutorial implements API using a JSON server in Go; check out the source code [here](https://github.com/traefik/hub/tree/main/src/api-server/). - -First, let's deploy a [weather app](https://github.com/traefik/hub/blob/main/src/manifests/weather-app.yaml) exposing an API: - -```shell -kubectl apply -f src/manifests/apps-namespace.yaml -kubectl apply -f src/manifests/weather-app.yaml -``` - -It creates the weather app: - -```shell -namespace/apps unchanged -configmap/weather-data unchanged -middleware.traefik.io/stripprefix-weather unchanged -deployment.apps/weather-app unchanged -service/weather-app unchanged -configmap/weather-app-openapispec unchanged -``` - -Then, expose the weather app using an `IngressRoute`: - -```yaml :manifests/weather-app-ingressroute.yaml ---- -apiVersion: traefik.io/v1alpha1 -kind: IngressRoute -metadata: - name: getting-started-apimanagement - namespace: apps -spec: - entryPoints: - - web - routes: - - match: Host(`getting-started.apimanagement.docker.localhost`) - kind: Rule - services: - - name: weather-app - port: 3000 - middlewares: - - name: stripprefix-weather -``` - -```shell -kubectl apply -f api-management/1-getting-started/manifests/weather-app-ingressroute.yaml -``` - -```shell -ingressroute.traefik.io/getting-started-apimanagement created -``` - -At this moment, this API is exposed. It's possible to reach it using `curl` command: - -```shell -curl -s http://getting-started.apimanagement.docker.localhost/weather | jq -``` - -```json -[ - {"city":"City of Gophers","id":"1","weather":"Sunny"}, - {"city":"GopherRocks","id":"2","weather":"Cloudy"}, - {"city":"GopherCity","id":"0","weather":"Moderate rain"} -] -``` - -## Step 3: Manage the API using Traefik Hub API Management - -Let's manage the weather API with Traefik Hub using `API` and `APIAccess` resources: - -```yaml :manifests/api.yaml -s 1 -e 23 ---- -apiVersion: hub.traefik.io/v1alpha1 -kind: API -metadata: - name: getting-started-apimanagement-weather-api - namespace: apps -spec: - openApiSpec: - path: /openapi.yaml - override: - servers: - - url: http://api.getting-started.apimanagement.docker.localhost - ---- -apiVersion: hub.traefik.io/v1alpha1 -kind: APIAccess -metadata: - name: getting-started-apimanagement-weather-api - namespace: apps -spec: - apis: - - name: getting-started-apimanagement-weather-api - everyone: true -``` - -First, reference the `API` in the `IngressRoute` using the dedicated annotation: - -```yaml :manifests/api.yaml -s 25 -e 41 ---- -apiVersion: traefik.io/v1alpha1 -kind: IngressRoute -metadata: - name: getting-started-apimanagement-weather-api - namespace: apps - annotations: - hub.traefik.io/api: getting-started-apimanagement-weather-api # <=== Link to the API using its name -spec: - entryPoints: - - web - routes: - - match: Host(`api.getting-started.apimanagement.docker.localhost`) && PathPrefix(`/weather`) - kind: Rule - services: - - name: weather-app - port: 3000 -``` - -Now, we can apply the above resources: - -```shell -kubectl apply -f api-management/1-getting-started/manifests/api.yaml -``` - -It creates `API`, `APIAccess`, and links `IngressRoute` to the weather API: - -```shell -api.hub.traefik.io/getting-started-apimanagement-weather-api created -apiaccess.hub.traefik.io/getting-started-apimanagement-weather-api created -ingressroute.traefik.io/getting-started-apimanagement-weather-api created -``` - -Now, the API is secured. When someone tries to access the API, it returns the expected `401 Unauthorized` HTTP code: - -```shell -curl -i http://api.getting-started.apimanagement.docker.localhost/weather -``` - -```shell -HTTP/1.1 401 Unauthorized -Date: Mon, 06 May 2024 12:09:56 GMT -Content-Length: 0 -``` - -## Step 4: Create a user for this API - -We can create a user in the [Traefik Hub Online Dashboard](https://hub.traefik.io/users): - -![Create user admin](./images/create-user-admin.png) - -We can provide an API Portal to this user. - -## Step 5: Deploy the API Portal - -An API Portal use the same logic as an API for the routing, using `Ingress` and dedicated annotation. - -:information_source: The portal enforces namespace boundaries and considers only `APIAccess` resources within the same namespace as the `APIPortal`. - -```yaml :manifests/api-portal.yaml ---- -apiVersion: hub.traefik.io/v1alpha1 -kind: APIPortal -metadata: - name: getting-started-apimanagement-apiportal - namespace: apps -spec: - title: API Portal - description: "Apps Developer Portal" - trustedUrls: - - http://api.getting-started.apimanagement.docker.localhost - ---- -apiVersion: networking.k8s.io/v1 -kind: Ingress -metadata: - name: getting-started-apimanagement-apiportal - namespace: traefik - annotations: - # This annotation link this Ingress to the API Portal using @ format. - hub.traefik.io/api-portal: getting-started-apimanagement-apiportal@apps -spec: - rules: - - host: api.getting-started.apimanagement.docker.localhost - http: - paths: - - path: / - pathType: Prefix - backend: - service: - name: apiportal - port: - number: 9903 -``` - -:information_source: This API Portal is routed with the internal _ClusterIP_ `Service` named _apiportal_ provided with the Helm Chart. - -```shell -kubectl apply -f api-management/1-getting-started/manifests/api-portal.yaml -sleep 30 -``` - -```shell -apiportal.hub.traefik.io/getting-started-apimanagement-apiportal created -ingress.networking.k8s.io/getting-started-apimanagement-apiportal created -``` - -The API Portal is reachable on http://api.getting-started.apimanagement.docker.localhost. - -Now, we should be able to log in with the admin user and create a token for the user: - -![API Portal Log in](./images/api-portal-login.png) - -![API Portal Create Token](./images/api-portal-create-token.png) - -```shell -export ADMIN_TOKEN= -``` - -The weather API is reachable with this token set as header :tada: : - -```shell -curl -s -H "Authorization: Bearer $ADMIN_TOKEN" http://api.getting-started.apimanagement.docker.localhost/weather | jq -``` - -```json -[ - {"city":"City of Gophers","id":"1","weather":"Sunny"}, - {"city":"GopherRocks","id":"2","weather":"Cloudy"}, - {"city":"GopherCity","id":"0","weather":"Moderate rain"} -] -``` - -:information_source: If it fails with 401, wait a minute and try again. The token needs to be sync before it can be accepted by Traefik Hub. - -We can see the API available in the `apps` namespace in the portal. We advise every API to come with an OpenAPI specification (OAS): - -![API Portal with OAS](./images/api-portal-with-oas.png) - -However, it's still possible not setting an OAS, but it severely hurts getting started with API consumption. -Let's deploy a [forecast app](https://github.com/traefik/hub/blob/main/src/manifests/weather-app-forecast.yaml) without an OpenAPI specification: - -```shell -kubectl apply -f src/manifests/weather-app-forecast.yaml -``` - -This time, we will specify how to get this openapi spec in API _CRD_: - -```yaml :manifests/forecast.yaml -s 1 -e 7 ---- -apiVersion: hub.traefik.io/v1alpha1 -kind: API -metadata: - name: getting-started-apimanagement-weather-api-forecast - namespace: apps -spec: {} -``` - -The other resources are built on the same model, as we can see in [the complete file](https://github.com/traefik/hub/blob/main/api-management/1-getting-started/manifests/forecast.yaml). Let's apply it: - -```shell -kubectl apply -f api-management/1-getting-started/manifests/forecast.yaml -``` - -```shell -api.hub.traefik.io/getting-started-apimanagement-weather-api-forecast created -apiaccess.hub.traefik.io/getting-started-apimanagement-weather-api-forecast created -ingressroute.traefik.io/getting-started-apimanagement-weather-api-forecast created -``` - -And that's it! This time, we have documentation built from the OpenAPI specification, and we can also interactively try the API with the Try Out functionality. - -![API Portal without OAS](./images/api-portal-without-oas.png) +Please refer to the [Traefik Hub API Management quick start guide](https://doc.traefik.io/traefik-hub/api-management/quick-start-guide) tutorial. diff --git a/api-management/1-getting-started/manifests/api.yaml b/api-management/1-getting-started/manifests/api.yaml deleted file mode 100644 index 6070738..0000000 --- a/api-management/1-getting-started/manifests/api.yaml +++ /dev/null @@ -1,41 +0,0 @@ ---- -apiVersion: hub.traefik.io/v1alpha1 -kind: API -metadata: - name: getting-started-apimanagement-weather-api - namespace: apps -spec: - openApiSpec: - path: /openapi.yaml - override: - servers: - - url: http://api.getting-started.apimanagement.docker.localhost - ---- -apiVersion: hub.traefik.io/v1alpha1 -kind: APIAccess -metadata: - name: getting-started-apimanagement-weather-api - namespace: apps -spec: - apis: - - name: getting-started-apimanagement-weather-api - everyone: true - ---- -apiVersion: traefik.io/v1alpha1 -kind: IngressRoute -metadata: - name: getting-started-apimanagement-weather-api - namespace: apps - annotations: - hub.traefik.io/api: getting-started-apimanagement-weather-api # <=== Link to the API using its name -spec: - entryPoints: - - web - routes: - - match: Host(`api.getting-started.apimanagement.docker.localhost`) && PathPrefix(`/weather`) - kind: Rule - services: - - name: weather-app - port: 3000 diff --git a/api-management/2-access-control/README.md b/api-management/2-access-control/README.md deleted file mode 100644 index 2705e21..0000000 --- a/api-management/2-access-control/README.md +++ /dev/null @@ -1,364 +0,0 @@ -# Access Control - -In this tutorial, we will see how to control access to APIs. - -## Simple access control - -Let's try a simple example start. Let's say we want to give separate access between an (internal) admin user and an external user with a subscription. - -```mermaid ---- -title: Simple access control ---- -graph LR - admin-user[Admin User] --> admin-app(admin API) - external-user[External User] --> weather-app(weather API) - - %% CSS - classDef apis fill:#326ce5,stroke:#fff,stroke-width:4px,color:#fff; - classDef users fill:#fff,stroke:#bbb,stroke-width:2px,color:#326ce5; - class admin-user,external-user users; - class admin-app,private-app apis; -``` - -First, we will deploy the _weather_ app and the _admin_ app: - -```shell -kubectl apply -f src/manifests/apps-namespace.yaml -kubectl apply -f src/manifests/weather-app.yaml -kubectl apply -f src/manifests/admin-app.yaml -``` - -To ensure isolation between access, there is a versatile `APIAccess` CRD, allowing the linking of user groups and APIs. So, let's declare the _admin_ API with its `APIAccess`: - -```yaml :manifests/simple-admin-api.yaml ---- -apiVersion: hub.traefik.io/v1alpha1 -kind: API -metadata: - name: access-control-apimanagement-simple-admin - namespace: admin -spec: {} - ---- -apiVersion: hub.traefik.io/v1alpha1 -kind: APIAccess -metadata: - name: access-control-apimanagement-simple-admin - namespace: admin -spec: - groups: # <=== Allow access only for this group - - admin - apis: # <=== Select only this API - - name: access-control-apimanagement-simple-admin - ---- -apiVersion: traefik.io/v1alpha1 -kind: IngressRoute -metadata: - name: access-control-apimanagement-simple-admin - namespace: admin - annotations: - hub.traefik.io/api: access-control-apimanagement-simple-admin -spec: - entryPoints: - - web - routes: - - match: Host(`api.access-control.apimanagement.docker.localhost`) && PathPrefix(`/simple/admin`) - kind: Rule - services: - - name: admin-app - port: 3000 - middlewares: - - name: stripprefix-admin -``` - -```shell -kubectl apply -f api-management/2-access-control/manifests/simple-admin-api.yaml -``` - -```shell -api.hub.traefik.io/access-control-apimanagement-simple-admin created -apiaccess.hub.traefik.io/access-control-apimanagement-simple-admin created -ingressroute.traefik.io/access-control-apimanagement-simple-admin created -``` - -For the _external_ `API` with its `APIAccess`, we'll see how to use a label selector: - -```yaml :manifests/simple-weather-api.yaml ---- -apiVersion: hub.traefik.io/v1alpha1 -kind: API -metadata: - name: access-control-apimanagement-simple-weather - namespace: apps - labels: - subscription: standard -spec: - openApiSpec: - path: /openapi.yaml - ---- -apiVersion: hub.traefik.io/v1alpha1 -kind: APIAccess -metadata: - name: access-control-apimanagement-simple-weather - namespace: apps -spec: - groups: - - external - apiSelector: # <======== Select all APIs with label subscription=external - matchLabels: - subscription: standard - ---- -apiVersion: traefik.io/v1alpha1 -kind: IngressRoute -metadata: - name: access-control-apimanagement-simple-weather - namespace: apps - annotations: - hub.traefik.io/api: access-control-apimanagement-simple-weather -spec: - entryPoints: - - web - routes: - - match: Host(`api.access-control.apimanagement.docker.localhost`) && PathRegexp(`^/simple/weather(/([0-9]+|openapi.yaml))?$`) - kind: Rule - services: - - name: weather-app - port: 3000 - middlewares: - - name: stripprefix-weather -``` - -```shell -kubectl apply -f api-management/2-access-control/manifests/simple-weather-api.yaml -``` - -```shell -api.hub.traefik.io/access-control-apimanagement-simple-weather created -apiaccess.hub.traefik.io/access-control-apimanagement-simple-weather created -ingressroute.traefik.io/access-control-apimanagement-simple-weather created -``` - -### Test it - -First, we'll need to create the _admin_ user in the _admin_ group and the _external_ user in the _external_ group, following instructions in [getting started](../1-getting-started/README.md) - -Now, we can test it with the users' API tokens. - -```shell -export ADMIN_TOKEN= -export EXTERNAL_TOKEN= -``` - -```shell -# This call is allowed => 200 -curl -i -H "Authorization: Bearer $ADMIN_TOKEN" "http://api.access-control.apimanagement.docker.localhost/simple/admin" -# This call is forbidden => 403 -curl -i -H "Authorization: Bearer $ADMIN_TOKEN" "http://api.access-control.apimanagement.docker.localhost/simple/weather" -``` - -```shell -# This call is allowed => 200 -curl -i -H "Authorization: Bearer $EXTERNAL_TOKEN" "http://api.access-control.apimanagement.docker.localhost/simple/weather" -# This call is forbidden => 403 -curl -i -H "Authorization: Bearer $EXTERNAL_TOKEN" "http://api.access-control.apimanagement.docker.localhost/simple/admin" -``` - -:information_source: If it fails, just wait one minute and try again. The token needs to be sync before it can be accepted by Traefik Hub. - -## Advanced access control - -This second example is more complex, but it's also more secure, using Operation Filters. - -* _admin_ can get and update weather data on **private-app** and access without restriction on **admin-app** -* _external_ user can only get data on **weather-app** - -```mermaid ---- -title: Advanced access control ---- -graph LR - admin-user[Admin User] -->|ALL| admin-app(admin API) - admin-user -->|GET,PUT| weather-app - external-user[External User] --> |GET| weather-app(weather API) - - %% CSS - classDef apis fill:#326ce5,stroke:#fff,stroke-width:4px,color:#fff; - classDef users fill:#fff,stroke:#bbb,stroke-width:2px,color:#326ce5; - class admin-user,external-user users; - class admin-app,weather-app apis; -``` - -One needs to define operationSets to configure operationFilters. Here, we'll differentiate **GET** and **PATCH** HTTP methods. - -```diff :../../hack/diff.sh -r -a "manifests/simple-weather-api.yaml manifests/complex-weather-api.yaml" ---- manifests/simple-weather-api.yaml -+++ manifests/complex-weather-api.yaml -@@ -2,40 +2,69 @@ - apiVersion: hub.traefik.io/v1alpha1 - kind: API - metadata: -- name: access-control-apimanagement-simple-weather -+ name: access-control-apimanagement-complex-weather - namespace: apps - labels: - subscription: standard - spec: - openApiSpec: - path: /openapi.yaml -+ operationSets: -+ - name: get-forecast -+ matchers: -+ - pathPrefix: "/weather" -+ methods: [ "GET" ] -+ - name: patch-forecast -+ matchers: -+ - pathPrefix: "/weather/0" -+ methods: [ "PATCH" ] - - --- - apiVersion: hub.traefik.io/v1alpha1 - kind: APIAccess - metadata: -- name: access-control-apimanagement-simple-weather -+ name: access-control-apimanagement-complex-weather-external - namespace: apps - spec: - groups: - - external -- apiSelector: # <======== Select all APIs with label subscription=external -+ apiSelector: - matchLabels: - subscription: standard -+ operationFilter: -+ include: -+ - get-forecast -+ -+--- -+apiVersion: hub.traefik.io/v1alpha1 -+kind: APIAccess -+metadata: -+ name: access-control-apimanagement-complex-weather-admin -+ namespace: apps -+spec: -+ groups: -+ - admin -+ apiSelector: -+ matchLabels: -+ subscription: standard -+ operationFilter: -+ include: -+ - get-forecast -+ - patch-forecast - - --- - apiVersion: traefik.io/v1alpha1 - kind: IngressRoute - metadata: -- name: access-control-apimanagement-simple-weather -+ name: access-control-apimanagement-complex-weather - namespace: apps - annotations: -- hub.traefik.io/api: access-control-apimanagement-simple-weather -+ hub.traefik.io/api: access-control-apimanagement-complex-weather - spec: - entryPoints: - - web - routes: -- - match: Host(`api.access-control.apimanagement.docker.localhost`) && PathRegexp(`^/simple/weather(/([0-9]+|openapi.yaml))?$`) -+ - match: Host(`api.access-control.apimanagement.docker.localhost`) && PathRegexp(`^/complex/weather(/([0-9]+|openapi.yaml))?$`) - kind: Rule - services: - - name: weather-app -``` - -### Deploy and test it - -After deploying it: - -```shell -kubectl apply -f api-management/2-access-control/manifests/complex-weather-api.yaml -kubectl apply -f api-management/2-access-control/manifests/complex-admin-api.yaml -``` - -It can be tested with the API token of the admin: - -```shell -# This call is allowed. -curl -i -H "Authorization: Bearer $ADMIN_TOKEN" "http://api.access-control.apimanagement.docker.localhost/complex/admin" -# This call is now allowed -curl -i -H "Authorization: Bearer $ADMIN_TOKEN" "http://api.access-control.apimanagement.docker.localhost/complex/weather" -# And even PATCH is allowed -curl -i -XPATCH -H "Authorization: Bearer $ADMIN_TOKEN" "http://api.access-control.apimanagement.docker.localhost/complex/weather/0" -d '[{"op": "replace", "path": "/city", "value": "GopherTown"}]' -``` - -And test it with the external user's token: - -```shell -# This one is allowed -curl -i -H "Authorization: Bearer $EXTERNAL_TOKEN" "http://api.access-control.apimanagement.docker.localhost/complex/weather" -# And PATCH should be not allowed -curl -i -XPATCH -H "Authorization: Bearer $EXTERNAL_TOKEN" "http://api.access-control.apimanagement.docker.localhost/complex/weather/0" -d '[{"op": "replace", "path": "/weather", "value": "Cloudy"}]' -``` - -It can be explained quite easily if **PATCH** is still allowed. There is still an `APIAccess` created with the simple tutorial: - -```yaml -kubectl get apiaccess -n apps -NAME AGE -getting-started-apimanagement-weather-api 86m -getting-started-apimanagement-weather-api-forecast 9m1s -access-control-apimanagement-simple-weather 6m15s -access-control-apimanagement-complex-weather-external 54s -access-control-apimanagement-complex-weather-admin 54s -``` - -It means that for the `external` user group, there are two `APIAccess` applying: - -This is the first one: - -```yaml :manifests/simple-weather-api.yaml -s 13 -e 24 ---- -apiVersion: hub.traefik.io/v1alpha1 -kind: APIAccess -metadata: - name: access-control-apimanagement-simple-weather - namespace: apps -spec: - groups: - - external - apiSelector: # <======== Select all APIs with label subscription=external - matchLabels: - subscription: standard -``` - -And this is the second one: - -```yaml :manifests/complex-weather-api.yaml -s 22 -e 36 ---- -apiVersion: hub.traefik.io/v1alpha1 -kind: APIAccess -metadata: - name: access-control-apimanagement-complex-weather-external - namespace: apps -spec: - groups: - - external - apiSelector: - matchLabels: - subscription: standard - operationFilter: - include: - - get-forecast -``` - -The first one allows all kinds of HTTP requests. If we delete it, the _external_ user can no longer call the API with the **PATCH** HTTP verb. - -```shell -kubectl delete apiaccess -n apps access-control-apimanagement-simple-weather -# This time, PATCH is not allowed -curl -i -XPATCH -H "Authorization: Bearer $EXTERNAL_TOKEN" "http://api.access-control.apimanagement.docker.localhost/complex/weather/0" -d '[{"op": "replace", "path": "/weather", "value": "Cloudy"}]' -``` diff --git a/api-management/2-access-control/manifests/complex-admin-api.yaml b/api-management/2-access-control/manifests/complex-admin-api.yaml deleted file mode 100644 index 53c77a8..0000000 --- a/api-management/2-access-control/manifests/complex-admin-api.yaml +++ /dev/null @@ -1,39 +0,0 @@ ---- -apiVersion: hub.traefik.io/v1alpha1 -kind: API -metadata: - name: access-control-apimanagement-complex-admin - namespace: admin -spec: {} - ---- -apiVersion: hub.traefik.io/v1alpha1 -kind: APIAccess -metadata: - name: access-control-apimanagement-complex-admin - namespace: admin -spec: - groups: # <=== Allow access only for this group - - admin - apis: # <=== Select only this API - - name: access-control-apimanagement-complex-admin - ---- -apiVersion: traefik.io/v1alpha1 -kind: IngressRoute -metadata: - name: access-control-apimanagement-complex-admin - namespace: admin - annotations: - hub.traefik.io/api: access-control-apimanagement-complex-admin -spec: - entryPoints: - - web - routes: - - match: Host(`api.access-control.apimanagement.docker.localhost`) && PathPrefix(`/complex/admin`) - kind: Rule - services: - - name: admin-app - port: 3000 - middlewares: - - name: stripprefix-admin diff --git a/api-management/2-access-control/manifests/complex-weather-api.yaml b/api-management/2-access-control/manifests/complex-weather-api.yaml deleted file mode 100644 index cb2e77b..0000000 --- a/api-management/2-access-control/manifests/complex-weather-api.yaml +++ /dev/null @@ -1,73 +0,0 @@ ---- -apiVersion: hub.traefik.io/v1alpha1 -kind: API -metadata: - name: access-control-apimanagement-complex-weather - namespace: apps - labels: - subscription: standard -spec: - openApiSpec: - path: /openapi.yaml - operationSets: - - name: get-forecast - matchers: - - pathPrefix: "/weather" - methods: [ "GET" ] - - name: patch-forecast - matchers: - - pathPrefix: "/weather/0" - methods: [ "PATCH" ] - ---- -apiVersion: hub.traefik.io/v1alpha1 -kind: APIAccess -metadata: - name: access-control-apimanagement-complex-weather-external - namespace: apps -spec: - groups: - - external - apiSelector: - matchLabels: - subscription: standard - operationFilter: - include: - - get-forecast - ---- -apiVersion: hub.traefik.io/v1alpha1 -kind: APIAccess -metadata: - name: access-control-apimanagement-complex-weather-admin - namespace: apps -spec: - groups: - - admin - apiSelector: - matchLabels: - subscription: standard - operationFilter: - include: - - get-forecast - - patch-forecast - ---- -apiVersion: traefik.io/v1alpha1 -kind: IngressRoute -metadata: - name: access-control-apimanagement-complex-weather - namespace: apps - annotations: - hub.traefik.io/api: access-control-apimanagement-complex-weather -spec: - entryPoints: - - web - routes: - - match: Host(`api.access-control.apimanagement.docker.localhost`) && PathRegexp(`^/complex/weather(/([0-9]+|openapi.yaml))?$`) - kind: Rule - services: - - name: weather-app - port: 3000 - middlewares: - - name: stripprefix-weather diff --git a/api-management/2-access-control/manifests/simple-admin-api.yaml b/api-management/2-access-control/manifests/simple-admin-api.yaml deleted file mode 100644 index 1d64615..0000000 --- a/api-management/2-access-control/manifests/simple-admin-api.yaml +++ /dev/null @@ -1,39 +0,0 @@ ---- -apiVersion: hub.traefik.io/v1alpha1 -kind: API -metadata: - name: access-control-apimanagement-simple-admin - namespace: admin -spec: {} - ---- -apiVersion: hub.traefik.io/v1alpha1 -kind: APIAccess -metadata: - name: access-control-apimanagement-simple-admin - namespace: admin -spec: - groups: # <=== Allow access only for this group - - admin - apis: # <=== Select only this API - - name: access-control-apimanagement-simple-admin - ---- -apiVersion: traefik.io/v1alpha1 -kind: IngressRoute -metadata: - name: access-control-apimanagement-simple-admin - namespace: admin - annotations: - hub.traefik.io/api: access-control-apimanagement-simple-admin -spec: - entryPoints: - - web - routes: - - match: Host(`api.access-control.apimanagement.docker.localhost`) && PathPrefix(`/simple/admin`) - kind: Rule - services: - - name: admin-app - port: 3000 - middlewares: - - name: stripprefix-admin diff --git a/api-management/2-access-control/manifests/simple-weather-api.yaml b/api-management/2-access-control/manifests/simple-weather-api.yaml deleted file mode 100644 index 0f3103a..0000000 --- a/api-management/2-access-control/manifests/simple-weather-api.yaml +++ /dev/null @@ -1,44 +0,0 @@ ---- -apiVersion: hub.traefik.io/v1alpha1 -kind: API -metadata: - name: access-control-apimanagement-simple-weather - namespace: apps - labels: - subscription: standard -spec: - openApiSpec: - path: /openapi.yaml - ---- -apiVersion: hub.traefik.io/v1alpha1 -kind: APIAccess -metadata: - name: access-control-apimanagement-simple-weather - namespace: apps -spec: - groups: - - external - apiSelector: # <======== Select all APIs with label subscription=external - matchLabels: - subscription: standard - ---- -apiVersion: traefik.io/v1alpha1 -kind: IngressRoute -metadata: - name: access-control-apimanagement-simple-weather - namespace: apps - annotations: - hub.traefik.io/api: access-control-apimanagement-simple-weather -spec: - entryPoints: - - web - routes: - - match: Host(`api.access-control.apimanagement.docker.localhost`) && PathRegexp(`^/simple/weather(/([0-9]+|openapi.yaml))?$`) - kind: Rule - services: - - name: weather-app - port: 3000 - middlewares: - - name: stripprefix-weather diff --git a/api-management/3-api-lifecycle-management/README.md b/api-management/3-api-lifecycle-management/README.md deleted file mode 100644 index 2e6e9eb..0000000 --- a/api-management/3-api-lifecycle-management/README.md +++ /dev/null @@ -1,388 +0,0 @@ -# API LifeCycle Management - -In this tutorial, we will see how to publish a new version of an API. - -## Deploy an API - -First, we'll deploy the API in _getting started_: - -```shell -kubectl apply -f src/manifests/apps-namespace.yaml -kubectl apply -f src/manifests/weather-app.yaml -kubectl apply -f api-management/3-api-lifecycle-management/manifests/api.yaml -``` - -```shell -namespace/apps unchanged -configmap/weather-data unchanged -middleware.traefik.io/stripprefix-weather unchanged -deployment.apps/weather-app unchanged -service/weather-app unchanged -configmap/weather-app-openapispec unchanged -api.hub.traefik.io/api-lifecycle-apimanagement-weather-api created -apiaccess.hub.traefik.io/api-lifecycle-apimanagement-weather-api created -ingressroute.traefik.io/api-lifecycle-apimanagement-weather-api created -``` - -And confirms it works as expected: - -```shell -export ADMIN_TOKEN= -``` - -```shell -# This call is not allowed -curl -i http://api.lifecycle.apimanagement.docker.localhost/weather -# This call is allowed -curl -s -H "Authorization: Bearer $ADMIN_TOKEN" http://api.lifecycle.apimanagement.docker.localhost/weather | jq -``` - -## Publish the first API Version - -To use API Version features, we'll need to: - -1. Declare an `APIVersion` -2. Reference it into the `API` -3. Use it in the routing - -```diff :../../hack/diff.sh -r -a "manifests/api.yaml manifests/api-v1.yaml" ---- manifests/api.yaml -+++ manifests/api-v1.yaml -@@ -1,41 +1,54 @@ - --- - apiVersion: hub.traefik.io/v1alpha1 --kind: API -+kind: APIVersion - metadata: -- name: api-lifecycle-apimanagement-weather-api -+ name: api-lifecycle-apimanagement-weather-api-v1 - namespace: apps - spec: -+ release: v1.0.0 - openApiSpec: - path: /openapi.yaml - override: - servers: -- - url: http://api.lifecycle.apimanagement.docker.localhost -+ - url: http://api.lifecycle.apimanagement.docker.localhost/weather-v1 -+ -+--- -+apiVersion: hub.traefik.io/v1alpha1 -+kind: API -+metadata: -+ name: api-lifecycle-apimanagement-weather-api-v1 -+ namespace: apps -+spec: -+ versions: -+ - name: api-lifecycle-apimanagement-weather-api-v1 - - --- - apiVersion: hub.traefik.io/v1alpha1 - kind: APIAccess - metadata: -- name: api-lifecycle-apimanagement-weather-api -+ name: api-lifecycle-apimanagement-weather-api-v1 - namespace: apps - spec: - apis: -- - name: api-lifecycle-apimanagement-weather-api -+ - name: api-lifecycle-apimanagement-weather-api-v1 - everyone: true - - --- - apiVersion: traefik.io/v1alpha1 - kind: IngressRoute - metadata: -- name: api-lifecycle-apimanagement-weather-api -+ name: api-lifecycle-apimanagement-weather-api-v1 - namespace: apps - annotations: -- hub.traefik.io/api: api-lifecycle-apimanagement-weather-api # <=== Link to the API using its name -+ hub.traefik.io/api-version: api-lifecycle-apimanagement-weather-api-v1 - spec: - entryPoints: - - web - routes: -- - match: Host(`api.lifecycle.apimanagement.docker.localhost`) && PathPrefix(`/weather`) -+ - match: Host(`api.lifecycle.apimanagement.docker.localhost`) && PathPrefix(`/weather-v1`) - kind: Rule - services: - - name: weather-app - port: 3000 -+ middlewares: -+ - name: stripprefix-weather -``` - -We can apply it: - -```shell -kubectl apply -f api-management/3-api-lifecycle-management/manifests/api-v1.yaml -``` - -```shell -apiversion.hub.traefik.io/api-lifecycle-apimanagement-weather-api-v1 created -api.hub.traefik.io/api-lifecycle-apimanagement-weather-api-v1 created -apiaccess.hub.traefik.io/api-lifecycle-apimanagement-weather-api-v1 created -ingressroute.traefik.io/api-lifecycle-apimanagement-weather-api-v1 created -``` - -And confirm it's still working: - -```shell -# This call is not allowed -curl -i http://api.lifecycle.apimanagement.docker.localhost/weather-v1/weather -# This call is allowed -curl -s -H "Authorization: Bearer $ADMIN_TOKEN" http://api.lifecycle.apimanagement.docker.localhost/weather-v1/weather | jq -``` - -## Publish a second API Version - -Now, let's say a new version is available. We'll need to test whether everything is OK before making it go to production. - -So, for this second API Version, we'll need to: - -1. Deploy this new version -2. Declare an `APIVersion` -3. Reference it into the `API` -4. Create a new `IngressRoute` requiring a special header - -```diff :../../hack/diff.sh -r -a "manifests/api-v1.yaml manifests/api-v1.1.yaml" ---- manifests/api-v1.yaml -+++ manifests/api-v1.1.yaml -@@ -2,42 +2,38 @@ - apiVersion: hub.traefik.io/v1alpha1 - kind: APIVersion - metadata: -- name: api-lifecycle-apimanagement-weather-api-v1 -+ name: api-lifecycle-apimanagement-weather-api-v1-1 - namespace: apps - spec: -- release: v1.0.0 -- openApiSpec: -- path: /openapi.yaml -- override: -- servers: -- - url: http://api.lifecycle.apimanagement.docker.localhost/weather-v1 -+ release: v1.1.0 - - --- - apiVersion: hub.traefik.io/v1alpha1 - kind: API - metadata: -- name: api-lifecycle-apimanagement-weather-api-v1 -+ name: api-lifecycle-apimanagement-weather-api-v1-1 - namespace: apps - spec: - versions: - - name: api-lifecycle-apimanagement-weather-api-v1 -+ - name: api-lifecycle-apimanagement-weather-api-v1-1 - - --- - apiVersion: hub.traefik.io/v1alpha1 - kind: APIAccess - metadata: -- name: api-lifecycle-apimanagement-weather-api-v1 -+ name: api-lifecycle-apimanagement-weather-api-v1-1 - namespace: apps - spec: - apis: -- - name: api-lifecycle-apimanagement-weather-api-v1 -+ - name: api-lifecycle-apimanagement-weather-api-v1-1 - everyone: true - - --- - apiVersion: traefik.io/v1alpha1 - kind: IngressRoute - metadata: -- name: api-lifecycle-apimanagement-weather-api-v1 -+ name: api-lifecycle-apimanagement-weather-api-v1-1 - namespace: apps - annotations: - hub.traefik.io/api-version: api-lifecycle-apimanagement-weather-api-v1 -@@ -45,10 +41,30 @@ - entryPoints: - - web - routes: -- - match: Host(`api.lifecycle.apimanagement.docker.localhost`) && PathPrefix(`/weather-v1`) -+ - match: Host(`api.lifecycle.apimanagement.docker.localhost`) && PathPrefix(`/weather-multi-versions`) - kind: Rule - services: - - name: weather-app - port: 3000 - middlewares: - - name: stripprefix-weather -+ -+--- -+apiVersion: traefik.io/v1alpha1 -+kind: IngressRoute -+metadata: -+ name: api-lifecycle-apimanagement-weather-api-v1-1-preview -+ namespace: apps -+ annotations: -+ hub.traefik.io/api-version: api-lifecycle-apimanagement-weather-api-v1-1 -+spec: -+ entryPoints: -+ - web -+ routes: -+ - match: Host(`api.lifecycle.apimanagement.docker.localhost`) && PathPrefix(`/weather-multi-versions`) && Header(`X-Version`, `preview`) -+ kind: Rule -+ services: -+ - name: weather-app-forecast -+ port: 3000 -+ middlewares: -+ - name: stripprefix-weather -``` - -So let's do it: - -```shell -kubectl apply -f src/manifests/weather-app-forecast.yaml -kubectl apply -f api-management/3-api-lifecycle-management/manifests/api-v1.1.yaml -``` - -```shell -configmap/weather-app-forecast-data unchanged -deployment.apps/weather-app-forecast unchanged -service/weather-app-forecast unchanged -apiversion.hub.traefik.io/api-lifecycle-apimanagement-weather-api-v1-1 created -api.hub.traefik.io/api-lifecycle-apimanagement-weather-api-v1-1 created -apiaccess.hub.traefik.io/api-lifecycle-apimanagement-weather-api-v1-1 created -ingressroute.traefik.io/api-lifecycle-apimanagement-weather-api-v1-1 created -ingressroute.traefik.io/api-lifecycle-apimanagement-weather-api-v1-1-preview created -``` - -Now, we can test if it works: - -```shell -# Even with preview X-Version header, it should return 401 without token -curl -i -H "X-Version: preview" http://api.lifecycle.apimanagement.docker.localhost/weather-multi-versions/weather -# Regular access => returns weather data -curl -s -H "Authorization: Bearer $ADMIN_TOKEN" http://api.lifecycle.apimanagement.docker.localhost/weather-multi-versions/weather | jq -# Preview access, with special header => returns forecast data -curl -s -H "X-Version: preview" -H "Authorization: Bearer $ADMIN_TOKEN" http://api.lifecycle.apimanagement.docker.localhost/weather-multi-versions/weather | jq -``` - -To go further, one can use this pattern with other Traefik Middlewares to route versions based on many parameters: path, query, content type, clientIP, basicAuth, forwardAuth, and many others! - -## Try the new version with a part of the traffic - -Once this new version is adequately tested, we'll want to put it in production. We'll distribute the traffic among the two versions to see if it can handle the load. - -To achieve this goal, we'll need to: - -1. Remove test `IngressRoute` weather-api-v1-1 -2. Declare a Weighted Round Robin TraefikService for load balancing -3. Use it in the `IngressRoute` - -Since the last step, the diff is looking like this: - -```diff :../../hack/diff.sh -r -a "manifests/api-v1.1.yaml manifests/api-v1.1-weighted.yaml" ---- manifests/api-v1.1.yaml -+++ manifests/api-v1.1-weighted.yaml -@@ -1,59 +1,24 @@ - --- --apiVersion: hub.traefik.io/v1alpha1 --kind: APIVersion --metadata: -- name: api-lifecycle-apimanagement-weather-api-v1-1 -- namespace: apps --spec: -- release: v1.1.0 -- ----- --apiVersion: hub.traefik.io/v1alpha1 --kind: API --metadata: -- name: api-lifecycle-apimanagement-weather-api-v1-1 -- namespace: apps --spec: -- versions: -- - name: api-lifecycle-apimanagement-weather-api-v1 -- - name: api-lifecycle-apimanagement-weather-api-v1-1 -- ----- --apiVersion: hub.traefik.io/v1alpha1 --kind: APIAccess --metadata: -- name: api-lifecycle-apimanagement-weather-api-v1-1 -- namespace: apps --spec: -- apis: -- - name: api-lifecycle-apimanagement-weather-api-v1-1 -- everyone: true -- ----- - apiVersion: traefik.io/v1alpha1 --kind: IngressRoute -+kind: TraefikService - metadata: -- name: api-lifecycle-apimanagement-weather-api-v1-1 -+ name: api-lifecycle-apimanagement-weather-api-wrr - namespace: apps -- annotations: -- hub.traefik.io/api-version: api-lifecycle-apimanagement-weather-api-v1 - spec: -- entryPoints: -- - web -- routes: -- - match: Host(`api.lifecycle.apimanagement.docker.localhost`) && PathPrefix(`/weather-multi-versions`) -- kind: Rule -+ weighted: - services: -- - name: weather-app -- port: 3000 -- middlewares: -- - name: stripprefix-weather -+ - name: weather-app -+ port: 3000 -+ weight: 1 -+ - name: weather-app-forecast -+ port: 3000 -+ weight: 1 - - --- - apiVersion: traefik.io/v1alpha1 - kind: IngressRoute - metadata: -- name: api-lifecycle-apimanagement-weather-api-v1-1-preview -+ name: weather-api - namespace: apps - annotations: - hub.traefik.io/api-version: api-lifecycle-apimanagement-weather-api-v1-1 -@@ -61,10 +26,11 @@ - entryPoints: - - web - routes: -- - match: Host(`api.lifecycle.apimanagement.docker.localhost`) && PathPrefix(`/weather-multi-versions`) && Header(`X-Version`, `preview`) -+ - match: Host(`api.lifecycle.apimanagement.docker.localhost`) && PathPrefix(`/weather-v1-wrr`) - kind: Rule - services: -- - name: weather-app-forecast -+ - name: api-lifecycle-apimanagement-weather-api-wrr - port: 3000 -+ kind: TraefikService - middlewares: - - name: stripprefix-weather -``` - -Let's apply it: - -```shell -kubectl apply -f api-management/3-api-lifecycle-management/manifests/api-v1.1-weighted.yaml -``` - -```shell -traefikservice.traefik.io/api-lifecycle-apimanagement-weather-api-wrr created -ingressroute.traefik.io/weather-api created -``` - -A simple test should confirm that it works: - -```shell -curl -s -H "Authorization: Bearer $ADMIN_TOKEN" http://api.lifecycle.apimanagement.docker.localhost/weather-v1-wrr/weather | jq -curl -s -H "Authorization: Bearer $ADMIN_TOKEN" http://api.lifecycle.apimanagement.docker.localhost/weather-v1-wrr/weather | jq -curl -s -H "Authorization: Bearer $ADMIN_TOKEN" http://api.lifecycle.apimanagement.docker.localhost/weather-v1-wrr/weather | jq -curl -s -H "Authorization: Bearer $ADMIN_TOKEN" http://api.lifecycle.apimanagement.docker.localhost/weather-v1-wrr/weather | jq -``` - -To go further, it's also possible to mirror production traffic to a new version and/or to use a sticky session. diff --git a/api-management/3-api-lifecycle-management/manifests/api-portal.yaml b/api-management/3-api-lifecycle-management/manifests/api-portal.yaml deleted file mode 100644 index 433b252..0000000 --- a/api-management/3-api-lifecycle-management/manifests/api-portal.yaml +++ /dev/null @@ -1,33 +0,0 @@ ---- -apiVersion: hub.traefik.io/v1alpha1 -kind: APIPortal -metadata: - name: api-lifecycle-apimanagement-apiportal - namespace: apps -spec: - title: API Portal - description: "Apps Developer Portal" - trustedUrls: - - http://api.lifecycle.apimanagement.docker.localhost - ---- -apiVersion: networking.k8s.io/v1 -kind: Ingress -metadata: - name: api-lifecycle-apimanagement-apiportal - namespace: traefik - annotations: - # This annotation link this Ingress to the API Portal using @ format. - hub.traefik.io/api-portal: api-lifecycle-apimanagement-apiportal@apps -spec: - rules: - - host: api.lifecycle.apimanagement.docker.localhost - http: - paths: - - path: / - pathType: Prefix - backend: - service: - name: apiportal - port: - number: 9903 diff --git a/api-management/3-api-lifecycle-management/manifests/api-v1.1-weighted.yaml b/api-management/3-api-lifecycle-management/manifests/api-v1.1-weighted.yaml deleted file mode 100644 index 5744dfe..0000000 --- a/api-management/3-api-lifecycle-management/manifests/api-v1.1-weighted.yaml +++ /dev/null @@ -1,36 +0,0 @@ ---- -apiVersion: traefik.io/v1alpha1 -kind: TraefikService -metadata: - name: api-lifecycle-apimanagement-weather-api-wrr - namespace: apps -spec: - weighted: - services: - - name: weather-app - port: 3000 - weight: 1 - - name: weather-app-forecast - port: 3000 - weight: 1 - ---- -apiVersion: traefik.io/v1alpha1 -kind: IngressRoute -metadata: - name: weather-api - namespace: apps - annotations: - hub.traefik.io/api-version: api-lifecycle-apimanagement-weather-api-v1-1 -spec: - entryPoints: - - web - routes: - - match: Host(`api.lifecycle.apimanagement.docker.localhost`) && PathPrefix(`/weather-v1-wrr`) - kind: Rule - services: - - name: api-lifecycle-apimanagement-weather-api-wrr - port: 3000 - kind: TraefikService - middlewares: - - name: stripprefix-weather diff --git a/api-management/3-api-lifecycle-management/manifests/api-v1.1.yaml b/api-management/3-api-lifecycle-management/manifests/api-v1.1.yaml deleted file mode 100644 index db0fbf6..0000000 --- a/api-management/3-api-lifecycle-management/manifests/api-v1.1.yaml +++ /dev/null @@ -1,70 +0,0 @@ ---- -apiVersion: hub.traefik.io/v1alpha1 -kind: APIVersion -metadata: - name: api-lifecycle-apimanagement-weather-api-v1-1 - namespace: apps -spec: - release: v1.1.0 - ---- -apiVersion: hub.traefik.io/v1alpha1 -kind: API -metadata: - name: api-lifecycle-apimanagement-weather-api-v1-1 - namespace: apps -spec: - versions: - - name: api-lifecycle-apimanagement-weather-api-v1 - - name: api-lifecycle-apimanagement-weather-api-v1-1 - ---- -apiVersion: hub.traefik.io/v1alpha1 -kind: APIAccess -metadata: - name: api-lifecycle-apimanagement-weather-api-v1-1 - namespace: apps -spec: - apis: - - name: api-lifecycle-apimanagement-weather-api-v1-1 - everyone: true - ---- -apiVersion: traefik.io/v1alpha1 -kind: IngressRoute -metadata: - name: api-lifecycle-apimanagement-weather-api-v1-1 - namespace: apps - annotations: - hub.traefik.io/api-version: api-lifecycle-apimanagement-weather-api-v1 -spec: - entryPoints: - - web - routes: - - match: Host(`api.lifecycle.apimanagement.docker.localhost`) && PathPrefix(`/weather-multi-versions`) - kind: Rule - services: - - name: weather-app - port: 3000 - middlewares: - - name: stripprefix-weather - ---- -apiVersion: traefik.io/v1alpha1 -kind: IngressRoute -metadata: - name: api-lifecycle-apimanagement-weather-api-v1-1-preview - namespace: apps - annotations: - hub.traefik.io/api-version: api-lifecycle-apimanagement-weather-api-v1-1 -spec: - entryPoints: - - web - routes: - - match: Host(`api.lifecycle.apimanagement.docker.localhost`) && PathPrefix(`/weather-multi-versions`) && Header(`X-Version`, `preview`) - kind: Rule - services: - - name: weather-app-forecast - port: 3000 - middlewares: - - name: stripprefix-weather diff --git a/api-management/3-api-lifecycle-management/manifests/api-v1.yaml b/api-management/3-api-lifecycle-management/manifests/api-v1.yaml deleted file mode 100644 index e64e732..0000000 --- a/api-management/3-api-lifecycle-management/manifests/api-v1.yaml +++ /dev/null @@ -1,54 +0,0 @@ ---- -apiVersion: hub.traefik.io/v1alpha1 -kind: APIVersion -metadata: - name: api-lifecycle-apimanagement-weather-api-v1 - namespace: apps -spec: - release: v1.0.0 - openApiSpec: - path: /openapi.yaml - override: - servers: - - url: http://api.lifecycle.apimanagement.docker.localhost/weather-v1 - ---- -apiVersion: hub.traefik.io/v1alpha1 -kind: API -metadata: - name: api-lifecycle-apimanagement-weather-api-v1 - namespace: apps -spec: - versions: - - name: api-lifecycle-apimanagement-weather-api-v1 - ---- -apiVersion: hub.traefik.io/v1alpha1 -kind: APIAccess -metadata: - name: api-lifecycle-apimanagement-weather-api-v1 - namespace: apps -spec: - apis: - - name: api-lifecycle-apimanagement-weather-api-v1 - everyone: true - ---- -apiVersion: traefik.io/v1alpha1 -kind: IngressRoute -metadata: - name: api-lifecycle-apimanagement-weather-api-v1 - namespace: apps - annotations: - hub.traefik.io/api-version: api-lifecycle-apimanagement-weather-api-v1 -spec: - entryPoints: - - web - routes: - - match: Host(`api.lifecycle.apimanagement.docker.localhost`) && PathPrefix(`/weather-v1`) - kind: Rule - services: - - name: weather-app - port: 3000 - middlewares: - - name: stripprefix-weather diff --git a/api-management/3-api-lifecycle-management/manifests/api.yaml b/api-management/3-api-lifecycle-management/manifests/api.yaml deleted file mode 100644 index 656818e..0000000 --- a/api-management/3-api-lifecycle-management/manifests/api.yaml +++ /dev/null @@ -1,41 +0,0 @@ ---- -apiVersion: hub.traefik.io/v1alpha1 -kind: API -metadata: - name: api-lifecycle-apimanagement-weather-api - namespace: apps -spec: - openApiSpec: - path: /openapi.yaml - override: - servers: - - url: http://api.lifecycle.apimanagement.docker.localhost - ---- -apiVersion: hub.traefik.io/v1alpha1 -kind: APIAccess -metadata: - name: api-lifecycle-apimanagement-weather-api - namespace: apps -spec: - apis: - - name: api-lifecycle-apimanagement-weather-api - everyone: true - ---- -apiVersion: traefik.io/v1alpha1 -kind: IngressRoute -metadata: - name: api-lifecycle-apimanagement-weather-api - namespace: apps - annotations: - hub.traefik.io/api: api-lifecycle-apimanagement-weather-api # <=== Link to the API using its name -spec: - entryPoints: - - web - routes: - - match: Host(`api.lifecycle.apimanagement.docker.localhost`) && PathPrefix(`/weather`) - kind: Rule - services: - - name: weather-app - port: 3000 diff --git a/api-management/4-protect-api-infrastructure/README.md b/api-management/4-protect-api-infrastructure/README.md deleted file mode 100644 index 0e82f6c..0000000 --- a/api-management/4-protect-api-infrastructure/README.md +++ /dev/null @@ -1,560 +0,0 @@ -# Protect API infrastructure - -In this tutorial, we will learn how to protect our APIs from excessive usage using API Plans in Traefik Hub’s API Management. -By following these steps, we’ll ensure that our APIs remain performant and are not overwhelmed by excessive requests. - -By the end of this tutorial, we’ll be able to: - -- Protect our APIs from excessive usage by implementing rate limits and quotas using API Plans. -- Manage API Plans to provide different levels of service to various consumer groups. - -## What are our APIs ? - -In this tutorial, we will use two APIs: - -1. `admin` API: It's internal and not really reliable. It should not take more than one request per second -2. `weather` API: It's exposed externally. Users can be billed for this one, up to 500 requests each day. - -## Pre-requisites - -- Traefik Hub should be installed with API Management following [getting started](../1-getting-started/README.md) instructions -- Follow the [Access Control](../2-access-control/README.md) tutorial to set up the users and API portal we will need in this tutorial - -### Step 1: Deploy `weather` and `admin` apps and APIs - -First, deploy the weather app and the admin app, with an API Access. It follows the simple access control that is detailed in the [Access Control Tutorial](../2-access-control/README.md). - -```sh -kubectl apply -f src/manifests/apps-namespace.yaml -kubectl apply -f src/manifests/weather-app.yaml -kubectl apply -f src/manifests/admin-app.yaml -kubectl wait -n apps --for=condition=ready pod --selector=app=weather-app --timeout=90s -kubectl wait -n admin --for=condition=ready pod --selector=app=admin-app --timeout=90s -kubectl apply -f api-management/4-protect-api-infrastructure/manifests/admin-api.yaml -kubectl apply -f api-management/4-protect-api-infrastructure/manifests/admin-apiaccess.yaml -kubectl apply -f api-management/4-protect-api-infrastructure/manifests/admin-ingressroute.yaml -kubectl apply -f api-management/4-protect-api-infrastructure/manifests/weather-api.yaml -kubectl apply -f api-management/4-protect-api-infrastructure/manifests/weather-apiaccess.yaml -kubectl apply -f api-management/4-protect-api-infrastructure/manifests/weather-ingressroute.yaml -``` - -### Step 2: Check Access Control - -Those two APIs should be deployed with an access control. Let's check that! - -You'll need to get the token for the admin user in the admin group and the external user in the external group, following instructions in the [access control](../2-access-control/README.md) tutorial. - -```sh -# Set here after "=" the token of admin user -export ADMIN_TOKEN= -# Set here after "=" the token of external user -export EXTERNAL_TOKEN= -``` - -Admin user is only allowed to access admin API: - -```sh -# This call is allowed => 200 -curl -sIXGET -H "Authorization: Bearer $ADMIN_TOKEN" "http://api.protect-infrastructure.apimanagement.docker.localhost/admin" -# This call is forbidden => 403 -curl -sIXGET -H "Authorization: Bearer $ADMIN_TOKEN" "http://api.protect-infrastructure.apimanagement.docker.localhost/weather" -``` - -External user is only allowed to access external API: - -```sh -# This call is allowed => 200 -curl -sIXGET -H "Authorization: Bearer $EXTERNAL_TOKEN" "http://api.protect-infrastructure.apimanagement.docker.localhost/weather" -# This call is forbidden => 403 -curl -sIXGET -H "Authorization: Bearer $EXTERNAL_TOKEN" "http://api.protect-infrastructure.apimanagement.docker.localhost/admin" -``` - -### Step 3: Set up Redis - -To use the API plan feature, we need to deploy and configure Redis in your cluster and the Traefik deployment. - -We can do that by following the instructions in the [official documentation.](https://doc.traefik.io/traefik-hub/api-management/api-plans#prerequisites) - -## Protect from excessive usage - -### RateLimit on Admin - -In order to set rate limit on an API, we'll need to create an API Plan. This API Plan should be associated with an `APIAccess` resource. - -```yaml :manifests/admin-apiplan.yaml -apiVersion: hub.traefik.io/v1alpha1 -kind: APIPlan -metadata: - name: admin - namespace: admin -spec: - title: "Admin Protection" - description: "Admin API should not take more than one request per second." - rateLimit: - limit: 1 - period: 1s -``` - -And link it in the API Access - -```diff :../../hack/diff.sh -r -a "manifests/admin-apiaccess.yaml manifests/admin-apiaccess-ratelimit.yaml" ---- manifests/admin-apiaccess.yaml -+++ manifests/admin-apiaccess-ratelimit.yaml -@@ -1,10 +1,12 @@ - apiVersion: hub.traefik.io/v1alpha1 - kind: APIAccess - metadata: -- name: protect-api-infrastructure-apimanagement-admin -+ name: protect-api-infrastructure-apimanagement-admin-ratelimit - namespace: admin - spec: - groups: - - admin - apis: - - name: protect-api-infrastructure-apimanagement-admin -+ apiPlan: -+ name: admin -``` - -Let's apply it: - -```shell -kubectl apply -f api-management/4-protect-api-infrastructure/manifests/admin-apiplan.yaml -kubectl apply -f api-management/4-protect-api-infrastructure/manifests/admin-apiaccess-ratelimit.yaml -``` - -And test it: - -```sh -# This call is allowed => 200 -curl -sIXGET -H "Authorization: Bearer $ADMIN_TOKEN" "http://api.protect-infrastructure.apimanagement.docker.localhost/admin" -``` - -```sh -HTTP/1.1 200 OK -Content-Length: 55 -Content-Type: text/plain; charset=utf-8 -``` - -:warning: We can see here that the API plan is not working yet because we do not get any HTTP header showing us our Rate Limit remaining as mentioned in the [documentation](https://doc.traefik.io/traefik-hub/api-management/api-plans#response-headers). - -Let's try to understand why. - -We can check the API Access: - -```sh -kubectl get apiaccess -n admin -``` - -```sh -protect-api-infrastructure-apimanagement-admin 23m -protect-api-infrastructure-apimanagement-admin-ratelimit 98s -``` - -It seems we have two API Access. When multiple `APIAccess` overlap, the [documentation](https://doc.traefik.io/traefik-hub/api-management/api-plans#managing-overlapping-plans) says: - -> If the weights were equal or not set for both APIAccess resources, the system would select the one whose name comes first alphabetically. - -So Traefik Hub is using the first one in alphabetical order (`protect-api-infrastructure-apimanagement-admin`), without the `APIPlan`. - -Let's see what happens when we delete the first one: - -```sh -kubectl delete apiaccess -n admin protect-api-infrastructure-apimanagement-admin -``` - -Let's test it again: - -```sh -# This call is allowed => 200 -curl -sIXGET -H "Authorization: Bearer $ADMIN_TOKEN" "http://api.protect-infrastructure.apimanagement.docker.localhost/admin" -``` - -```sh -HTTP/1.1 200 OK -Content-Length: 55 -Content-Type: text/plain; charset=utf-8 -X-Ratelimit-Remaining: 0 -``` - -Yeah, it works :tada: ! We can see the HTTP header `X-Ratelimit-Remaining`. Since we have enforced one request per second, there is 0 left. If we do it twice in a row, the second one will be rejected: - -```sh -curl -sIXGET -H "Authorization: Bearer $ADMIN_TOKEN" "http://api.protect-infrastructure.apimanagement.docker.localhost/admin" -curl -sIXGET -H "Authorization: Bearer $ADMIN_TOKEN" "http://api.protect-infrastructure.apimanagement.docker.localhost/admin" -``` - -```sh -HTTP/1.1 200 OK -Content-Length: 55 -Content-Type: text/plain; charset=utf-8 -X-Ratelimit-Remaining: 0 - -HTTP/1.1 429 Too Many Requests -X-Ratelimit-Remaining: 0 -Content-Length: 0 -``` - -We can see the second one was rejected. Within an application, it can wait for a bit of time before re-trying the request. - -Now, let's see how to use quota. - -### Quota on external API - -In order to set a quota on the weather API that is exposed externally, we'll need to create another API Plan. This API Plan should be associated with an `APIAccess` resource. - -> [!NOTE] -> Ratelimit and Quota can also be configured in the same API Plan depending on your use case. - -```yaml :manifests/weather-apiplan.yaml -apiVersion: hub.traefik.io/v1alpha1 -kind: APIPlan -metadata: - name: external - namespace: apps -spec: - title: "Weather Protection for external users" - description: "Weather API should not take more than 5 requests in 24 hours" - quota: - limit: 5 - period: 24h -``` - -And link it in the API Access: - -```diff :../../hack/diff.sh -r -a "manifests/weather-apiaccess.yaml manifests/weather-apiaccess-quota.yaml" ---- manifests/weather-apiaccess.yaml -+++ manifests/weather-apiaccess-quota.yaml -@@ -1,11 +1,12 @@ ----- - apiVersion: hub.traefik.io/v1alpha1 - kind: APIAccess - metadata: -- name: protect-api-infrastructure-apimanagement-weather -+ name: protect-api-infrastructure-apimanagement-weather-quota - namespace: apps - spec: - groups: - - external - apis: - - name: protect-api-infrastructure-apimanagement-weather -+ apiPlan: -+ name: external -``` - -Let's apply it: - -```yaml -kubectl apply -f api-management/4-protect-api-infrastructure/manifests/weather-apiplan.yaml -kubectl apply -f api-management/4-protect-api-infrastructure/manifests/weather-apiaccess-quota.yaml -``` - -And test it: - -```sh -# This call is allowed => 200 -curl -sIXGET -H "Authorization: Bearer $EXTERNAL_TOKEN" "http://api.protect-infrastructure.apimanagement.docker.localhost/weather" -``` - -```sh -HTTP/1.1 200 OK -Content-Length: 163 -Content-Type: text/plain; charset=utf-8 -``` - -:warning: We can see here that the API plan is not working yet! - -Just like we faced when we were applying [rate limiting](#ratelimit-on-admin) on the admin API, we have to delete the first API Access we applied for weather. - -```sh -kubectl delete apiaccess -n apps protect-api-infrastructure-apimanagement-weather -``` - -Let's test it again: - -```sh -# This call is allowed => 200 -curl -sIXGET -H "Authorization: Bearer $EXTERNAL_TOKEN" "http://api.protect-infrastructure.apimanagement.docker.localhost/weather" -``` - -```sh -HTTP/1.1 200 OK -Content-Length: 163 -Content-Type: text/plain; charset=utf-8 -X-Quota-Remaining: 3 -``` - -This time it works and now we can see that there's a new header `X-Quota-Remaining: 3` that specifies our quota. - -Now, what happens when we exhaust our quota? Can we still make API requests? - -Let's try to exhaust our quota like this: - -```sh -for i in {1..6}; do - curl -sIXGET -H "Authorization: Bearer $EXTERNAL_TOKEN" "http://api.protect-infrastructure.apimanagement.docker.localhost/weather" -done -``` - -```sh -HTTP/1.1 200 OK -Content-Length: 163 -Content-Type: text/plain; charset=utf-8 -X-Quota-Remaining: 2 - -HTTP/1.1 200 OK -Content-Length: 163 -Content-Type: text/plain; charset=utf-8 -X-Quota-Remaining: 1 - -HTTP/1.1 200 OK -Content-Length: 163 -Content-Type: text/plain; charset=utf-8 -X-Quota-Remaining: 0 - -HTTP/1.1 429 Too Many Requests -X-Quota-Remaining: 0 -Content-Length: 0 - -HTTP/1.1 429 Too Many Requests -X-Quota-Remaining: 0 -Content-Length: 0 - -HTTP/1.1 429 Too Many Requests -X-Quota-Remaining: 0 -Content-Length: 0 -``` - -Well, we can see that after we exhaust our quota, we cannot make any more requests to the API, we get a HTTP status code of `429 too many requests`. - -We will only be able to make requests again in 24 hours like our API plan specifies or if we subscribe to a new plan. - -Why don't we subscribe to a new plan? - -Let's create a new API plan called `premium-plan` with a higher quota: - -```yaml :manifests/weather-apiplan-premium.yaml -apiVersion: hub.traefik.io/v1alpha1 -kind: APIPlan -metadata: - name: external-premium - namespace: apps -spec: - title: "Premium Weather API Plan for external users" - description: "Weather API should not take more than 500 requests in 24 hours" - quota: - limit: 500 - period: 24h -``` - -And we need to associate this new `APIPlan` with with the same `APIAccess`: - -```diff :../../hack/diff.sh -r -a "manifests/weather-apiaccess-quota.yaml manifests/weather-apiaccess-quota-premium.yaml" ---- manifests/weather-apiaccess-quota.yaml -+++ manifests/weather-apiaccess-quota-premium.yaml -@@ -1,3 +1,4 @@ -+--- - apiVersion: hub.traefik.io/v1alpha1 - kind: APIAccess - metadata: -@@ -9,4 +10,4 @@ - apis: - - name: protect-api-infrastructure-apimanagement-weather - apiPlan: -- name: external -+ name: external-premium -``` - -Let's apply the new `APIPlan` and the updated `APIAccess`: - -```sh -kubectl apply -f api-management/4-protect-api-infrastructure/manifests/weather-apiplan-premium.yaml -kubectl apply -f api-management/4-protect-api-infrastructure/manifests/weather-apiaccess-quota-premium.yaml -``` - -If we make a request again: - -```sh -curl -sIXGET -H "Authorization: Bearer $EXTERNAL_TOKEN" "http://api.protect-infrastructure.apimanagement.docker.localhost/weather" -``` - -We get this output now: - -```sh -HTTP/1.1 200 OK -Content-Length: 163 -Content-Type: text/plain; charset=utf-8 -X-Quota-Remaining: 494 -``` - -Yeah, it works now! :tada: - -We can see the HTTP header `X-Quota-Remaining: 494` is present, so our new API Plan is in effect. - -Also, If you haven't yet noticed, despite having 500 requests in our new quota, the header says the quota remaining is `494`. -This means that when we applied the new API plan, the system took account of our previously exhausted 5 requests. - -### Viewing your API plan in the Traefik Hub online dashboard - -With our API plans and API Accesses created, if we navigate to the [Traefik Hub Online Dashboard](https://hub.traefik.io/plans), we should see the new API plans listed. - -!["API plans page in Traefik Hub Online Dashboard"](./images/api-plans.png) - -Also, if we navigate to the Apps API Portal we deployed earlier in the [getting started tutorial](../1-getting-started/README.md#step-5-deploy-the-api-portal), we should see the API listed and the API plans available to it. - -!["API page in the Apps API portal"](./images/api-portal.png) - -If you click on **Multiple plans applied**, you should see all the plans we have applied to the `weather` API. - -!["Multiple plans applied"](./images/multiple-plans-applied.png) - -## Utilize API bundles to manage multiple APIs - -In this section, We’ll create an [API Bundle](https://doc.traefik.io/traefik-hub/api-management/api-bundle) that includes the weather API and a new API called `whoami`. -This bundle will allow us to apply API Plans collectively to the APIs, facilitating easier management. - -First, we need to deploy all the resources needed for the `whoami` app: - -```sh -kubectl apply -f src/manifests/whoami-app.yaml -kubectl apply -f api-management/4-protect-api-infrastructure/manifests/whoami-ingressroute.yaml -kubectl apply -f api-management/4-protect-api-infrastructure/manifests/whoami-api.yaml -kubectl apply -f api-management/4-protect-api-infrastructure/manifests/whoami-apiaccess.yaml -``` - -After this has been deployed, if we make a request to `whoami` using the external token, we should get a response: - -```sh -curl -sIXGET -H "Authorization: Bearer $EXTERNAL_TOKEN" "http://api.protect-infrastructure.apimanagement.docker.localhost/whoami" -``` - -```sh -HTTP/1.1 200 OK -Content-Length: 691 -Content-Type: text/plain; charset=utf-8 -``` - -Now, let's create an API Bundle in the apps namespace for both APIs: - -```yaml :manifests/api-bundle.yaml -apiVersion: hub.traefik.io/v1alpha1 -kind: APIBundle -metadata: - name: protect-api-infrastructure-apimanagement-bundle - namespace: apps -spec: - apis: - - name: protect-api-infrastructure-apimanagement-admin - - name: protect-api-infrastructure-apimanagement-whoami -``` - -And apply it: - -```sh -kubectl apply -f api-management/4-protect-api-infrastructure/manifests/api-bundle.yaml -``` - ->[!NOTE] -> API Bundles cannot include other API Bundles to maintain clear and manageable hierarchies. - -Let's create an API plan for the bundle: - -```yaml :manifests/apiplan-for-bundle.yaml -apiVersion: hub.traefik.io/v1alpha1 -kind: APIPlan -metadata: - name: plan-for-bundle - namespace: apps -spec: - title: "Weather & Whoami Bundle Plan" - description: "Enforces rate limits and quotas for both the Weather and Whoami APIs" - rateLimit: - limit: 1 - period: 1s - quota: - limit: 500 - period: 24h -``` - -```sh -kubectl apply -f api-management/4-protect-api-infrastructure/manifests/api-plan-for-bundle.yaml -``` - -Now, let's link it with an API Access that allows only the `external` group to have access to both APIs: - -```yaml :manifests/api-bundle-access.yaml -apiVersion: hub.traefik.io/v1alpha1 -kind: APIAccess -metadata: - name: protect-api-infrastructure-apimanagement-external-bundle - namespace: apps -spec: - groups: - - external - apiBundles: - - name: protect-api-infrastructure-apimanagement-bundle - apiPlan: - name: plan-for-bundle - weight: 5 # Higher weight to prioritize this plan over previous APIAccess resources -``` - -Apply the new API access: - -```sh -kubectl apply -f api-management/4-protect-api-infrastructure/manifests/api-bundle-access.yaml -``` - -Now, Let's test the bundle. - -If we make a request to the whoami API, we should get a response: - -```sh -curl -sIXGET -H "Authorization: Bearer $EXTERNAL_TOKEN" "http://api.protect-infrastructure.apimanagement.docker.localhost/whoami" -``` - -```sh -HTTP/1.1 200 OK -Content-Length: 691 -Content-Type: text/plain; charset=utf-8 -Date: Thu, 17 Oct 2024 07:01:35 GMT -X-Quota-Remaining: 499 -X-Ratelimit-Remaining: 0 -``` - -As you can see, we get both the `X-Quota-Remaining` and `X-Ratelimit-remaining` headers because of our API plan for the bundle. - -Now, if we make a request to the weather API: - -```sh -curl -sIXGET -H "Authorization: Bearer $EXTERNAL_TOKEN" "http://api.protect-infrastructure.apimanagement.docker.localhost/weather" -``` - -We should also get a response from the API: - -```sh -HTTP/1.1 200 OK -Content-Length: 163 -Content-Type: text/plain; charset=utf-8 -Date: Thu, 17 Oct 2024 07:18:02 GMT -X-Quota-Remaining: 498 -X-Ratelimit-Remaining: 0 -``` - -If you haven't noticed, the APIs' rate limit and quota are handled as one because they are referenced in the same API Access as part of a bundle.So this means, if we exhaust the quota on the weather API, -it will also affect the whoami API. - -### Viewing your API bundle in the Traefik Hub online dashboard - -With our API bundle created, if we navigate to the [Traefik Hub Online Dashboard](https://hub.traefik.io/bundles), we should see the new API bundle and all the APIs in the bundle listed. - -!["API bundle page in Traefik Hub Online Dashboard"](./images/api-bundle.png) - -Also, if we navigate to the Apps API Portal we deployed earlier in the [getting started tutorial](../1-getting-started/README.md#step-5-deploy-the-api-portal), we should see the APIs listed as part of a bundle. - -!["API page in the Apps API portal"](./images/api-portal-bundle.png) - -And that's it! In this tutorial, we've: - -- Secured access to our exposed APIs by defining access policies using `APIAccess` resources. -- Protected our APIs from excessive usage by implementing rate limits and quotas through `APIPlan` resources. -- Utilized `APIBundle` to manage multiple APIs collectively and apply API Plans efficiently. diff --git a/api-management/4-protect-api-infrastructure/images/api-bundle.png b/api-management/4-protect-api-infrastructure/images/api-bundle.png deleted file mode 100644 index 569b193..0000000 Binary files a/api-management/4-protect-api-infrastructure/images/api-bundle.png and /dev/null differ diff --git a/api-management/4-protect-api-infrastructure/images/api-plans.png b/api-management/4-protect-api-infrastructure/images/api-plans.png deleted file mode 100644 index f3a6bbf..0000000 Binary files a/api-management/4-protect-api-infrastructure/images/api-plans.png and /dev/null differ diff --git a/api-management/4-protect-api-infrastructure/images/api-portal-bundle.png b/api-management/4-protect-api-infrastructure/images/api-portal-bundle.png deleted file mode 100644 index 753e9d8..0000000 Binary files a/api-management/4-protect-api-infrastructure/images/api-portal-bundle.png and /dev/null differ diff --git a/api-management/4-protect-api-infrastructure/images/api-portal.png b/api-management/4-protect-api-infrastructure/images/api-portal.png deleted file mode 100644 index d74097f..0000000 Binary files a/api-management/4-protect-api-infrastructure/images/api-portal.png and /dev/null differ diff --git a/api-management/4-protect-api-infrastructure/images/multiple-plans-applied.png b/api-management/4-protect-api-infrastructure/images/multiple-plans-applied.png deleted file mode 100644 index 73a12f3..0000000 Binary files a/api-management/4-protect-api-infrastructure/images/multiple-plans-applied.png and /dev/null differ diff --git a/api-management/4-protect-api-infrastructure/manifests/admin-api.yaml b/api-management/4-protect-api-infrastructure/manifests/admin-api.yaml deleted file mode 100644 index c4ac2f2..0000000 --- a/api-management/4-protect-api-infrastructure/manifests/admin-api.yaml +++ /dev/null @@ -1,6 +0,0 @@ -apiVersion: hub.traefik.io/v1alpha1 -kind: API -metadata: - name: protect-api-infrastructure-apimanagement-admin - namespace: admin -spec: {} diff --git a/api-management/4-protect-api-infrastructure/manifests/admin-apiaccess-ratelimit.yaml b/api-management/4-protect-api-infrastructure/manifests/admin-apiaccess-ratelimit.yaml deleted file mode 100644 index 15be9c7..0000000 --- a/api-management/4-protect-api-infrastructure/manifests/admin-apiaccess-ratelimit.yaml +++ /dev/null @@ -1,12 +0,0 @@ -apiVersion: hub.traefik.io/v1alpha1 -kind: APIAccess -metadata: - name: protect-api-infrastructure-apimanagement-admin-ratelimit - namespace: admin -spec: - groups: - - admin - apis: - - name: protect-api-infrastructure-apimanagement-admin - apiPlan: - name: admin diff --git a/api-management/4-protect-api-infrastructure/manifests/admin-apiaccess.yaml b/api-management/4-protect-api-infrastructure/manifests/admin-apiaccess.yaml deleted file mode 100644 index f761541..0000000 --- a/api-management/4-protect-api-infrastructure/manifests/admin-apiaccess.yaml +++ /dev/null @@ -1,10 +0,0 @@ -apiVersion: hub.traefik.io/v1alpha1 -kind: APIAccess -metadata: - name: protect-api-infrastructure-apimanagement-admin - namespace: admin -spec: - groups: - - admin - apis: - - name: protect-api-infrastructure-apimanagement-admin diff --git a/api-management/4-protect-api-infrastructure/manifests/admin-apiplan.yaml b/api-management/4-protect-api-infrastructure/manifests/admin-apiplan.yaml deleted file mode 100644 index 6ec3606..0000000 --- a/api-management/4-protect-api-infrastructure/manifests/admin-apiplan.yaml +++ /dev/null @@ -1,11 +0,0 @@ -apiVersion: hub.traefik.io/v1alpha1 -kind: APIPlan -metadata: - name: admin - namespace: admin -spec: - title: "Admin Protection" - description: "Admin API should not take more than one request per second." - rateLimit: - limit: 1 - period: 1s diff --git a/api-management/4-protect-api-infrastructure/manifests/admin-ingressroute.yaml b/api-management/4-protect-api-infrastructure/manifests/admin-ingressroute.yaml deleted file mode 100644 index 0b61592..0000000 --- a/api-management/4-protect-api-infrastructure/manifests/admin-ingressroute.yaml +++ /dev/null @@ -1,18 +0,0 @@ -apiVersion: traefik.io/v1alpha1 -kind: IngressRoute -metadata: - name: protect-api-infrastructure-apimanagement-admin - namespace: admin - annotations: - hub.traefik.io/api: protect-api-infrastructure-apimanagement-admin -spec: - entryPoints: - - web - routes: - - match: Host(`api.protect-infrastructure.apimanagement.docker.localhost`) && PathPrefix(`/admin`) - kind: Rule - services: - - name: admin-app - port: 3000 - middlewares: - - name: stripprefix-admin diff --git a/api-management/4-protect-api-infrastructure/manifests/api-bundle-access.yaml b/api-management/4-protect-api-infrastructure/manifests/api-bundle-access.yaml deleted file mode 100644 index 6aa7658..0000000 --- a/api-management/4-protect-api-infrastructure/manifests/api-bundle-access.yaml +++ /dev/null @@ -1,13 +0,0 @@ -apiVersion: hub.traefik.io/v1alpha1 -kind: APIAccess -metadata: - name: protect-api-infrastructure-apimanagement-external-bundle - namespace: apps -spec: - groups: - - external - apiBundles: - - name: protect-api-infrastructure-apimanagement-bundle - apiPlan: - name: plan-for-bundle - weight: 1 # Higher weight to prioritize this plan over previous APIAccess resources diff --git a/api-management/4-protect-api-infrastructure/manifests/api-bundle.yaml b/api-management/4-protect-api-infrastructure/manifests/api-bundle.yaml deleted file mode 100644 index bf487c6..0000000 --- a/api-management/4-protect-api-infrastructure/manifests/api-bundle.yaml +++ /dev/null @@ -1,9 +0,0 @@ -apiVersion: hub.traefik.io/v1alpha1 -kind: APIBundle -metadata: - name: protect-api-infrastructure-apimanagement-bundle - namespace: apps -spec: - apis: - - name: protect-api-infrastructure-apimanagement-weather - - name: protect-api-infrastructure-apimanagement-whoami diff --git a/api-management/4-protect-api-infrastructure/manifests/api-plan-for-bundle.yaml b/api-management/4-protect-api-infrastructure/manifests/api-plan-for-bundle.yaml deleted file mode 100644 index 80b6da2..0000000 --- a/api-management/4-protect-api-infrastructure/manifests/api-plan-for-bundle.yaml +++ /dev/null @@ -1,14 +0,0 @@ -apiVersion: hub.traefik.io/v1alpha1 -kind: APIPlan -metadata: - name: plan-for-bundle - namespace: apps -spec: - title: "Weather & whoami Bundle Plan" - description: "Enforces rate limits and quotas for both the Weather and Whoami APIs" - rateLimit: - limit: 1 - period: 1s - quota: - limit: 500 - period: 24h diff --git a/api-management/4-protect-api-infrastructure/manifests/weather-api.yaml b/api-management/4-protect-api-infrastructure/manifests/weather-api.yaml deleted file mode 100644 index 7bf342a..0000000 --- a/api-management/4-protect-api-infrastructure/manifests/weather-api.yaml +++ /dev/null @@ -1,11 +0,0 @@ ---- -apiVersion: hub.traefik.io/v1alpha1 -kind: API -metadata: - name: protect-api-infrastructure-apimanagement-weather - namespace: apps - labels: - subscription: standard -spec: - openApiSpec: - path: /openapi.yaml diff --git a/api-management/4-protect-api-infrastructure/manifests/weather-apiaccess-quota-premium.yaml b/api-management/4-protect-api-infrastructure/manifests/weather-apiaccess-quota-premium.yaml deleted file mode 100644 index 63fa800..0000000 --- a/api-management/4-protect-api-infrastructure/manifests/weather-apiaccess-quota-premium.yaml +++ /dev/null @@ -1,13 +0,0 @@ ---- -apiVersion: hub.traefik.io/v1alpha1 -kind: APIAccess -metadata: - name: protect-api-infrastructure-apimanagement-weather-quota - namespace: apps -spec: - groups: - - external - apis: - - name: protect-api-infrastructure-apimanagement-weather - apiPlan: - name: external-premium diff --git a/api-management/4-protect-api-infrastructure/manifests/weather-apiaccess-quota.yaml b/api-management/4-protect-api-infrastructure/manifests/weather-apiaccess-quota.yaml deleted file mode 100644 index 79e7da0..0000000 --- a/api-management/4-protect-api-infrastructure/manifests/weather-apiaccess-quota.yaml +++ /dev/null @@ -1,12 +0,0 @@ -apiVersion: hub.traefik.io/v1alpha1 -kind: APIAccess -metadata: - name: protect-api-infrastructure-apimanagement-weather-quota - namespace: apps -spec: - groups: - - external - apis: - - name: protect-api-infrastructure-apimanagement-weather - apiPlan: - name: external diff --git a/api-management/4-protect-api-infrastructure/manifests/weather-apiaccess.yaml b/api-management/4-protect-api-infrastructure/manifests/weather-apiaccess.yaml deleted file mode 100644 index 1c3cded..0000000 --- a/api-management/4-protect-api-infrastructure/manifests/weather-apiaccess.yaml +++ /dev/null @@ -1,11 +0,0 @@ ---- -apiVersion: hub.traefik.io/v1alpha1 -kind: APIAccess -metadata: - name: protect-api-infrastructure-apimanagement-weather - namespace: apps -spec: - groups: - - external - apis: - - name: protect-api-infrastructure-apimanagement-weather diff --git a/api-management/4-protect-api-infrastructure/manifests/weather-apiplan-premium.yaml b/api-management/4-protect-api-infrastructure/manifests/weather-apiplan-premium.yaml deleted file mode 100644 index 757ed8c..0000000 --- a/api-management/4-protect-api-infrastructure/manifests/weather-apiplan-premium.yaml +++ /dev/null @@ -1,11 +0,0 @@ -apiVersion: hub.traefik.io/v1alpha1 -kind: APIPlan -metadata: - name: external-premium - namespace: apps -spec: - title: "Premium Weather API Plan for external users" - description: "Weather API should not take more than 500 requests in 24 hours" - quota: - limit: 500 - period: 24h diff --git a/api-management/4-protect-api-infrastructure/manifests/weather-apiplan.yaml b/api-management/4-protect-api-infrastructure/manifests/weather-apiplan.yaml deleted file mode 100644 index 7f0d4b3..0000000 --- a/api-management/4-protect-api-infrastructure/manifests/weather-apiplan.yaml +++ /dev/null @@ -1,11 +0,0 @@ -apiVersion: hub.traefik.io/v1alpha1 -kind: APIPlan -metadata: - name: external - namespace: apps -spec: - title: "Weather Protection for external users" - description: "Weather API should not take more than 5 requests in 24 hours" - quota: - limit: 5 - period: 24h diff --git a/api-management/4-protect-api-infrastructure/manifests/weather-ingressroute.yaml b/api-management/4-protect-api-infrastructure/manifests/weather-ingressroute.yaml deleted file mode 100644 index a51b6f6..0000000 --- a/api-management/4-protect-api-infrastructure/manifests/weather-ingressroute.yaml +++ /dev/null @@ -1,18 +0,0 @@ -apiVersion: traefik.io/v1alpha1 -kind: IngressRoute -metadata: - name: protect-api-infrastructure-apimanagement-weather - namespace: apps - annotations: - hub.traefik.io/api: protect-api-infrastructure-apimanagement-weather -spec: - entryPoints: - - web - routes: - - match: Host(`api.protect-infrastructure.apimanagement.docker.localhost`) && PathRegexp(`^/weather(/([0-9]+|openapi.yaml))?$`) - kind: Rule - services: - - name: weather-app - port: 3000 - middlewares: - - name: stripprefix-weather diff --git a/api-management/4-protect-api-infrastructure/manifests/whoami-api.yaml b/api-management/4-protect-api-infrastructure/manifests/whoami-api.yaml deleted file mode 100644 index 8bd8c15..0000000 --- a/api-management/4-protect-api-infrastructure/manifests/whoami-api.yaml +++ /dev/null @@ -1,6 +0,0 @@ -apiVersion: hub.traefik.io/v1alpha1 -kind: API -metadata: - name: protect-api-infrastructure-apimanagement-whoami - namespace: apps -spec: {} diff --git a/api-management/4-protect-api-infrastructure/manifests/whoami-apiaccess.yaml b/api-management/4-protect-api-infrastructure/manifests/whoami-apiaccess.yaml deleted file mode 100644 index bc23f41..0000000 --- a/api-management/4-protect-api-infrastructure/manifests/whoami-apiaccess.yaml +++ /dev/null @@ -1,11 +0,0 @@ ---- -apiVersion: hub.traefik.io/v1alpha1 -kind: APIAccess -metadata: - name: protect-api-infrastructure-apimanagement-whoami - namespace: apps -spec: - groups: - - external - apis: - - name: protect-api-infrastructure-apimanagement-whoami diff --git a/api-management/4-protect-api-infrastructure/manifests/whoami-ingressroute.yaml b/api-management/4-protect-api-infrastructure/manifests/whoami-ingressroute.yaml deleted file mode 100644 index 8ae68d3..0000000 --- a/api-management/4-protect-api-infrastructure/manifests/whoami-ingressroute.yaml +++ /dev/null @@ -1,16 +0,0 @@ -apiVersion: traefik.io/v1alpha1 -kind: IngressRoute -metadata: - name: protect-api-infrastructure-apimanagement-whoami - namespace: apps - annotations: - hub.traefik.io/api: protect-api-infrastructure-apimanagement-whoami -spec: - entryPoints: - - web - routes: - - match: Host(`api.protect-infrastructure.apimanagement.docker.localhost`) && PathPrefix(`/whoami`) - kind: Rule - services: - - name: whoami - port: 80 diff --git a/api-management/README.md b/api-management/README.md new file mode 100644 index 0000000..bc35b98 --- /dev/null +++ b/api-management/README.md @@ -0,0 +1,3 @@ +# Traefik Hub API Management + +This section is coming soon. In the meantime, you can follow the [Traefik Hub API Management quick start guide](https://doc.traefik.io/traefik-hub/api-management/quick-start-guide) tutorial.