diff --git a/docs/en/latest/plugins/authz-keycloak.md b/docs/en/latest/plugins/authz-keycloak.md
index e9da7c41e30e..9e0f6099af5d 100644
--- a/docs/en/latest/plugins/authz-keycloak.md
+++ b/docs/en/latest/plugins/authz-keycloak.md
@@ -34,15 +34,9 @@ description: The authz-keycloak Plugin supports the integration with Keycloak to
## Description
-The `authz-keycloak` Plugin supports the integration with [Keycloak](https://www.keycloak.org/) to authenticate and authorize users. See [Keycloak's Authorization Services Guide](https://www.keycloak.org/docs/latest/authorization_services/) for more information about the configuration options available in this Plugin.
+The `authz-keycloak` Plugin integrates with [Keycloak](https://www.keycloak.org/) to authenticate and authorize users. See Keycloak's [Authorization Services Guide](https://www.keycloak.org/docs/latest/authorization_services/) for more information about the configuration options available in this Plugin.
-:::tip
-
-Although this Plugin was developed to work with Keycloak, it should work with any OAuth/OIDC and UMA compliant identity providers as well.
-
-:::
-
-Refer to [Authorization Services Guide](https://www.keycloak.org/docs/latest/authorization_services/) for more information on Keycloak.
+While the Plugin was developed for Keycloak, it could theoretically be used with other OAuth/OIDC and UMA-compliant identity providers.
## Attributes
@@ -52,7 +46,7 @@ Refer to [Authorization Services Guide](https://www.keycloak.org/docs/latest/aut
| token_endpoint | string | False | | https://host.domain/realms/foo/protocol/openid-connect/token | An OAuth2-compliant token endpoint that supports the `urn:ietf:params:oauth:grant-type:uma-ticket` grant type. If provided, overrides the value from discovery. |
| resource_registration_endpoint | string | False | | https://host.domain/realms/foo/authz/protection/resource_set | A UMA-compliant resource registration endpoint. If provided, overrides the value from discovery. |
| client_id | string | True | | | The identifier of the resource server to which the client is seeking access. |
-| client_secret | string | False | | | The client secret, if required. You can use APISIX secret to store and reference this value. APISIX currently supports storing secrets in two ways: [Environment Variables and HashiCorp Vault](../terminology/secret.md). |
+| client_secret | string | False | | | The client secret, if required. You can use APISIX Secret to store and reference this value. APISIX currently supports storing secrets in two ways: [Environment Variables and HashiCorp Vault](../terminology/secret.md). |
| grant_type | string | False | "urn:ietf:params:oauth:grant-type:uma-ticket" | ["urn:ietf:params:oauth:grant-type:uma-ticket"] | |
| policy_enforcement_mode | string | False | "ENFORCING" | ["ENFORCING", "PERMISSIVE"] | |
| permissions | array[string] | False | | | An array of strings, each representing a set of one or more resources and scopes the client is seeking access. |
@@ -156,7 +150,17 @@ admin_key=$(yq '.deployment.admin.admin_key[0].key' conf/config.yaml | sed 's/"/
#### Start Keycloak
-Start a Keycloak instance named `apisix-quickstart-keycloak` with the administrator name `quickstart-admin` and password `quickstart-admin-pass` in [development mode](https://www.keycloak.org/server/configuration#_starting_keycloak_in_development_mode) in Docker:
+
+
+
+
+Start a Keycloak instance named `apisix-quickstart-keycloak` with the administrator name `quickstart-admin` and password `quickstart-admin-pass` in [development mode](https://www.keycloak.org/server/configuration#_starting_keycloak_in_development_mode):
```shell
docker run -d --name "apisix-quickstart-keycloak" \
@@ -166,57 +170,159 @@ docker run -d --name "apisix-quickstart-keycloak" \
quay.io/keycloak/keycloak:18.0.2 start-dev
```
-Save the Keycloak IP to an environment variable to be referenced in future configuration:
+
+
+
+
+Deploy Keycloak to Kubernetes:
+
+```yaml title="keycloak.yaml"
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+ namespace: aic
+ name: keycloak
+spec:
+ replicas: 1
+ selector:
+ matchLabels:
+ app: keycloak
+ template:
+ metadata:
+ labels:
+ app: keycloak
+ spec:
+ containers:
+ - name: keycloak
+ image: quay.io/keycloak/keycloak:18.0.2
+ args:
+ - start-dev
+ env:
+ - name: KEYCLOAK_ADMIN
+ value: quickstart-admin
+ - name: KEYCLOAK_ADMIN_PASSWORD
+ value: quickstart-admin-pass
+ ports:
+ - containerPort: 8080
+---
+apiVersion: v1
+kind: Service
+metadata:
+ namespace: aic
+ name: keycloak
+spec:
+ selector:
+ app: keycloak
+ ports:
+ - port: 8080
+ targetPort: 8080
+```
+
+Apply the manifest:
```shell
-KEYCLOAK_IP=192.168.42.145 # replace with your host IP
+kubectl apply -f keycloak.yaml
```
-Navigate to `http://localhost:8080` in browser and click **Administration Console**:
+If you would like to open the Keycloak console locally, port-forward the Service:
-
+```shell
+kubectl port-forward -n aic svc/keycloak 8080:8080
+```
-Enter the administrator's username `quickstart-admin` and password `quickstart-admin-pass` to sign in:
+
-
+
-#### Create a Realm
+#### Save Keycloak URL
-In the left menu, hover over **Master**, and select **Add realm** in the dropdown:
+Save the Keycloak URL to an environment variable to be referenced in future configuration:
-
+
-Enter the realm name `quickstart-realm` and click **Create** to create it:
+
-
+```shell
+KEYCLOAK_URL=http://192.168.42.145:8080 # replace with your Keycloak URL
+```
+
+
-#### Create a Client
+
+
+```shell
+KEYCLOAK_URL=http://keycloak.aic.svc.cluster.local:8080
+```
-Click **Clients** > **Create** to open the **Add Client** page:
+
-
+
-Enter **Client ID** as `apisix-quickstart-client`, keep the **Client Protocol** as `openid-connect` and **Save**:
+#### Create a Realm, Client, and Authorization Objects
+
+Navigate to `http://localhost:8080` and click __Administration Console__:
+
+
+
+Sign in with the administrator username `quickstart-admin` and password `quickstart-admin-pass`:
+
+
+
+Create a realm named `quickstart-realm`:
+
+
+
+Create a client named `apisix-quickstart-client`:

-The client `apisix-quickstart-client` is created. After redirecting to the detailed page, select `confidential` as the **Access Type**:
+On the client settings page, select `confidential` as the access type:

-When the user login is successful during the SSO, Keycloak will carry the state and code to redirect the client to the addresses in **Valid Redirect URIs**. For simplicity of demonstration, enter wildcard `*` to accept any redirect URI:
+Enable authorization for the client and save the configuration. This should also enable the client service account and assign the `uma_protection` role automatically:
-
+
-Enable authorization for the client, which should also enable service accounts with an assigned role `uma_protection` automatically:
+Create a client scope named `httpbin-access`:
-
+
+
+In the client's **Authorization** section, create the authorization scope `access`:
+
+
+
+Create the resource `httpbin-anything` with URI `/anything` and scope `access`:
+
+
-Select **Save** to apply custom configurations.
+Create the client scope policy `access-client-scope-policy` that requires `httpbin-access`:
-#### Save Client ID and Secret
+
+
+Create the scope-based permission `access-scope-perm` that uses the `access` scope and `access-client-scope-policy`:
+
+
+
+Add `httpbin-access` to the default client scopes of `apisix-quickstart-client`:
+
+
+
+Create a user named `quickstart-user`:
+
+
+
+Set the password to `quickstart-user-pass` and turn off **Temporary**:
+
+
-Click on **Clients** > `apisix-quickstart-client` > **Credentials**, and copy the client secret from **Secret**:
+Save the client secret from **Clients** > `apisix-quickstart-client` > **Credentials**:

@@ -227,35 +333,66 @@ OIDC_CLIENT_ID=apisix-quickstart-client
OIDC_CLIENT_SECRET=bSaIN3MV1YynmtXvU8lKkfeY0iwpr9cH # replace with your value
```
+:::tip
+
+If APISIX runs in Kubernetes, use the same Keycloak hostname consistently in both the Plugin configuration and the token request. Otherwise, Keycloak may reject the bearer token because the token issuer does not match the configured authorization endpoints.
+
+:::
+
#### Request Access Token
-Request an access token from Keycloak:
+Request an access token from Keycloak and save it to `ACCESS_TOKEN`:
+
+
+
+
```shell
-curl -i "http://$KEYCLOAK_IP:8080/realms/quickstart-realm/protocol/openid-connect/token" -X POST \
+ACCESS_TOKEN=$(curl -sS "$KEYCLOAK_URL/realms/quickstart-realm/protocol/openid-connect/token" \
-d 'grant_type=client_credentials' \
-d 'client_id='$OIDC_CLIENT_ID'' \
- -d 'client_secret='$OIDC_CLIENT_SECRET''
+ -d 'client_secret='$OIDC_CLIENT_SECRET'' | jq -r '.access_token')
```
-You should see a response similar to the following:
+
-```text
-{"access_token":"eyJhbGciOiJSUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICJoT3ludlBPY2d6Y3VWWnYtTU42bXZKMUczb0dOX2d6MFo3WFl6S2FSa1NBIn0...","expires_in":300,"refresh_expires_in":0,"token_type":"Bearer","not-before-policy":0,"scope":"email profile"}
-```
+
-Save the token to an environment variable:
+Run the token request inside the Keycloak pod and save the result to `ACCESS_TOKEN`:
```shell
-# replace with your access token
-ACCESS_TOKEN=
+ACCESS_TOKEN=$(kubectl exec -n aic deploy/keycloak -- env OIDC_CLIENT_SECRET="$OIDC_CLIENT_SECRET" sh -lc 'curl -sS "http://keycloak.aic.svc.cluster.local:8080/realms/quickstart-realm/protocol/openid-connect/token" \
+ -d grant_type=client_credentials \
+ -d client_id=apisix-quickstart-client \
+ -d client_secret="$OIDC_CLIENT_SECRET"' | jq -r '.access_token')
```
+
+
+
+
### Use Lazy Load Path and Resource Registration Endpoint
-The examples below demonstrate how you can configure the plugin to dynamically resolve the request URI to resource(s) using the resource registration endpoint instead of the static permissions.
+The examples below demonstrate how you can configure `authz-keycloak` to dynamically resolve the request URI to one or more resources using the resource registration endpoint instead of static permissions.
+
+
-Create a route with `authz-keycloak-route` as follows:
+
+
+Create a Route with `authz-keycloak-route` as follows:
```shell
curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \
@@ -264,118 +401,252 @@ curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \
"id": "authz-keycloak-route",
"uri": "/anything",
"plugins": {
+ # highlight-start
"authz-keycloak": {
"lazy_load_paths": true,
- "resource_registration_endpoint": "http://'"$KEYCLOAK_IP"':8080/realms/quickstart-realm/authz/protection/resource_set",
- "discovery": "http://'"$KEYCLOAK_IP"':8080/realms/quickstart-realm/.well-known/uma2-configuration",
+ "resource_registration_endpoint": "'"$KEYCLOAK_URL"'/realms/quickstart-realm/authz/protection/resource_set",
+ "discovery": "'"$KEYCLOAK_URL"'/realms/quickstart-realm/.well-known/uma2-configuration",
"client_id": "'"$OIDC_CLIENT_ID"'",
"client_secret": "'"$OIDC_CLIENT_SECRET"'"
}
+ # highlight-end
},
"upstream": {
"type": "roundrobin",
"nodes": {
- "httpbin.org": 1
+ "httpbin.org:80": 1
}
}
}'
```
-- Set `lazy_load_paths` to `true`.
-- Set `resource_registration_endpoint` to Keycloak's UMA-compliant resource registration endpoint. Required when `lazy_load_paths` is `true` and `discovery` is not provided.
-- Set `discovery` to the discovery document endpoint of Keycloak authorization services.
-- Set `client_id` to client ID created previously.
-- Set `client_secret` to client secret created previously. Required when `lazy_load_paths` is `true`.
-
-Send a request to the route:
-
-```shell
-curl "http://127.0.0.1:9080/anything" -H "Authorization: Bearer $ACCESS_TOKEN"
+
+
+
+
+Create a Route with `authz-keycloak` configured in ADC:
+
+```yaml title="adc.yaml"
+services:
+ - name: authz-keycloak-service
+ routes:
+ - name: authz-keycloak-route
+ uris:
+ - /anything
+ plugins:
+ # highlight-start
+ authz-keycloak:
+ lazy_load_paths: true
+ resource_registration_endpoint: ${KEYCLOAK_URL}/realms/quickstart-realm/authz/protection/resource_set
+ discovery: ${KEYCLOAK_URL}/realms/quickstart-realm/.well-known/uma2-configuration
+ client_id: ${OIDC_CLIENT_ID}
+ client_secret: ${OIDC_CLIENT_SECRET}
+ # highlight-end
+ upstream:
+ type: roundrobin
+ nodes:
+ - host: httpbin.org
+ port: 80
+ weight: 1
```
-You should see an `HTTP/1.1 200 OK` response similar to the following:
+Synchronize the configuration to the gateway:
-```json
-{
- "args": {},
- "data": "",
- "files": {},
- "form": {},
- "headers": {
- "Accept": "*/*",
- "Authorization": "Bearer eyJhbGciOiJSU...",
- ...
- },
- "json": null,
- "method": "GET",
- "origin": "127.0.0.1, 108.180.51.111",
- "url": "http://127.0.0.1/anything"
-}
+```shell
+adc sync -f adc.yaml
```
-### Use Static Permissions
+
-The examples below demonstrate how you can configure Keycloak for scope-based permission associated with a client scope policy, and configure the `authz-keycloak` plugin to use static permissions.
+
-#### Create Scope in Keycloak
+Configure `authz-keycloak` on the Route:
-Go to **Clients** > **`apisix-quickstart-client`** > **Authorization** > **Authorization Scopes**, and click **Create** to open the **Add Scope** page:
+
-
-
-Enter the scope names as `access` and click **Save**:
-
-
+
-#### Create Resource in Keycloak
-
-Go to **Clients** > **`apisix-quickstart-client`** > **Authorization** > **Resources** and click **Create** to open the **Add Resource** page:
+```yaml title="authz-keycloak-ic.yaml"
+apiVersion: v1
+kind: Service
+metadata:
+ namespace: aic
+ name: httpbin-external-domain
+spec:
+ type: ExternalName
+ externalName: httpbin.org
+---
+apiVersion: apisix.apache.org/v1alpha1
+kind: PluginConfig
+metadata:
+ namespace: aic
+ name: authz-keycloak-plugin-config
+spec:
+ plugins:
+ - name: authz-keycloak
+ config:
+ # highlight-start
+ lazy_load_paths: true
+ resource_registration_endpoint: http://keycloak.aic.svc.cluster.local:8080/realms/quickstart-realm/authz/protection/resource_set
+ discovery: http://keycloak.aic.svc.cluster.local:8080/realms/quickstart-realm/.well-known/uma2-configuration
+ client_id: apisix-quickstart-client
+ client_secret: replace-with-your-client-secret
+ # highlight-end
+---
+apiVersion: gateway.networking.k8s.io/v1
+kind: HTTPRoute
+metadata:
+ namespace: aic
+ name: authz-keycloak-route
+spec:
+ parentRefs:
+ - name: apisix
+ rules:
+ - matches:
+ - path:
+ type: Exact
+ value: /anything
+ filters:
+ - type: ExtensionRef
+ extensionRef:
+ group: apisix.apache.org
+ kind: PluginConfig
+ name: authz-keycloak-plugin-config
+ backendRefs:
+ - name: httpbin-external-domain
+ port: 80
+```
-
+Apply the configuration to your cluster:
-Enter the resource names `httpbin-anything`, URI `/anything`, scope `access`, and click **Save**:
+```shell
+kubectl apply -f authz-keycloak-ic.yaml
+```
-
+
+
+
+
+```yaml title="authz-keycloak-ic.yaml"
+apiVersion: apisix.apache.org/v2
+kind: ApisixUpstream
+metadata:
+ namespace: aic
+ name: httpbin-external-domain
+spec:
+ ingressClassName: apisix
+ externalNodes:
+ - type: Domain
+ name: httpbin.org
+---
+apiVersion: apisix.apache.org/v2
+kind: ApisixPluginConfig
+metadata:
+ namespace: aic
+ name: authz-keycloak-plugin-config
+spec:
+ ingressClassName: apisix
+ plugins:
+ - name: authz-keycloak
+ enable: true
+ config:
+ # highlight-start
+ lazy_load_paths: true
+ resource_registration_endpoint: http://keycloak.aic.svc.cluster.local:8080/realms/quickstart-realm/authz/protection/resource_set
+ discovery: http://keycloak.aic.svc.cluster.local:8080/realms/quickstart-realm/.well-known/uma2-configuration
+ client_id: apisix-quickstart-client
+ client_secret: replace-with-your-client-secret
+ # highlight-end
+---
+apiVersion: apisix.apache.org/v2
+kind: ApisixRoute
+metadata:
+ namespace: aic
+ name: authz-keycloak-route
+spec:
+ ingressClassName: apisix
+ http:
+ - name: authz-keycloak-route
+ match:
+ paths:
+ - /anything
+ methods:
+ - GET
+ upstreams:
+ - name: httpbin-external-domain
+ plugin_config_name: authz-keycloak-plugin-config
+```
-#### Create Client Scope in Keycloak
+Apply the configuration to your cluster:
-Go to **Client Scopes** and click **Create** to open the **Add client scope** page:
+```shell
+kubectl apply -f authz-keycloak-ic.yaml
+```
-
+
-Enter the scope name `httpbin-access` and click **Save**:
+
-
+
-#### Create Policy in Keycloak
+
-Go to **Clients** > **`apisix-quickstart-client`** > **Authorization** > **Policies** > **Create Policies** and select **Client Scope** from the dropdown to open the **Add Client Scope Policy** page:
+- Set `lazy_load_paths` to `true`.
-
+- Set `resource_registration_endpoint` to Keycloak's UMA-compliant resource registration endpoint. Required when `lazy_load_paths` is `true`.
-Enter the policy name `access-client-scope-policy` for client scope `httpbin-access`, check the **Required** box, and click **Save**:
+- Set `discovery` to the discovery document endpoint of Keycloak authorization services.
-
+- Set `client_id` to the client ID created previously.
-#### Create Permission in Keycloak
+- Set `client_secret` to the client secret created previously. Required when `lazy_load_paths` is `true`.
-Go to **Clients** > **`apisix-quickstart-client`** > **Authorization** > **Permissions** > **Create Permissions** and select **Scope-Based** from the dropdown to open the **Add Scope Permission** page:
+Send a request to the Route:
-
+```shell
+curl "http://127.0.0.1:9080/anything" -H "Authorization: Bearer $ACCESS_TOKEN"
+```
-Enter the permission name `access-scope-perm`, select the `access` scope, apply the policy `access-client-scope-policy`, and click **Save**:
+You should see an `HTTP/1.1 200 OK` response similar to the following:
-
+```json
+{
+ "args": {},
+ "data": "",
+ "files": {},
+ "form": {},
+ "headers": {
+ "Accept": "*/*",
+ "Authorization": "Bearer eyJhbGciOiJSU..."
+ },
+ "json": null,
+ "method": "GET",
+ "url": "http://127.0.0.1/anything"
+}
+```
-#### Assign Client Scope
+### Use Static Permissions
-Go to **Clients** > **`apisix-quickstart-client`** > **Client Scopes** and add `httpbin-access` to the default client scopes:
+The examples below demonstrate how you can configure `authz-keycloak` to use the static permission `httpbin-anything#access`.
-
+
-#### Configure APISIX
+
-Create a route with `authz-keycloak-route` as follows:
+Create a Route with `authz-keycloak-route` as follows:
```shell
curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \
@@ -384,77 +655,233 @@ curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \
"id": "authz-keycloak-route",
"uri": "/anything",
"plugins": {
+ # highlight-start
"authz-keycloak": {
"lazy_load_paths": false,
- "discovery": "http://'"$KEYCLOAK_IP"':8080/realms/quickstart-realm/.well-known/uma2-configuration",
+ "discovery": "'"$KEYCLOAK_URL"'/realms/quickstart-realm/.well-known/uma2-configuration",
"permissions": ["httpbin-anything#access"],
- "client_id": "apisix-quickstart-client"
+ "client_id": "'"$OIDC_CLIENT_ID"'"
}
+ # highlight-end
},
"upstream": {
"type": "roundrobin",
"nodes": {
- "httpbin.org": 1
+ "httpbin.org:80": 1
}
}
}'
```
-- Set `lazy_load_paths` to `false`.
-- Set `discovery` to the discovery document endpoint of Keycloak authorization services.
-- Set `permissions` to resource `httpbin-anything` and scope `access`.
+
+
+
+
+Create a Route with `authz-keycloak` configured in ADC:
+
+```yaml title="adc.yaml"
+services:
+ - name: authz-keycloak-service
+ routes:
+ - name: authz-keycloak-route
+ uris:
+ - /anything
+ plugins:
+ # highlight-start
+ authz-keycloak:
+ lazy_load_paths: false
+ discovery: ${KEYCLOAK_URL}/realms/quickstart-realm/.well-known/uma2-configuration
+ permissions:
+ - "httpbin-anything#access"
+ client_id: ${OIDC_CLIENT_ID}
+ # highlight-end
+ upstream:
+ type: roundrobin
+ nodes:
+ - host: httpbin.org
+ port: 80
+ weight: 1
+```
-Send a request to the route:
+Synchronize the configuration to the gateway:
```shell
-curl "http://127.0.0.1:9080/anything" -H "Authorization: Bearer $ACCESS_TOKEN"
+adc sync -f adc.yaml
```
-You should see an `HTTP/1.1 200 OK` response similar to the following:
+
-```json
-{
- "args": {},
- "data": "",
- "files": {},
- "form": {},
- "headers": {
- "Accept": "*/*",
- "Authorization": "Bearer eyJhbGciOiJSU...",
- ...
- },
- "json": null,
- "method": "GET",
- "origin": "127.0.0.1, 108.180.51.111",
- "url": "http://127.0.0.1/anything"
-}
+
+
+Configure `authz-keycloak` on the Route:
+
+
+
+
+
+```yaml title="authz-keycloak-ic.yaml"
+apiVersion: v1
+kind: Service
+metadata:
+ namespace: aic
+ name: httpbin-external-domain
+spec:
+ type: ExternalName
+ externalName: httpbin.org
+---
+apiVersion: apisix.apache.org/v1alpha1
+kind: PluginConfig
+metadata:
+ namespace: aic
+ name: authz-keycloak-plugin-config
+spec:
+ plugins:
+ - name: authz-keycloak
+ config:
+ # highlight-start
+ lazy_load_paths: false
+ discovery: http://keycloak.aic.svc.cluster.local:8080/realms/quickstart-realm/.well-known/uma2-configuration
+ permissions:
+ - "httpbin-anything#access"
+ client_id: apisix-quickstart-client
+ # highlight-end
+---
+apiVersion: gateway.networking.k8s.io/v1
+kind: HTTPRoute
+metadata:
+ namespace: aic
+ name: authz-keycloak-route
+spec:
+ parentRefs:
+ - name: apisix
+ rules:
+ - matches:
+ - path:
+ type: Exact
+ value: /anything
+ filters:
+ - type: ExtensionRef
+ extensionRef:
+ group: apisix.apache.org
+ kind: PluginConfig
+ name: authz-keycloak-plugin-config
+ backendRefs:
+ - name: httpbin-external-domain
+ port: 80
```
-If you remove the client scope `httpbin-access` for `apisix-quickstart-client`, you should receive a `401 Unauthorized` response when requesting the resource.
+Apply the configuration to your cluster:
-### Generate Token with Password Grant at Custom Token Endpoint
+```shell
+kubectl apply -f authz-keycloak-ic.yaml
+```
+
+
+
+
+
+```yaml title="authz-keycloak-ic.yaml"
+apiVersion: apisix.apache.org/v2
+kind: ApisixUpstream
+metadata:
+ namespace: aic
+ name: httpbin-external-domain
+spec:
+ ingressClassName: apisix
+ externalNodes:
+ - type: Domain
+ name: httpbin.org
+---
+apiVersion: apisix.apache.org/v2
+kind: ApisixPluginConfig
+metadata:
+ namespace: aic
+ name: authz-keycloak-plugin-config
+spec:
+ ingressClassName: apisix
+ plugins:
+ - name: authz-keycloak
+ enable: true
+ config:
+ # highlight-start
+ lazy_load_paths: false
+ discovery: http://keycloak.aic.svc.cluster.local:8080/realms/quickstart-realm/.well-known/uma2-configuration
+ permissions:
+ - "httpbin-anything#access"
+ client_id: apisix-quickstart-client
+ # highlight-end
+---
+apiVersion: apisix.apache.org/v2
+kind: ApisixRoute
+metadata:
+ namespace: aic
+ name: authz-keycloak-route
+spec:
+ ingressClassName: apisix
+ http:
+ - name: authz-keycloak-route
+ match:
+ paths:
+ - /anything
+ methods:
+ - GET
+ upstreams:
+ - name: httpbin-external-domain
+ plugin_config_name: authz-keycloak-plugin-config
+```
-The examples below demonstrate how you can generate a token using the password grant at a custom endpoint.
+Apply the configuration to your cluster:
-#### Create User in Keycloak
+```shell
+kubectl apply -f authz-keycloak-ic.yaml
+```
-To use the password grant, you should first create a user.
+
-Go to **Users** > **Add user** and click on **Add user**:
+
-
+
-Enter the **Username** as `quickstart-user` and select **Save**:
+
-
+- Set `lazy_load_paths` to `false`.
-Click on **Credentials**, then set the **Password** as `quickstart-user-pass`. Switch **Temporary** to `OFF` so that you do not need to change the password the first time you log in:
+- Set `discovery` to the discovery document endpoint of Keycloak authorization services.
-
+- Set `permissions` to resource `httpbin-anything` and scope `access`.
-#### Configure APISIX
+Send a request to the Route:
-Create a route with `authz-keycloak-route` as follows:
+```shell
+curl "http://127.0.0.1:9080/anything" -H "Authorization: Bearer $ACCESS_TOKEN"
+```
+
+You should see an `HTTP/1.1 200 OK` response.
+
+If you remove the client scope `httpbin-access` from `apisix-quickstart-client`, you should receive a `401 Unauthorized` response when requesting the resource.
+
+### Generate Token with Password Grant at Custom Token Endpoint
+
+The examples below demonstrate how you can configure `authz-keycloak` to request a token with the password grant at a custom endpoint.
+
+
+
+
+
+Create a Route with `authz-keycloak-route` as follows:
```shell
curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \
@@ -463,28 +890,212 @@ curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \
"id": "authz-keycloak-route",
"uri": "/api/*",
"plugins": {
+ # highlight-start
"authz-keycloak": {
"lazy_load_paths": true,
- "resource_registration_endpoint": "http://'"$KEYCLOAK_IP"':8080/realms/quickstart-realm/authz/protection/resource_set",
+ "resource_registration_endpoint": "'"$KEYCLOAK_URL"'/realms/quickstart-realm/authz/protection/resource_set",
"client_id": "'"$OIDC_CLIENT_ID"'",
"client_secret": "'"$OIDC_CLIENT_SECRET"'",
- "token_endpoint": "http://'"$KEYCLOAK_IP"':8080/realms/quickstart-realm/protocol/openid-connect/token",
+ "token_endpoint": "'"$KEYCLOAK_URL"'/realms/quickstart-realm/protocol/openid-connect/token",
"password_grant_token_generation_incoming_uri": "/api/token"
}
+ # highlight-end
},
"upstream": {
"type": "roundrobin",
"nodes": {
- "httpbin.org": 1
+ "httpbin.org:80": 1
}
}
}'
```
-- Set `token_endpoint` to the Keycloak token endpoint. Required when discovery document is not provided.
-- Set `password_grant_token_generation_incoming_uri` to a custom URI path users can obtain tokens from.
+
+
+
+
+Create a Route with `authz-keycloak` configured in ADC:
+
+```yaml title="adc.yaml"
+services:
+ - name: authz-keycloak-service
+ routes:
+ - name: authz-keycloak-route
+ uris:
+ - /api/*
+ plugins:
+ # highlight-start
+ authz-keycloak:
+ lazy_load_paths: true
+ resource_registration_endpoint: ${KEYCLOAK_URL}/realms/quickstart-realm/authz/protection/resource_set
+ client_id: ${OIDC_CLIENT_ID}
+ client_secret: ${OIDC_CLIENT_SECRET}
+ token_endpoint: ${KEYCLOAK_URL}/realms/quickstart-realm/protocol/openid-connect/token
+ password_grant_token_generation_incoming_uri: /api/token
+ # highlight-end
+ upstream:
+ type: roundrobin
+ nodes:
+ - host: httpbin.org
+ port: 80
+ weight: 1
+```
-Send a request to the configured token endpoint. Note that the request should use the POST method and `application/x-www-form-urlencoded` as the `Content-Type`:
+Synchronize the configuration to the gateway:
+
+```shell
+adc sync -f adc.yaml
+```
+
+
+
+
+
+Configure `authz-keycloak` on the Route:
+
+
+
+
+
+```yaml title="authz-keycloak-ic.yaml"
+apiVersion: v1
+kind: Service
+metadata:
+ namespace: aic
+ name: httpbin-external-domain
+spec:
+ type: ExternalName
+ externalName: httpbin.org
+---
+apiVersion: apisix.apache.org/v1alpha1
+kind: PluginConfig
+metadata:
+ namespace: aic
+ name: authz-keycloak-plugin-config
+spec:
+ plugins:
+ - name: authz-keycloak
+ config:
+ lazy_load_paths: true
+ resource_registration_endpoint: http://keycloak.aic.svc.cluster.local:8080/realms/quickstart-realm/authz/protection/resource_set
+ client_id: apisix-quickstart-client
+ client_secret: replace-with-your-client-secret
+ # highlight-start
+ token_endpoint: http://keycloak.aic.svc.cluster.local:8080/realms/quickstart-realm/protocol/openid-connect/token
+ password_grant_token_generation_incoming_uri: /api/token
+ # highlight-end
+---
+apiVersion: gateway.networking.k8s.io/v1
+kind: HTTPRoute
+metadata:
+ namespace: aic
+ name: authz-keycloak-route
+spec:
+ parentRefs:
+ - name: apisix
+ rules:
+ - matches:
+ - path:
+ type: PathPrefix
+ value: /api/
+ filters:
+ - type: ExtensionRef
+ extensionRef:
+ group: apisix.apache.org
+ kind: PluginConfig
+ name: authz-keycloak-plugin-config
+ backendRefs:
+ - name: httpbin-external-domain
+ port: 80
+```
+
+Apply the configuration to your cluster:
+
+```shell
+kubectl apply -f authz-keycloak-ic.yaml
+```
+
+
+
+
+
+```yaml title="authz-keycloak-ic.yaml"
+apiVersion: apisix.apache.org/v2
+kind: ApisixUpstream
+metadata:
+ namespace: aic
+ name: httpbin-external-domain
+spec:
+ ingressClassName: apisix
+ externalNodes:
+ - type: Domain
+ name: httpbin.org
+---
+apiVersion: apisix.apache.org/v2
+kind: ApisixPluginConfig
+metadata:
+ namespace: aic
+ name: authz-keycloak-plugin-config
+spec:
+ ingressClassName: apisix
+ plugins:
+ - name: authz-keycloak
+ enable: true
+ config:
+ lazy_load_paths: true
+ resource_registration_endpoint: http://keycloak.aic.svc.cluster.local:8080/realms/quickstart-realm/authz/protection/resource_set
+ client_id: apisix-quickstart-client
+ client_secret: replace-with-your-client-secret
+ # highlight-start
+ token_endpoint: http://keycloak.aic.svc.cluster.local:8080/realms/quickstart-realm/protocol/openid-connect/token
+ password_grant_token_generation_incoming_uri: /api/token
+ # highlight-end
+---
+apiVersion: apisix.apache.org/v2
+kind: ApisixRoute
+metadata:
+ namespace: aic
+ name: authz-keycloak-route
+spec:
+ ingressClassName: apisix
+ http:
+ - name: authz-keycloak-route
+ match:
+ paths:
+ - /api/*
+ methods:
+ - GET
+ - POST
+ upstreams:
+ - name: httpbin-external-domain
+ plugin_config_name: authz-keycloak-plugin-config
+```
+
+Apply the configuration to your cluster:
+
+```shell
+kubectl apply -f authz-keycloak-ic.yaml
+```
+
+
+
+
+
+
+
+
+
+- Set `token_endpoint` to the Keycloak token endpoint. Required when the discovery document is not provided.
+
+- Set `password_grant_token_generation_incoming_uri` to a custom URI path where users can obtain tokens.
+
+Send a request to the configured token endpoint. The request should use the POST method and `application/x-www-form-urlencoded` as the `Content-Type`:
```shell
OIDC_USER=quickstart-user
@@ -497,8 +1108,14 @@ curl "http://127.0.0.1:9080/api/token" -X POST \
-d 'password='$OIDC_PASSWORD''
```
-You should see a JSON response with the access token, similar to the following:
+You should see a JSON response with an access token similar to the following:
-```text
-{"access_token":"eyJhbGciOiJSUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICJ6U3FFaXN6VlpuYi1sRWMzZkp0UHNpU1ZZcGs4RGN3dXI1Mkx5V05aQTR3In0...","expires_in":300,"refresh_expires_in":1800,"refresh_token":"eyJhbGciOiJIUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICI0YjFiNTQ3Yi0zZmZjLTQ5YzQtYjE2Ni03YjdhNzIxMjk1ODcifQ...","token_type":"Bearer","not-before-policy":0,"session_state":"b16b262e-1056-4515-a455-f25e077ccb76","scope":"profile email"}
+```json
+{
+ "access_token": "eyJhbGciOiJSUzI1NiIsInR5cCIgOiAiSldUIi...",
+ "expires_in": 300,
+ "refresh_expires_in": 1800,
+ "token_type": "Bearer",
+ "scope": "profile email httpbin-access"
+}
```
diff --git a/docs/en/latest/plugins/aws-lambda.md b/docs/en/latest/plugins/aws-lambda.md
index 101befc4f4f5..f348ef1cd489 100644
--- a/docs/en/latest/plugins/aws-lambda.md
+++ b/docs/en/latest/plugins/aws-lambda.md
@@ -59,7 +59,7 @@ The Plugin supports authentication and authorization with AWS via IAM user crede
The examples below demonstrate how you can configure `aws-lambda` for different scenarios.
-To follow along the examples, first log into your AWS console and create a Lambda function with any runtime. By default, the function should return `Hello from Lambda!` when called.
+To follow along the examples, please first log into your AWS console and create a Lambda function with any runtime. You do not need to customize the function and by default, the function should return `Hello from Lambda!` when called.
:::note
@@ -73,7 +73,7 @@ admin_key=$(yq '.deployment.admin.admin_key[0].key' conf/config.yaml | sed 's/"/
### Invoke Lambda Function Securely using IAM Access Keys
-The following example demonstrates how you can integrate APISIX with the Lambda function and configure IAM access keys for authorization. The `aws-lambda` Plugin implements [AWS Signature Version 4](https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_aws-signing.html) for IAM access keys.
+The following example demonstrates how you can integrate APISIX with the Lambda function and configure IAM access keys for authorization. The `aws-lambda` Plugin implements [AWS Signature Version 4](https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_aws-signing.html) for IAM access keys. You will be first creating IAM access keys and the Lambda function URL on AWS console.
For IAM access keys, go to **AWS Identity and Access Management (IAM)** and click into the user you would like to use for integration.
@@ -95,26 +95,184 @@ To create the Lambda function URL, go to the **Configuration** tab of the Lambda
Finally, create a Route in APISIX with your function URL and IAM access keys:
+
+
+
+
```shell
curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \
-H "X-API-KEY: ${admin_key}" \
-d '{
- "id": "aws-lambda-iam-route",
+ "id": "aws-lambda-route",
+ "uri": "/aws-lambda",
+ "plugins": {
+ # highlight-start
+ "aws-lambda": {
+ "function_uri": "https://your-lambda-function-url.lambda-url.us-west-2.on.aws/",
"authorization": {
"iam": {
- "accesskey": "",
- "secretkey": "",
- "aws_region": "",
+ "accesskey": "YOUR_IAM_ACCESS_KEY",
+ "secretkey": "YOUR_IAM_SECRET_KEY",
+ "aws_region": "us-west-2",
"service": "lambda"
}
},
"ssl_verify": false
+ # highlight-end
}
}
}'
```
-Replace `function_uri`, `accesskey`, `secretkey`, and `aws_region` with your actual values. Set `service` to `lambda` when integrating with a Lambda function directly.
+
+
+
+
+```yaml title="adc.yaml"
+services:
+ - name: aws-lambda-service
+ routes:
+ - name: aws-lambda-route
+ uris:
+ - /aws-lambda
+ plugins:
+ aws-lambda:
+ # highlight-start
+ function_uri: https://your-lambda-function-url.lambda-url.us-west-2.on.aws/
+ authorization:
+ iam:
+ accesskey: YOUR_IAM_ACCESS_KEY
+ secretkey: YOUR_IAM_SECRET_KEY
+ aws_region: us-west-2
+ service: lambda
+ # highlight-end
+```
+
+Synchronize the configuration to the gateway:
+
+```shell
+adc sync -f adc.yaml
+```
+
+
+
+
+
+
+
+
+
+```yaml title="aws-lambda-ic.yaml"
+apiVersion: apisix.apache.org/v1alpha1
+kind: PluginConfig
+metadata:
+ namespace: aic
+ name: aws-lambda-plugin-config
+spec:
+ plugins:
+ - name: aws-lambda
+ config:
+ # highlight-start
+ function_uri: https://your-lambda-function-url.lambda-url.us-west-2.on.aws/
+ authorization:
+ iam:
+ accesskey: YOUR_IAM_ACCESS_KEY
+ secretkey: YOUR_IAM_SECRET_KEY
+ aws_region: us-west-2
+ service: lambda
+ ssl_verify: false
+ # highlight-end
+---
+apiVersion: gateway.networking.k8s.io/v1
+kind: HTTPRoute
+metadata:
+ namespace: aic
+ name: aws-lambda-route
+spec:
+ parentRefs:
+ - name: apisix
+ rules:
+ - matches:
+ - path:
+ type: Exact
+ value: /aws-lambda
+ filters:
+ - type: ExtensionRef
+ extensionRef:
+ group: apisix.apache.org
+ kind: PluginConfig
+ name: aws-lambda-plugin-config
+```
+
+
+
+
+
+```yaml title="aws-lambda-ic.yaml"
+apiVersion: apisix.apache.org/v2
+kind: ApisixRoute
+metadata:
+ namespace: aic
+ name: aws-lambda-route
+spec:
+ ingressClassName: apisix
+ http:
+ - name: aws-lambda-route
+ match:
+ paths:
+ - /aws-lambda
+ plugins:
+ - name: aws-lambda
+ enable: true
+ config:
+ # highlight-start
+ function_uri: https://your-lambda-function-url.lambda-url.us-west-2.on.aws/
+ authorization:
+ iam:
+ accesskey: YOUR_IAM_ACCESS_KEY
+ secretkey: YOUR_IAM_SECRET_KEY
+ aws_region: us-west-2
+ service: lambda
+ ssl_verify: false
+ # highlight-end
+```
+
+
+
+
+
+Apply the configuration:
+
+```shell
+kubectl apply -f aws-lambda-ic.yaml
+```
+
+
+
+
+
+- replace with your Lambda function URL
+
+- replace with your IAM access key
+
+- replace with your IAM secret access key
+
+- replace with the AWS region of your Lambda function
+
+- set to `lambda` when integrating with Lambda function
Send a request to the Route:
@@ -138,11 +296,18 @@ To configure an API Gateway as a Lambda trigger, go to your Lambda function and
Next, select **API Gateway** as the trigger and **REST API** as the API type, and finish adding the trigger:
-
+
+
+
+
-:::note
+:::info
-Amazon API Gateway supports two types of RESTful APIs: HTTP APIs and REST APIs. Only REST APIs offer API key and IAM as security measures.
+Amazon API Gateway supports HTTP APIs and REST APIs. API key support is available only for REST APIs, which is why this example uses a REST API trigger.
:::
@@ -152,24 +317,157 @@ You should now be redirected back to the Lambda interface. To find the API key a
Finally, create a Route in APISIX with your gateway endpoint and API key:
+
+
+
+
```shell
curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \
-H "X-API-KEY: ${admin_key}" \
-d '{
- "id": "aws-lambda-apikey-route",
+ "id": "aws-lambda-route",
"uri": "/aws-lambda",
"plugins": {
+ # highlight-start
"aws-lambda": {
- "function_uri": "https:///default/api7-docs",
+ "function_uri": "https://your-api-id.execute-api.us-west-2.amazonaws.com/default/your-resource",
"authorization": {
- "apikey": ""
+ "apikey": "YOUR_API_GATEWAY_API_KEY"
},
"ssl_verify": false
}
+ # highlight-end
}
}'
```
+
+
+
+
+```yaml title="adc.yaml"
+services:
+ - name: aws-lambda-service
+ routes:
+ - name: aws-lambda-route
+ uris:
+ - /aws-lambda
+ plugins:
+ aws-lambda:
+ function_uri: https://your-api-id.execute-api.us-west-2.amazonaws.com/default/your-resource
+ authorization:
+ apikey: YOUR_API_GATEWAY_API_KEY
+ ssl_verify: false
+```
+
+Synchronize the configuration to the gateway:
+
+```shell
+adc sync -f adc.yaml
+```
+
+
+
+
+
+
+
+
+
+```yaml title="aws-lambda-ic.yaml"
+apiVersion: apisix.apache.org/v1alpha1
+kind: PluginConfig
+metadata:
+ namespace: aic
+ name: aws-lambda-plugin-config
+spec:
+ plugins:
+ - name: aws-lambda
+ config:
+ # highlight-start
+ function_uri: https://your-api-id.execute-api.us-west-2.amazonaws.com/default/your-resource
+ authorization:
+ apikey: YOUR_API_GATEWAY_API_KEY
+ ssl_verify: false
+ # highlight-end
+---
+apiVersion: gateway.networking.k8s.io/v1
+kind: HTTPRoute
+metadata:
+ namespace: aic
+ name: aws-lambda-route
+spec:
+ parentRefs:
+ - name: apisix
+ rules:
+ - matches:
+ - path:
+ type: Exact
+ value: /aws-lambda
+ filters:
+ - type: ExtensionRef
+ extensionRef:
+ group: apisix.apache.org
+ kind: PluginConfig
+ name: aws-lambda-plugin-config
+```
+
+
+
+
+
+```yaml title="aws-lambda-ic.yaml"
+apiVersion: apisix.apache.org/v2
+kind: ApisixRoute
+metadata:
+ namespace: aic
+ name: aws-lambda-route
+spec:
+ ingressClassName: apisix
+ http:
+ - name: aws-lambda-route
+ match:
+ paths:
+ - /aws-lambda
+ plugins:
+ - name: aws-lambda
+ enable: true
+ config:
+ # highlight-start
+ function_uri: https://your-api-id.execute-api.us-west-2.amazonaws.com/default/your-resource
+ authorization:
+ apikey: YOUR_API_GATEWAY_API_KEY
+ ssl_verify: false
+ # highlight-end
+```
+
+
+
+
+
+Apply the configuration:
+
+```shell
+kubectl apply -f aws-lambda-ic.yaml
+```
+
+
+
+
+
Send a request to the Route:
```shell
@@ -186,7 +484,7 @@ If your API key is invalid, you should receive an `HTTP/1.1 403 Forbidden` respo
### Forward Requests to Amazon API Gateway Sub-Paths
-The following example demonstrates how you can forward requests to a sub-path of the Amazon API Gateway and configure the API to trigger the execution of Lambda function.
+The following example demonstrates how you can forward requests to a sub-path of the Amazon API Gateway API and configure the API to trigger the execution of Lambda function.
Please follow the [previous example](#integrate-with-amazon-api-gateway-securely-with-api-key) to set up an API Gateway first.
@@ -200,7 +498,13 @@ Next, select **Create resource** to create a sub-path:
Enter the sub-path information and complete creation:
-
+
+
+
Once redirected back to the main gateway console, you should see the newly created path. Select **Create method** to configure HTTP methods for the path and the associated action:
@@ -208,38 +512,186 @@ Once redirected back to the main gateway console, you should see the newly creat
Select the allowed HTTP method in the dropdown. For the purpose of demonstration, this example continues to use the same Lambda function as the triggered action when the path is requested:
-
+
+
+
Finish the method creation. Once redirected back to the main gateway console, click on **Deploy API** to deploy the path and method changes:

-Finally, create a Route in APISIX with your gateway endpoint and API key. The `uri` must end with `*` so that any sub-path is matched to the same Route, and the matched sub-path will be appended to `function_uri`:
+Finally, create a Route in APISIX with your gateway endpoint and API key:
+
+
+
+
```shell
curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \
-H "X-API-KEY: ${admin_key}" \
-d '{
- "id": "aws-lambda-subpath-route",
+ "id": "aws-lambda-route",
+ # highlight-start
+ "uri": "/aws-lambda/*",
"plugins": {
"aws-lambda": {
- "function_uri": "https:///default",
+ "function_uri": "https://your-api-id.execute-api.us-west-2.amazonaws.com/default",
"authorization": {
- "apikey": ""
+ "apikey": "YOUR_API_GATEWAY_API_KEY"
},
"ssl_verify": false
}
}
+ # highlight-end
}'
```
+
+
+
+
+```yaml title="adc.yaml"
+services:
+ - name: aws-lambda-service
+ routes:
+ - name: aws-lambda-route
+ # highlight-start
+ uris:
+ - /aws-lambda/*
+ plugins:
+ aws-lambda:
+ function_uri: https://your-api-id.execute-api.us-west-2.amazonaws.com/default
+ authorization:
+ apikey: YOUR_API_GATEWAY_API_KEY
+ ssl_verify: false
+ # highlight-end
+```
+
+Synchronize the configuration to the gateway:
+
+```shell
+adc sync -f adc.yaml
+```
+
+
+
+
+
+
+
+
+
+```yaml title="aws-lambda-ic.yaml"
+apiVersion: apisix.apache.org/v1alpha1
+kind: PluginConfig
+metadata:
+ namespace: aic
+ name: aws-lambda-plugin-config
+spec:
+ plugins:
+ # highlight-start
+ - name: aws-lambda
+ config:
+ function_uri: https://your-api-id.execute-api.us-west-2.amazonaws.com/default
+ authorization:
+ apikey: YOUR_API_GATEWAY_API_KEY
+ ssl_verify: false
+ # highlight-end
+---
+apiVersion: gateway.networking.k8s.io/v1
+kind: HTTPRoute
+metadata:
+ namespace: aic
+ name: aws-lambda-route
+spec:
+ parentRefs:
+ - name: apisix
+ rules:
+ - matches:
+ - path:
+ type: PathPrefix
+ value: /aws-lambda/
+ filters:
+ - type: ExtensionRef
+ extensionRef:
+ group: apisix.apache.org
+ kind: PluginConfig
+ name: aws-lambda-plugin-config
+```
+
+
+
+
+
+```yaml title="aws-lambda-ic.yaml"
+apiVersion: apisix.apache.org/v2
+kind: ApisixRoute
+metadata:
+ namespace: aic
+ name: aws-lambda-route
+spec:
+ ingressClassName: apisix
+ http:
+ - name: aws-lambda-route
+ match:
+ # highlight-start
+ paths:
+ - /aws-lambda/*
+ # highlight-end
+ plugins:
+ # highlight-start
+ - name: aws-lambda
+ enable: true
+ config:
+ function_uri: https://your-api-id.execute-api.us-west-2.amazonaws.com/default
+ authorization:
+ apikey: YOUR_API_GATEWAY_API_KEY
+ ssl_verify: false
+ # highlight-end
+```
+
+
+
+
+
+Apply the configuration:
+
+```shell
+kubectl apply -f aws-lambda-ic.yaml
+```
+
+
+
+
+
+- match all sub-paths of `/aws-lambda/`
+
+- For Admin API, ADC, and APISIX CRD examples, the sub-paths matched by the wildcard `*` will be appended to the end of the `function_uri`. In the Gateway API example, `PathPrefix` matches requests under `/aws-lambda/`, so the forwarded request path continues after the configured `function_uri` prefix.
+
Send a request to the Route:
```shell
curl -i "http://127.0.0.1:9080/aws-lambda/api7-docs"
```
-APISIX will forward the request to `https:///default/api7-docs` and you should receive an `HTTP/1.1 200 OK` response with the following message:
+APISIX will forward the request to `https://your-api-id.execute-api.us-west-2.amazonaws.com/default/api7-docs` and you should receive an `HTTP/1.1 200 OK` response with the following message:
```text
"Hello from Lambda!"
diff --git a/docs/en/latest/plugins/public-api.md b/docs/en/latest/plugins/public-api.md
index c9c62c5e5786..957b81d6da25 100644
--- a/docs/en/latest/plugins/public-api.md
+++ b/docs/en/latest/plugins/public-api.md
@@ -44,6 +44,16 @@ The `public-api` Plugin exposes an internal API endpoint, making it publicly acc
The examples below demonstrate how you can configure `public-api` in different scenarios.
+:::note
+
+You can fetch the `admin_key` from `config.yaml` and save to an environment variable with the following command:
+
+```bash
+admin_key=$(yq '.deployment.admin.admin_key[0].key' conf/config.yaml | sed 's/"//g')
+```
+
+:::
+
### Expose Prometheus Metrics at Custom Endpoint
The following example demonstrates how you can disable the Prometheus export server that, by default, exposes an endpoint on port `9091`, and expose APISIX Prometheus metrics on a new public API endpoint on port `9080`, which APISIX uses to listen to other client requests.
@@ -66,7 +76,18 @@ plugin_attr:
enable_export_server: false
```
-Next, create a Route with the `public-api` Plugin and expose a public API endpoint for APISIX metrics. You should set the Route `uri` to the custom endpoint path and set the Plugin `uri` to the internal endpoint to be exposed.
+Next, create a Route with `public-api` Plugin and expose a public API endpoint for APISIX metrics:
+
+
+
+
```shell
curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \
@@ -82,6 +103,113 @@ curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \
}'
```
+
+
+
+
+```yaml title="adc.yaml"
+services:
+ - name: public-api-metrics-service
+ routes:
+ - name: prometheus-metrics
+ uris:
+ - /prometheus_metrics
+ plugins:
+ public-api:
+ uri: /apisix/prometheus/metrics
+```
+
+Synchronize the configuration to the gateway:
+
+```shell
+adc sync -f adc.yaml
+```
+
+
+
+
+
+
+
+
+
+```yaml title="public-api-ic.yaml"
+apiVersion: gateway.networking.k8s.io/v1
+kind: HTTPRoute
+metadata:
+ namespace: aic
+ name: prometheus-metrics
+spec:
+ parentRefs:
+ - name: apisix
+ rules:
+ - matches:
+ - path:
+ type: Exact
+ value: /prometheus_metrics
+ filters:
+ - type: ExtensionRef
+ extensionRef:
+ group: apisix.apache.org
+ kind: PluginConfig
+ name: public-api-metrics-plugin-config
+---
+apiVersion: apisix.apache.org/v1alpha1
+kind: PluginConfig
+metadata:
+ namespace: aic
+ name: public-api-metrics-plugin-config
+spec:
+ plugins:
+ - name: public-api
+ config:
+ uri: /apisix/prometheus/metrics
+```
+
+
+
+
+
+```yaml title="public-api-ic.yaml"
+apiVersion: apisix.apache.org/v2
+kind: ApisixRoute
+metadata:
+ namespace: aic
+ name: prometheus-metrics
+spec:
+ ingressClassName: apisix
+ http:
+ - name: prometheus-metrics
+ match:
+ paths:
+ - /prometheus_metrics
+ plugins:
+ - name: public-api
+ enable: true
+ config:
+ uri: /apisix/prometheus/metrics
+```
+
+
+
+
+
+Apply the configuration:
+
+```shell
+kubectl apply -f public-api-ic.yaml
+```
+
+
+
+
+
Send a request to the custom metrics endpoint:
```shell
@@ -113,6 +241,17 @@ The following example demonstrates how you can use the `public-api` Plugin to ex
Create a sample Route to httpbin's `/anything` endpoint for verification purpose:
+
+
+
+
```shell
curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \
-H "X-API-KEY: ${admin_key}" \
@@ -128,8 +267,132 @@ curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \
}'
```
+
+
+
+
+```yaml title="adc.yaml"
+services:
+ - name: httpbin
+ routes:
+ - name: httpbin-anything
+ uris:
+ - /anything
+ upstream:
+ type: roundrobin
+ nodes:
+ - host: httpbin.org
+ port: 80
+ weight: 1
+```
+
+Synchronize the configuration to the gateway:
+
+```shell
+adc sync -f adc.yaml
+```
+
+
+
+
+
+
+
+
+
+```yaml title="public-api-httpbin-ic.yaml"
+apiVersion: v1
+kind: Service
+metadata:
+ namespace: aic
+ name: httpbin-external-domain
+spec:
+ type: ExternalName
+ externalName: httpbin.org
+---
+apiVersion: gateway.networking.k8s.io/v1
+kind: HTTPRoute
+metadata:
+ namespace: aic
+ name: httpbin-anything
+spec:
+ parentRefs:
+ - name: apisix
+ rules:
+ - matches:
+ - path:
+ type: Exact
+ value: /anything
+ backendRefs:
+ - name: httpbin-external-domain
+ port: 80
+```
+
+
+
+
+
+```yaml title="public-api-httpbin-ic.yaml"
+apiVersion: apisix.apache.org/v2
+kind: ApisixUpstream
+metadata:
+ namespace: aic
+ name: httpbin-external-domain
+spec:
+ ingressClassName: apisix
+ externalNodes:
+ - type: Domain
+ name: httpbin.org
+---
+apiVersion: apisix.apache.org/v2
+kind: ApisixRoute
+metadata:
+ namespace: aic
+ name: httpbin-anything
+spec:
+ ingressClassName: apisix
+ http:
+ - name: httpbin-anything
+ match:
+ paths:
+ - /anything
+ upstreams:
+ - name: httpbin-external-domain
+```
+
+
+
+
+
+Apply the configuration:
+
+```shell
+kubectl apply -f public-api-httpbin-ic.yaml
+```
+
+
+
+
+
Create a Route with `public-api` Plugin and set the Route `uri` to the internal endpoint to be exposed:
+
+
+
+
```shell
curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \
-H "X-API-KEY: ${admin_key}" \
@@ -142,6 +405,109 @@ curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \
}'
```
+
+
+
+
+```yaml title="adc.yaml"
+services:
+ - name: public-api-batch-service
+ routes:
+ - name: batch-requests
+ uris:
+ - /apisix/batch-requests
+ plugins:
+ public-api: {}
+```
+
+Synchronize the configuration to the gateway:
+
+```shell
+adc sync -f adc.yaml
+```
+
+
+
+
+
+
+
+
+
+```yaml title="public-api-batch-ic.yaml"
+apiVersion: apisix.apache.org/v1alpha1
+kind: PluginConfig
+metadata:
+ namespace: aic
+ name: public-api-batch-plugin-config
+spec:
+ plugins:
+ - name: public-api
+ config: {}
+---
+apiVersion: gateway.networking.k8s.io/v1
+kind: HTTPRoute
+metadata:
+ namespace: aic
+ name: batch-requests
+spec:
+ parentRefs:
+ - name: apisix
+ rules:
+ - matches:
+ - path:
+ type: Exact
+ value: /apisix/batch-requests
+ filters:
+ - type: ExtensionRef
+ extensionRef:
+ group: apisix.apache.org
+ kind: PluginConfig
+ name: public-api-batch-plugin-config
+```
+
+
+
+
+
+```yaml title="public-api-batch-ic.yaml"
+apiVersion: apisix.apache.org/v2
+kind: ApisixRoute
+metadata:
+ namespace: aic
+ name: batch-requests
+spec:
+ ingressClassName: apisix
+ http:
+ - name: batch-requests
+ match:
+ paths:
+ - /apisix/batch-requests
+ plugins:
+ - name: public-api
+ enable: true
+```
+
+
+
+
+
+Apply the configuration:
+
+```shell
+kubectl apply -f public-api-batch-ic.yaml
+```
+
+
+
+
+
Send a pipelined request consisting of a GET and a POST request to the exposed batch requests endpoint:
```shell
@@ -184,7 +550,18 @@ You should receive responses from both requests, similar to the following:
]
```
-If you would like to expose the batch requests endpoint at a custom endpoint, create a Route with `public-api` Plugin as such. You should set the Route `uri` to the custom endpoint path and set the plugin `uri` to the internal endpoint to be exposed.
+If you would like to expose the batch requests endpoint at a custom endpoint, create a Route with `public-api` Plugin as such:
+
+
+
+
```shell
curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \
@@ -200,6 +577,113 @@ curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \
}'
```
+
+
+
+
+```yaml title="adc.yaml"
+services:
+ - name: public-api-batch-service
+ routes:
+ - name: batch-requests
+ uris:
+ - /batch-requests
+ plugins:
+ public-api:
+ uri: /apisix/batch-requests
+```
+
+Synchronize the configuration to the gateway:
+
+```shell
+adc sync -f adc.yaml
+```
+
+
+
+
+
+
+
+
+
+```yaml title="public-api-batch-ic.yaml"
+apiVersion: gateway.networking.k8s.io/v1
+kind: HTTPRoute
+metadata:
+ namespace: aic
+ name: batch-requests
+spec:
+ parentRefs:
+ - name: apisix
+ rules:
+ - matches:
+ - path:
+ type: Exact
+ value: /batch-requests
+ filters:
+ - type: ExtensionRef
+ extensionRef:
+ group: apisix.apache.org
+ kind: PluginConfig
+ name: public-api-batch-plugin-config
+---
+apiVersion: apisix.apache.org/v1alpha1
+kind: PluginConfig
+metadata:
+ namespace: aic
+ name: public-api-batch-plugin-config
+spec:
+ plugins:
+ - name: public-api
+ config:
+ uri: /apisix/batch-requests
+```
+
+
+
+
+
+```yaml title="public-api-batch-ic.yaml"
+apiVersion: apisix.apache.org/v2
+kind: ApisixRoute
+metadata:
+ namespace: aic
+ name: batch-requests
+spec:
+ ingressClassName: apisix
+ http:
+ - name: batch-requests
+ match:
+ paths:
+ - /batch-requests
+ plugins:
+ - name: public-api
+ enable: true
+ config:
+ uri: /apisix/batch-requests
+```
+
+
+
+
+
+Apply the configuration:
+
+```shell
+kubectl apply -f public-api-batch-ic.yaml
+```
+
+
+
+
+
The batch requests endpoint should now be exposed as `/batch-requests`, instead of `/apisix/batch-requests`.
Send a pipelined request consisting of a GET and a POST request to the exposed batch requests endpoint:
diff --git a/docs/en/latest/plugins/real-ip.md b/docs/en/latest/plugins/real-ip.md
index 147ad73d6689..f4b4dc6a318b 100644
--- a/docs/en/latest/plugins/real-ip.md
+++ b/docs/en/latest/plugins/real-ip.md
@@ -33,9 +33,9 @@ description: The real-ip plugin allows Apache APISIX to set the client's real IP
## Description
-The `real-ip` Plugin allows APISIX to set the client's real IP by the IP address passed in the HTTP header or HTTP query string. This is particularly useful when APISIX is behind a reverse proxy since the proxy could act as the request-originating client otherwise.
+The `real-ip` Plugin allows APISIX to set the client's real IP by IP address passed in the HTTP header or HTTP query string. This is particularly useful when APISIX is behind a reverse proxy, since the proxy could act as the request originating client otherwise.
-The Plugin is functionally similar to NGINX's [ngx_http_realip_module](https://nginx.org/en/docs/http/ngx_http_realip_module.html) but offers more flexibility.
+The Plugin is functionally similar to NGINX's [ngx_http_realip_module](https://nginx.org/en/docs/http/ngx_http_realip_module.html) but offers more flexibilities.
## Attributes
@@ -69,9 +69,20 @@ admin_key=$(yq '.deployment.admin.admin_key[0].key' conf/config.yaml | sed 's/"/
### Obtain Real Client Address From URI Parameter
-The following example demonstrates how to update the client IP address with a URI parameter.
+The following example demonstrates how to update client IP address with a URI parameter.
-Create a Route as follows. You should configure `source` to obtain value from the URL parameter `realip` using [APISIX variable](https://apisix.apache.org/docs/apisix/apisix-variable/) or [NGINX variable](https://nginx.org/en/docs/varindex.html). Use the `response-rewrite` Plugin to set response headers to verify if the client IP and port were actually updated.
+Create a Route as follows:
+
+
+
+
```shell
curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \
@@ -80,6 +91,7 @@ curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \
"id": "real-ip-route",
"uri": "/get",
"plugins": {
+ # highlight-start
"real-ip": {
"source": "arg_realip",
"trusted_addresses": ["127.0.0.0/24"]
@@ -90,6 +102,7 @@ curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \
"remote_port": "$remote_port"
}
}
+ # highlight-end
},
"upstream": {
"type": "roundrobin",
@@ -100,24 +113,202 @@ curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \
}'
```
+
+
+
+
+```yaml title="adc.yaml"
+services:
+ - name: httpbin
+ routes:
+ - name: real-ip-route
+ uris:
+ - /get
+ plugins:
+ real-ip:
+ # highlight-start
+ source: arg_realip
+ trusted_addresses:
+ - 127.0.0.0/24
+ response-rewrite:
+ headers:
+ remote_addr: $remote_addr
+ remote_port: $remote_port
+ # highlight-end
+ upstream:
+ type: roundrobin
+ nodes:
+ - host: httpbin.org
+ port: 80
+ weight: 1
+```
+
+Synchronize the configuration to the gateway:
+
+```shell
+adc sync -f adc.yaml
+```
+
+
+
+
+
+
+
+
+
+```yaml title="real-ip-ic.yaml"
+apiVersion: v1
+kind: Service
+metadata:
+ namespace: aic
+ name: httpbin-external-domain
+spec:
+ type: ExternalName
+ externalName: httpbin.org
+---
+apiVersion: apisix.apache.org/v1alpha1
+kind: PluginConfig
+metadata:
+ namespace: aic
+ name: real-ip-plugin-config
+spec:
+ plugins:
+ # highlight-start
+ - name: real-ip
+ config:
+ source: arg_realip
+ trusted_addresses:
+ - 127.0.0.0/24
+ - name: response-rewrite
+ config:
+ headers:
+ remote_addr: $remote_addr
+ remote_port: $remote_port
+ # highlight-end
+---
+apiVersion: gateway.networking.k8s.io/v1
+kind: HTTPRoute
+metadata:
+ namespace: aic
+ name: real-ip-route
+spec:
+ parentRefs:
+ - name: apisix
+ rules:
+ - matches:
+ - path:
+ type: Exact
+ value: /get
+ filters:
+ - type: ExtensionRef
+ extensionRef:
+ group: apisix.apache.org
+ kind: PluginConfig
+ name: real-ip-plugin-config
+ backendRefs:
+ - name: httpbin-external-domain
+ port: 80
+```
+
+
+
+
+
+```yaml title="real-ip-ic.yaml"
+apiVersion: apisix.apache.org/v2
+kind: ApisixUpstream
+metadata:
+ namespace: aic
+ name: httpbin-external-domain
+spec:
+ ingressClassName: apisix
+ externalNodes:
+ - type: Domain
+ name: httpbin.org
+---
+apiVersion: apisix.apache.org/v2
+kind: ApisixRoute
+metadata:
+ namespace: aic
+ name: real-ip-route
+spec:
+ ingressClassName: apisix
+ http:
+ - name: real-ip-route
+ match:
+ paths:
+ - /get
+ upstreams:
+ - name: httpbin-external-domain
+ plugins:
+ # highlight-start
+ - name: real-ip
+ config:
+ source: arg_realip
+ trusted_addresses:
+ - 127.0.0.0/24
+ - name: response-rewrite
+ config:
+ headers:
+ remote_addr: $remote_addr
+ remote_port: $remote_port
+ # highlight-end
+```
+
+
+
+
+
+Apply the configuration:
+
+```shell
+kubectl apply -f real-ip-ic.yaml
+```
+
+
+
+
+
+- Configure `source` to obtain value from the URL parameter `realip` using the built-in [APISIX variable](https://apisix.apache.org/docs/apisix/apisix-variable/) or [NGINX variable](https://nginx.org/en/docs/varindex.html).
+
+- Use the `response-rewrite` Plugin to set response headers to verify if the client IP and port were actually updated.
+
Send a request to the Route with real IP and port in the URL parameter:
```shell
curl -i "http://127.0.0.1:9080/get?realip=1.2.3.4:9080"
```
-You should see the response includes the following header:
+You should see the response includes the following headers:
```text
-remote-addr: 1.2.3.4
-remote-port: 9080
+remote_addr: 1.2.3.4
+remote_port: 9080
```
### Obtain Real Client Address From Header
-The following example shows how to set the real client IP when APISIX is behind a reverse proxy, such as a load balancer when the proxy exposes the real client IP in the [`X-Forwarded-For`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Forwarded-For) header.
+The following example shows how to set the real client IP when APISIX is behind a reverse proxy, such as a load balancer, when the proxy exposes the real client IP in the [`X-Forwarded-For`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Forwarded-For) header.
-Create a Route as follows. You should configure `source` to obtain value from the request header `X-Forwarded-For` using [APISIX variable](https://apisix.apache.org/docs/apisix/apisix-variable/) or [NGINX variable](https://nginx.org/en/docs/varindex.html). Use the `response-rewrite` Plugin to set a response header to verify if the client IP was actually updated.
+Create a Route as follows:
+
+
+
+
```shell
curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \
@@ -126,6 +317,7 @@ curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \
"id": "real-ip-route",
"uri": "/get",
"plugins": {
+ # highlight-start
"real-ip": {
"source": "http_x_forwarded_for",
"trusted_addresses": ["127.0.0.0/24"]
@@ -135,6 +327,7 @@ curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \
"remote_addr": "$remote_addr"
}
}
+ # highlight-end
},
"upstream": {
"type": "roundrobin",
@@ -145,25 +338,201 @@ curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \
}'
```
+
+
+
+
+```yaml title="adc.yaml"
+services:
+ - name: httpbin
+ routes:
+ - name: real-ip-route
+ uris:
+ - /get
+ plugins:
+ real-ip:
+ # highlight-start
+ source: http_x_forwarded_for
+ trusted_addresses:
+ - 127.0.0.0/24
+ response-rewrite:
+ headers:
+ remote_addr: $remote_addr
+ # highlight-end
+ upstream:
+ type: roundrobin
+ nodes:
+ - host: httpbin.org
+ port: 80
+ weight: 1
+```
+
+Synchronize the configuration to the gateway:
+
+```shell
+adc sync -f adc.yaml
+```
+
+
+
+
+
+
+
+
+
+```yaml title="real-ip-ic.yaml"
+apiVersion: v1
+kind: Service
+metadata:
+ namespace: aic
+ name: httpbin-external-domain
+spec:
+ type: ExternalName
+ externalName: httpbin.org
+---
+apiVersion: apisix.apache.org/v1alpha1
+kind: PluginConfig
+metadata:
+ namespace: aic
+ name: real-ip-plugin-config
+spec:
+ plugins:
+ # highlight-start
+ - name: real-ip
+ config:
+ source: http_x_forwarded_for
+ trusted_addresses:
+ - 127.0.0.0/24
+ - name: response-rewrite
+ config:
+ headers:
+ remote_addr: $remote_addr
+ # highlight-end
+---
+apiVersion: gateway.networking.k8s.io/v1
+kind: HTTPRoute
+metadata:
+ namespace: aic
+ name: real-ip-route
+spec:
+ parentRefs:
+ - name: apisix
+ rules:
+ - matches:
+ - path:
+ type: Exact
+ value: /get
+ filters:
+ - type: ExtensionRef
+ extensionRef:
+ group: apisix.apache.org
+ kind: PluginConfig
+ name: real-ip-plugin-config
+ backendRefs:
+ - name: httpbin-external-domain
+ port: 80
+```
+
+
+
+
+
+```yaml title="real-ip-ic.yaml"
+apiVersion: apisix.apache.org/v2
+kind: ApisixUpstream
+metadata:
+ namespace: aic
+ name: httpbin-external-domain
+spec:
+ ingressClassName: apisix
+ externalNodes:
+ - type: Domain
+ name: httpbin.org
+---
+apiVersion: apisix.apache.org/v2
+kind: ApisixRoute
+metadata:
+ namespace: aic
+ name: real-ip-route
+spec:
+ ingressClassName: apisix
+ http:
+ - name: real-ip-route
+ match:
+ paths:
+ - /get
+ upstreams:
+ - name: httpbin-external-domain
+ plugins:
+ # highlight-start
+ - name: real-ip
+ config:
+ source: http_x_forwarded_for
+ trusted_addresses:
+ - 127.0.0.0/24
+ - name: response-rewrite
+ config:
+ headers:
+ remote_addr: $remote_addr
+ # highlight-end
+```
+
+
+
+
+
+Apply the configuration:
+
+```shell
+kubectl apply -f real-ip-ic.yaml
+```
+
+
+
+
+
+- Configure `source` to obtain value from the request header `X-Forwarded-For` using the built-in [APISIX variable](https://apisix.apache.org/docs/apisix/apisix-variable/) or [NGINX variable](https://nginx.org/en/docs/varindex.html).
+
+- Use the `response-rewrite` Plugin to set a response header to verify if the client IP was actually updated.
+
Send a request to the Route:
```shell
-curl -i "http://127.0.0.1:9080/get"
+curl -i "http://127.0.0.1:9080/get" \
+ -H "X-Forwarded-For: 10.26.3.19"
```
You should see a response including the following header:
```text
-remote-addr: 10.26.3.19
+remote_addr: 10.26.3.19
```
-The IP address should correspond to the IP address of the request-originating client.
+The IP address should correspond to the IP address of the request originating client.
### Obtain Real Client Address Behind Multiple Proxies
The following example shows how to get the real client IP when APISIX is behind multiple proxies, which causes [`X-Forwarded-For`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Forwarded-For) header to include a list of proxy IP addresses.
-Create a Route as follows. You should configure `source` to obtain value from the request header `X-Forwarded-For` using [APISIX variable](https://apisix.apache.org/docs/apisix/apisix-variable/) or [NGINX variable](https://nginx.org/en/docs/varindex.html). Set `recursive` to `true` so that the original client address that matches one of the trusted addresses is replaced by the last non-trusted address sent in the configured `source`. Then, use the `response-rewrite` Plugin to set a response header to verify if the client IP was actually updated.
+Create a Route as follows:
+
+
+
+
```shell
curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \
@@ -172,16 +541,18 @@ curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \
"id": "real-ip-route",
"uri": "/get",
"plugins": {
+ # highlight-start
"real-ip": {
"source": "http_x_forwarded_for",
"recursive": true,
- "trusted_addresses": ["192.128.0.0/16", "127.0.0.0/24"]
+ "trusted_addresses": ["192.128.0.0/16", "127.0.0.1/32"]
},
"response-rewrite": {
"headers": {
"remote_addr": "$remote_addr"
}
}
+ # highlight-end
},
"upstream": {
"type": "roundrobin",
@@ -192,6 +563,178 @@ curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \
}'
```
+
+
+
+
+```yaml title="adc.yaml"
+services:
+ - name: httpbin
+ routes:
+ - name: real-ip-route
+ uris:
+ - /get
+ plugins:
+ real-ip:
+ # highlight-start
+ source: http_x_forwarded_for
+ recursive: true
+ trusted_addresses:
+ - 192.128.0.0/16
+ - 127.0.0.1/32
+ response-rewrite:
+ headers:
+ remote_addr: $remote_addr
+ # highlight-end
+ upstream:
+ type: roundrobin
+ nodes:
+ - host: httpbin.org
+ port: 80
+ weight: 1
+```
+
+Synchronize the configuration to the gateway:
+
+```shell
+adc sync -f adc.yaml
+```
+
+
+
+
+
+
+
+
+
+```yaml title="real-ip-ic.yaml"
+apiVersion: v1
+kind: Service
+metadata:
+ namespace: aic
+ name: httpbin-external-domain
+spec:
+ type: ExternalName
+ externalName: httpbin.org
+---
+apiVersion: apisix.apache.org/v1alpha1
+kind: PluginConfig
+metadata:
+ namespace: aic
+ name: real-ip-plugin-config
+spec:
+ plugins:
+ # highlight-start
+ - name: real-ip
+ config:
+ source: http_x_forwarded_for
+ recursive: true
+ trusted_addresses:
+ - 192.128.0.0/16
+ - 127.0.0.1/32
+ - name: response-rewrite
+ config:
+ headers:
+ remote_addr: $remote_addr
+ # highlight-end
+---
+apiVersion: gateway.networking.k8s.io/v1
+kind: HTTPRoute
+metadata:
+ namespace: aic
+ name: real-ip-route
+spec:
+ parentRefs:
+ - name: apisix
+ rules:
+ - matches:
+ - path:
+ type: Exact
+ value: /get
+ filters:
+ - type: ExtensionRef
+ extensionRef:
+ group: apisix.apache.org
+ kind: PluginConfig
+ name: real-ip-plugin-config
+ backendRefs:
+ - name: httpbin-external-domain
+ port: 80
+```
+
+
+
+
+
+```yaml title="real-ip-ic.yaml"
+apiVersion: apisix.apache.org/v2
+kind: ApisixUpstream
+metadata:
+ namespace: aic
+ name: httpbin-external-domain
+spec:
+ ingressClassName: apisix
+ externalNodes:
+ - type: Domain
+ name: httpbin.org
+---
+apiVersion: apisix.apache.org/v2
+kind: ApisixRoute
+metadata:
+ namespace: aic
+ name: real-ip-route
+spec:
+ ingressClassName: apisix
+ http:
+ - name: real-ip-route
+ match:
+ paths:
+ - /get
+ upstreams:
+ - name: httpbin-external-domain
+ plugins:
+ # highlight-start
+ - name: real-ip
+ config:
+ source: http_x_forwarded_for
+ recursive: true
+ trusted_addresses:
+ - 192.128.0.0/16
+ - 127.0.0.1/32
+ - name: response-rewrite
+ config:
+ headers:
+ remote_addr: $remote_addr
+ # highlight-end
+```
+
+
+
+
+
+Apply the configuration:
+
+```shell
+kubectl apply -f real-ip-ic.yaml
+```
+
+
+
+
+
+- Configure `source` to obtain value from the request header `X-Forwarded-For` using the built-in [APISIX variable](https://apisix.apache.org/docs/apisix/apisix-variable/) or [NGINX variable](https://nginx.org/en/docs/varindex.html).
+
+- Set `recursive` to `true` so that the original client address that matches one of the trusted addresses is replaced by the last non-trusted address sent in the configured `source`.
+
+- Use the `response-rewrite` Plugin to set a response header to verify if the client IP was actually updated.
+
Send a request to the Route:
```shell
@@ -202,5 +745,5 @@ curl -i "http://127.0.0.1:9080/get" \
You should see a response including the following header:
```text
-remote-addr: 127.0.0.2
+remote_addr: 127.0.0.2
```
diff --git a/docs/en/latest/plugins/serverless.md b/docs/en/latest/plugins/serverless.md
index e460b249cc31..6e3695cc028c 100644
--- a/docs/en/latest/plugins/serverless.md
+++ b/docs/en/latest/plugins/serverless.md
@@ -27,11 +27,13 @@ description: The serverless plugins, `serverless-pre-function` and `serverless-p
#
-->
-## Description
+
+
+
-There are two `serverless` Plugins in APISIX: `serverless-pre-function` and `serverless-post-function`. The former runs at the beginning of the specified phase, while the latter runs at the end of the specified phase.
+## Description
-Both Plugins have the same attributes.
+The serverless functions consist of two plugins, `serverless-pre-function` and `serverless-post-function`. These plugins enable the execution of user-defined logic at the beginning and end of the [execution phases](../terminology/plugin.md#plugins-execution-lifecycle) the functions hook to.
## Attributes
@@ -40,9 +42,9 @@ Both Plugins have the same attributes.
| phase | string | False | "access" | ["rewrite", "access", "header_filter", "body_filter", "log", "before_proxy"] | Phase before or after which the serverless function is executed. |
| functions | array[string] | True | | | List of functions that are executed sequentially. |
-:::note
+## Tips for Writing Functions
-Only Lua functions are allowed here and not other Lua code.
+Only Lua functions are allowed in the serverless plugins and not other Lua code.
For example, anonymous functions are legal:
@@ -69,12 +71,6 @@ local count = 1
ngx.say(count)
```
-From v2.6, `conf` and `ctx` are passed as the first two arguments to a serverless function like regular Plugins.
-
-Prior to v2.12.0, the phase `before_proxy` was called `balancer`. This was updated considering that this method would run after `access` and before the request goes Upstream and is unrelated to `balancer`.
-
-:::
-
## Examples
:::note
@@ -87,37 +83,958 @@ admin_key=$(yq '.deployment.admin.admin_key[0].key' conf/config.yaml | sed 's/"/
:::
-The following example enables `serverless-pre-function` and `serverless-post-function` Plugins on a Route:
+The examples below demonstrate how you can configure the `serverless-pre-function` and `serverless-post-function` plugins for different scenarios.
+
+### Log Information before and after a Phase
+
+The example below demonstrates how you can configure the serverless plugins to execute custom logics to log information to error logs before and after the `rewrite` [phase](../terminology/plugin.md#plugins-execution-lifecycle).
+
+Create a Route as such:
+
+
+
+
```shell
curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \
-H "X-API-KEY: ${admin_key}" \
-d '{
- "id": "serverless-route",
- "uri": "/index.html",
+ "id": "serverless-pre-route",
+ "uri": "/anything",
"plugins": {
+ # highlight-start
"serverless-pre-function": {
"phase": "rewrite",
- "functions": ["return function() ngx.log(ngx.ERR, \"serverless pre function\"); end"]
+ "functions" : [
+ "return function()
+ ngx.log(ngx.ERR, \"serverless pre function\");
+ end"
+ ]
},
"serverless-post-function": {
"phase": "rewrite",
- "functions": ["return function(conf, ctx) ngx.log(ngx.ERR, \"match uri \", ctx.curr_req_matched and ctx.curr_req_matched._path); end"]
+ "functions" : [
+ "return function(conf, ctx)
+ ngx.log(ngx.ERR, \"match uri \", ctx.curr_req_matched and ctx.curr_req_matched._path);
+ end"
+ ]
}
+ # highlight-end
},
"upstream": {
"type": "roundrobin",
"nodes": {
- "127.0.0.1:1980": 1
+ "httpbin.org:80": 1
+ }
+ }
+ }'
+```
+
+
+
+
+
+```yaml title="adc.yaml"
+services:
+ - name: httpbin
+ routes:
+ - name: serverless-pre-route
+ uris:
+ - /anything
+ plugins:
+ # highlight-start
+ serverless-pre-function:
+ phase: rewrite
+ functions:
+ - |
+ return function()
+ ngx.log(ngx.ERR, "serverless pre function")
+ end
+ serverless-post-function:
+ phase: rewrite
+ functions:
+ - |
+ return function(conf, ctx)
+ ngx.log(ngx.ERR, "match uri ", ctx.curr_req_matched and ctx.curr_req_matched._path)
+ end
+ # highlight-end
+ upstream:
+ type: roundrobin
+ nodes:
+ - host: httpbin.org
+ port: 80
+ weight: 1
+```
+
+Synchronize the configuration to the gateway:
+
+```shell
+adc sync -f adc.yaml
+```
+
+
+
+
+
+
+
+
+
+```yaml title="serverless-functions-ic.yaml"
+apiVersion: v1
+kind: Service
+metadata:
+ namespace: aic
+ name: httpbin-external-domain
+spec:
+ type: ExternalName
+ externalName: httpbin.org
+---
+apiVersion: apisix.apache.org/v1alpha1
+kind: PluginConfig
+metadata:
+ namespace: aic
+ name: serverless-functions-plugin-config
+spec:
+ plugins:
+ # highlight-start
+ - name: serverless-pre-function
+ config:
+ phase: rewrite
+ functions:
+ - |
+ return function()
+ ngx.log(ngx.ERR, "serverless pre function")
+ end
+ - name: serverless-post-function
+ config:
+ phase: rewrite
+ functions:
+ - |
+ return function(conf, ctx)
+ ngx.log(ngx.ERR, "match uri ", ctx.curr_req_matched and ctx.curr_req_matched._path)
+ end
+ # highlight-end
+---
+apiVersion: gateway.networking.k8s.io/v1
+kind: HTTPRoute
+metadata:
+ namespace: aic
+ name: serverless-pre-route
+spec:
+ parentRefs:
+ - name: apisix
+ rules:
+ - matches:
+ - path:
+ type: Exact
+ value: /anything
+ filters:
+ - type: ExtensionRef
+ extensionRef:
+ group: apisix.apache.org
+ kind: PluginConfig
+ name: serverless-functions-plugin-config
+ backendRefs:
+ - name: httpbin-external-domain
+ port: 80
+```
+
+
+
+
+
+```yaml title="serverless-functions-ic.yaml"
+apiVersion: apisix.apache.org/v2
+kind: ApisixUpstream
+metadata:
+ namespace: aic
+ name: httpbin-external-domain
+spec:
+ ingressClassName: apisix
+ externalNodes:
+ - type: Domain
+ name: httpbin.org
+---
+apiVersion: apisix.apache.org/v2
+kind: ApisixRoute
+metadata:
+ namespace: aic
+ name: serverless-pre-route
+spec:
+ ingressClassName: apisix
+ http:
+ - name: serverless-pre-route
+ match:
+ paths:
+ - /anything
+ upstreams:
+ - name: httpbin-external-domain
+ plugins:
+ # highlight-start
+ - name: serverless-pre-function
+ config:
+ phase: rewrite
+ functions:
+ - |
+ return function()
+ ngx.log(ngx.ERR, "serverless pre function")
+ end
+ - name: serverless-post-function
+ config:
+ phase: rewrite
+ functions:
+ - |
+ return function(conf, ctx)
+ ngx.log(ngx.ERR, "match uri ", ctx.curr_req_matched and ctx.curr_req_matched._path)
+ end
+ # highlight-end
+```
+
+
+
+
+
+Apply the configuration:
+
+```shell
+kubectl apply -f serverless-functions-ic.yaml
+```
+
+
+
+
+
+- Hook the serverless pre-function logic to the `rewrite` [phase](../terminology/plugin.md#plugins-execution-lifecycle).
+
+- Define a Lua function that logs a message of `serverless pre function` in the error log.
+
+- Hook the serverless post-function logic to the `rewrite` [phase](../terminology/plugin.md#plugins-execution-lifecycle).
+
+- Define a Lua function that logs the matched URI in the error log. `conf` and `ctx` can be passed as the first two arguments like other plugins, where `conf` is the plugin configurations and `ctx` is the request context.
+
+Send the request to the Route:
+
+```shell
+curl -i "http://127.0.0.1:9080/anything"
+```
+
+You should receive an `HTTP/1.1 200 OK` response and see the following entries in the error log:
+
+```text
+2024/05/09 15:07:09 [error] 51#51: *3963 [lua] [string "return function() ngx.log(ngx.ERR, "serverles..."]:1: func(): serverless pre function, client: 172.21.0.1, server: _, request: "GET /anything HTTP/1.1", host: "127.0.0.1:9080"
+2024/05/09 15:16:58 [error] 50#50: *9343 [lua] [string "return function(conf, ctx) ngx.log(ngx.ERR, "..."]:1: func(): match uri /anything, client: 172.21.0.1, server: _, request: "GET /anything HTTP/1.1", host: "127.0.0.1:9080"
+```
+
+The first entry is added by the pre-function and the second entry is added by the post-function.
+
+### Register Custom Variables
+
+The example below demonstrates how you can register custom built-in variables using the serverless plugins and use the newly created variable in logs.
+
+:::info
+
+This example cannot be completed with the Ingress Controller because it does not support configuring Route labels.
+
+:::
+
+Start an example rsyslog server:
+
+```shell
+docker run -d -p 514:514 --name example-rsyslog-server rsyslog/syslog_appliance_alpine
+```
+
+Create a [Service](../terminology/service.md) with a serverless function to register a custom variable `a6_route_labels`, enable a logging plugin to later log the custom variable, and configure an upstream:
+
+
+
+
+
+```shell
+curl "http://127.0.0.1:9180/apisix/admin/services" -X PUT \
+ -H "X-API-KEY: ${admin_key}" \
+ -d '{
+ "id":"srv_custom_var",
+ "plugins": {
+ # highlight-start
+ "serverless-pre-function": {
+ "phase": "rewrite",
+ "functions": [
+ "return function()
+ local core = require \"apisix.core\"
+ core.ctx.register_var(\"a6_route_labels\", function(ctx)
+ local route = ctx.matched_route and ctx.matched_route.value
+ if route and route.labels then
+ return route.labels
+ end
+ return nil
+ end);
+ end"
+ ]
+ },
+ "syslog": {
+ "host" : "172.0.0.1",
+ "port" : 514,
+ "flush_limit" : 1
+ }
+ # highlight-end
+ },
+ "upstream": {
+ "nodes": {
+ "httpbin.org:80": 1
}
}
}'
```
+
+
+
+
+```yaml title="adc.yaml"
+services:
+ - name: srv-custom-var
+ plugins:
+ # highlight-start
+ serverless-pre-function:
+ phase: rewrite
+ functions:
+ - |
+ return function()
+ local core = require("apisix.core")
+ core.ctx.register_var("a6_route_labels", function(ctx)
+ local route = ctx.matched_route and ctx.matched_route.value
+ if route and route.labels then
+ return route.labels
+ end
+ return nil
+ end)
+ end
+ syslog:
+ host: 172.0.0.1
+ port: 514
+ flush_limit: 1
+ # highlight-end
+ upstream:
+ nodes:
+ - host: httpbin.org
+ port: 80
+ weight: 1
+```
+
+Synchronize the configuration to the gateway:
+
+```shell
+adc sync -f adc.yaml
+```
+
+
+
+
+
+- `functions`: register a custom variable `a6_route_labels` and fetch the variable value from the matched Route's `labels` property.
+
+- `host` and `port`: replace with the address of your syslog server.
+
+- `flush_limit`: set to 1 to push log to the syslog server immediately.
+
+Next, update the log format for all `syslog` instances with the new variable by configuring the [Plugin metadata](../terminology/plugin.md#plugin-metadata):
+
+
+
+
+
+```shell
+curl "http://127.0.0.1:9180/apisix/admin/plugin_metadata/syslog" -X PUT \
+ -H "X-API-KEY: ${admin_key}" \
+ -d '{
+ "log_format": {
+ # highlight-start
+ "host": "$host",
+ "client_ip": "$remote_addr",
+ "labels": "$a6_route_labels"
+ # highlight-end
+ }
+ }'
+```
+
+
+
+
+
+```yaml title="adc.yaml"
+plugin_metadata:
+ syslog:
+ log_format:
+ # highlight-start
+ host: "$host"
+ client_ip: "$remote_addr"
+ labels: "$a6_route_labels"
+ # highlight-end
+```
+
+Synchronize the configuration to the gateway:
+
+```shell
+adc sync -f adc.yaml
+```
+
+
+
+
+
+- `$host` and `$remote_addr`: NGINX variables.
+
+- `$a6_route_labels`: custom variable.
+
+Finally, create a Route:
+
+
+
+
+
+```shell
+curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \
+ -H "X-API-KEY: ${admin_key}" \
+ -d '{
+ "id":"route_custom_var",
+ "uri":"/get",
+ # highlight-start
+ "service_id": "srv_custom_var",
+ "labels": {
+ "key": "test_a6_route_labels"
+ }
+ # highlight-end
+}'
+```
+
+
+
+
+
+```yaml title="adc.yaml"
+# Other Configs
+services:
+ - name: srv-custom-var
+ routes:
+ - name: route-custom-var
+ uris:
+ - /get
+ # highlight-start
+ labels:
+ key: test_a6_route_labels
+ # highlight-end
+```
+
+Synchronize the configuration to the gateway:
+
+```shell
+adc sync -f adc.yaml
+```
+
+
+
+
+
+- In the Admin API example, set `service_id` to associate the Route with the existing Service. In ADC, the Route is nested under the Service definition.
+
+- Add Route `labels` so the custom variable can log them.
+
+To verify the variable registration, send a request to the Route:
+
+```shell
+curl "http://127.0.0.1:9080/get"
+```
+
+You should see a log entry in your syslog server similar to the following:
+
+```json
+{
+ "host":"127.0.0.1",
+ "route_id":"route_custom_var",
+ "client_ip":"172.19.0.1",
+ # highlight-start
+ "labels":{
+ "key":"test_a6_route_labels"
+ },
+ # highlight-end
+ "service_id":"srv_custom_var"
+}
+```
+
+This verifies the custom variable was registered and it logs the `labels` information in a Route successfully.
+
+### Modify a Specific Field in Response Body
+
+The example below demonstrates how you can use the serverless plugins to remove a specific field from a JSON response body.
+
+Before proceeding with the removal, first configure a Route as follows to see the unmodified response:
+
+
+
+
+
+```shell
+curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \
+ -H "X-API-KEY: ${admin_key}" \
+ -d '{
+ "id":"serverless-remove-body-info",
+ "uri": "/get",
+ "upstream": {
+ "type": "roundrobin",
+ "nodes": {
+ "httpbin.org:80": 1
+ }
+ }
+ }'
+```
+
+
+
+
+
+```yaml title="adc.yaml"
+services:
+ - name: httpbin
+ routes:
+ - name: serverless-remove-body-info
+ uris:
+ - /get
+ upstream:
+ type: roundrobin
+ nodes:
+ - host: httpbin.org
+ port: 80
+ weight: 1
+```
+
+Synchronize the configuration to the gateway:
+
+```shell
+adc sync -f adc.yaml
+```
+
+
+
+
+
+
+
+
+
+```yaml title="serverless-remove-body-ic.yaml"
+apiVersion: v1
+kind: Service
+metadata:
+ namespace: aic
+ name: httpbin-external-domain
+spec:
+ type: ExternalName
+ externalName: httpbin.org
+---
+apiVersion: gateway.networking.k8s.io/v1
+kind: HTTPRoute
+metadata:
+ namespace: aic
+ name: serverless-remove-body-info
+spec:
+ parentRefs:
+ - name: apisix
+ rules:
+ - matches:
+ - path:
+ type: Exact
+ value: /get
+ backendRefs:
+ - name: httpbin-external-domain
+ port: 80
+```
+
+
+
+
+
+```yaml title="serverless-remove-body-ic.yaml"
+apiVersion: apisix.apache.org/v2
+kind: ApisixUpstream
+metadata:
+ namespace: aic
+ name: httpbin-external-domain
+spec:
+ ingressClassName: apisix
+ externalNodes:
+ - type: Domain
+ name: httpbin.org
+---
+apiVersion: apisix.apache.org/v2
+kind: ApisixRoute
+metadata:
+ namespace: aic
+ name: serverless-remove-body-info
+spec:
+ ingressClassName: apisix
+ http:
+ - name: serverless-remove-body-info
+ match:
+ paths:
+ - /get
+ upstreams:
+ - name: httpbin-external-domain
+```
+
+
+
+
+
+Apply the configuration:
+
+```shell
+kubectl apply -f serverless-remove-body-ic.yaml
+```
+
+
+
+
+
Send a request to the Route:
```shell
-curl -i "http://127.0.0.1:9080/index.html"
+curl "http://127.0.0.1:9080/get"
+```
+
+You should see a response similar to the following with your host and proxy's IP information:
+
+```json
+{
+ "args": {},
+ "headers": {
+ "Accept": "*/*",
+ "Host": "127.0.0.1",
+ "User-Agent": "curl/8.4.0",
+ "X-Amzn-Trace-Id": "Root=1-663db30f-51448a1b635f2f4338a4fcfc",
+ "X-Forwarded-Host": "127.0.0.1"
+ },
+ # highlight-next-line
+ "origin": "172.19.0.1, 43.252.208.84",
+ "url": "http://127.0.0.1/get"
+}
+```
+
+To remove the `origin` field from the response, update the Route with serverless plugins:
+
+
+
+
+
+```shell
+curl "http://127.0.0.1:9180/apisix/admin/routes/serverless-remove-body-info" -X PATCH \
+ -H "X-API-KEY: ${admin_key}" \
+ -d '{
+ "plugins": {
+ # highlight-start
+ "serverless-pre-function": {
+ "phase": "header_filter",
+ "functions" : [
+ "return function(conf, ctx)
+ local core = require(\"apisix.core\")
+ core.response.clear_header_as_body_modified()
+ end"
+ ]
+ },
+ "serverless-post-function": {
+ "phase": "body_filter",
+ "functions" : [
+ "return function(conf, ctx)
+ local cjson = require(\"cjson\")
+ local core = require(\"apisix.core\")
+ local body = core.response.hold_body_chunk(ctx)
+ if not body then
+ return
+ end
+ body = cjson.decode(body)
+ body.origin = nil
+ body = cjson.encode(body)
+ ngx.arg[1] = body
+ end"
+ ]
+ }
+ }
+ # highlight-end
+ }'
+```
+
+
+
+
+
+```yaml title="adc.yaml"
+services:
+ - name: httpbin
+ routes:
+ - name: serverless-remove-body-info
+ uris:
+ - /get
+ plugins:
+ # highlight-start
+ serverless-pre-function:
+ phase: header_filter
+ functions:
+ - |
+ return function(conf, ctx)
+ local core = require("apisix.core")
+ core.response.clear_header_as_body_modified()
+ end
+ serverless-post-function:
+ phase: body_filter
+ functions:
+ - |
+ return function(conf, ctx)
+ local cjson = require("cjson")
+ local core = require("apisix.core")
+ local body = core.response.hold_body_chunk(ctx)
+ if not body then
+ return
+ end
+ body = cjson.decode(body)
+ body.origin = nil
+ body = cjson.encode(body)
+ ngx.arg[1] = body
+ end
+ # highlight-end
+ upstream:
+ type: roundrobin
+ nodes:
+ - host: httpbin.org
+ port: 80
+ weight: 1
+```
+
+Synchronize the configuration to the gateway:
+
+```shell
+adc sync -f adc.yaml
+```
+
+
+
+
+
+
+
+
+
+```yaml title="serverless-remove-body-ic.yaml"
+# Other Configs
+# ---
+apiVersion: apisix.apache.org/v1alpha1
+kind: PluginConfig
+metadata:
+ namespace: aic
+ name: serverless-remove-body-plugin-config
+spec:
+ plugins:
+ # highlight-start
+ - name: serverless-pre-function
+ config:
+ phase: header_filter
+ functions:
+ - |
+ return function(conf, ctx)
+ local core = require("apisix.core")
+ core.response.clear_header_as_body_modified()
+ end
+ - name: serverless-post-function
+ config:
+ phase: body_filter
+ functions:
+ - |
+ return function(conf, ctx)
+ local cjson = require("cjson")
+ local core = require("apisix.core")
+ local body = core.response.hold_body_chunk(ctx)
+ if not body then
+ return
+ end
+ body = cjson.decode(body)
+ body.origin = nil
+ body = cjson.encode(body)
+ ngx.arg[1] = body
+ end
+ # highlight-end
+---
+apiVersion: gateway.networking.k8s.io/v1
+kind: HTTPRoute
+metadata:
+ namespace: aic
+ name: serverless-remove-body-info
+spec:
+ parentRefs:
+ - name: apisix
+ rules:
+ - matches:
+ - path:
+ type: Exact
+ value: /get
+ filters:
+ - type: ExtensionRef
+ extensionRef:
+ group: apisix.apache.org
+ kind: PluginConfig
+ name: serverless-remove-body-plugin-config
+ backendRefs:
+ - name: httpbin-external-domain
+ port: 80
+```
+
+
+
+
+
+```yaml title="serverless-remove-body-ic.yaml"
+# Other Configs
+# ---
+apiVersion: apisix.apache.org/v2
+kind: ApisixRoute
+metadata:
+ namespace: aic
+ name: serverless-remove-body-info
+spec:
+ ingressClassName: apisix
+ http:
+ - name: serverless-remove-body-info
+ match:
+ paths:
+ - /get
+ upstreams:
+ - name: httpbin-external-domain
+ plugins:
+ # highlight-start
+ - name: serverless-pre-function
+ config:
+ phase: header_filter
+ functions:
+ - |
+ return function(conf, ctx)
+ local core = require("apisix.core")
+ core.response.clear_header_as_body_modified()
+ end
+ - name: serverless-post-function
+ config:
+ phase: body_filter
+ functions:
+ - |
+ return function(conf, ctx)
+ local cjson = require("cjson")
+ local core = require("apisix.core")
+ local body = core.response.hold_body_chunk(ctx)
+ if not body then
+ return
+ end
+ body = cjson.decode(body)
+ body.origin = nil
+ body = cjson.encode(body)
+ ngx.arg[1] = body
+ end
+ # highlight-end
+```
+
+
+
+
+
+Apply the configuration:
+
+```shell
+kubectl apply -f serverless-remove-body-ic.yaml
+```
+
+
+
+
+
+- Execute a pre-function in the `header_filter` [phase](../terminology/plugin.md#plugins-execution-lifecycle).
+
+- Execute a post-function in the `body_filter` [phase](../terminology/plugin.md#plugins-execution-lifecycle).
+
+The pre-function calls `clear_header_as_body_modified` to clear body-related response headers such as `Content-Length`. The post-function collects the response body with `hold_body_chunk`, decodes the JSON payload, removes the `origin` field, and writes the updated body back to the response.
+
+Send another request to the Route:
+
+```shell
+curl "http://127.0.0.1:9080/get"
+```
+
+You should see a response without the `origin` information:
+
+```json
+{
+ "url":"http://127.0.0.1/get",
+ "args":{},
+ "headers":{
+ "X-Forwarded-Host":"127.0.0.1",
+ "Host":"127.0.0.1",
+ "Accept":"*/*",
+ "User-Agent":"curl/8.4.0",
+ "X-Amzn-Trace-Id":"Root=1-663db276-1c15276864294d963c6e1755"
+ }
+}
```
-You will find messages `serverless pre function` and `match uri /index.html` in the error log.
+For simpler response modifications, such as modifying HTTP status codes, request headers, or the entire response body, please use the [`response-rewrite`](./response-rewrite.md) plugin.
diff --git a/docs/zh/latest/plugins/authz-keycloak.md b/docs/zh/latest/plugins/authz-keycloak.md
index 5f912dbf776c..3ef8c131f5f7 100644
--- a/docs/zh/latest/plugins/authz-keycloak.md
+++ b/docs/zh/latest/plugins/authz-keycloak.md
@@ -1,5 +1,5 @@
---
-title: authz-keycloak
+title: Keycloak Authorization (authz-keycloak)
keywords:
- Apache APISIX
- API 网关
@@ -34,15 +34,9 @@ description: authz-keycloak 插件与 Keycloak 集成,用于用户认证和授
## 描述
-`authz-keycloak` 插件可用于通过 [Keycloak Identity Server](https://www.keycloak.org/) 进行认证。
+`authz-keycloak` 插件与 [Keycloak](https://www.keycloak.org/) 集成,用于用户认证和授权。有关本插件可用配置选项的更多信息,请参阅 Keycloak 的 [Authorization Services Guide](https://www.keycloak.org/docs/latest/authorization_services/)。
-:::tip
-
-虽然本插件是针对 Keycloak 开发的,但也应该适用于任何符合 OAuth/OIDC 和 UMA 规范的身份提供商。
-
-:::
-
-有关 Keycloak 的更多信息,请参阅 [Authorization Services Guide](https://www.keycloak.org/docs/latest/authorization_services/)。
+虽然本插件是针对 Keycloak 开发的,但理论上也可与其他符合 OAuth/OIDC 和 UMA 规范的身份提供商一起使用。
## 属性
@@ -52,26 +46,26 @@ description: authz-keycloak 插件与 Keycloak 集成,用于用户认证和授
| token_endpoint | string | 否 | | https://host.domain/realms/foo/protocol/openid-connect/token | 支持 `urn:ietf:params:oauth:grant-type:uma-ticket` 授权类型的符合 OAuth2 规范的令牌端点。若设置,将覆盖从发现文档中获取的值。 |
| resource_registration_endpoint | string | 否 | | https://host.domain/realms/foo/authz/protection/resource_set | 符合 UMA 规范的资源注册端点。若设置,将覆盖从发现文档中获取的值。 |
| client_id | string | 是 | | | 客户端尝试访问的资源服务器的标识符。 |
-| client_secret | string | 否 | | | 客户端密钥(如需要)。可以使用 APISIX secret 存储和引用该值,APISIX 目前支持[环境变量和 HashiCorp Vault](../terminology/secret.md) 两种方式。 |
+| client_secret | string | 否 | | | 客户端密钥 (如需要)。可以使用 APISIX Secret 存储和引用该值。APISIX 目前支持通过两种方式存储 secret:[环境变量和 HashiCorp Vault](../terminology/secret.md)。 |
| grant_type | string | 否 | "urn:ietf:params:oauth:grant-type:uma-ticket" | ["urn:ietf:params:oauth:grant-type:uma-ticket"] | |
| policy_enforcement_mode | string | 否 | "ENFORCING" | ["ENFORCING", "PERMISSIVE"] | |
| permissions | array[string] | 否 | | | 字符串数组,每个字符串代表客户端请求访问的一个或多个资源和作用域的集合。 |
| lazy_load_paths | boolean | 否 | false | | 设置为 `true` 时,使用资源注册端点将请求 URI 动态解析为资源,而非使用静态权限。 |
| http_method_as_scope | boolean | 否 | false | | 设置为 `true` 时,将 HTTP 请求方法映射为同名作用域,并添加到所有请求的权限中。 |
-| timeout | integer | 否 | 3000 | [1000, ...] | 与 Identity Server 进行 HTTP 连接的超时时间(毫秒)。 |
-| access_token_expires_in | integer | 否 | 300 | [1, ...] | 访问令牌的过期时间(秒)。 |
-| access_token_expires_leeway | integer | 否 | 0 | [0, ...] | 访问令牌续期的宽限时间(秒)。设置后,将在令牌过期前 access_token_expires_leeway 秒进行续期,以避免访问令牌恰好在到达 OAuth 资源服务器时过期的错误。 |
-| refresh_token_expires_in | integer | 否 | 3600 | [1, ...] | 刷新令牌的过期时间(秒)。 |
-| refresh_token_expires_leeway | integer | 否 | 0 | [0, ...] | 刷新令牌续期的宽限时间(秒)。设置后,将在令牌过期前 refresh_token_expires_leeway 秒进行续期,以避免刷新令牌恰好在到达 OAuth 资源服务器时过期的错误。 |
+| timeout | integer | 否 | 3000 | [1000, ...] | 与 Identity Server 进行 HTTP 连接的超时时间 (毫秒)。 |
+| access_token_expires_in | integer | 否 | 300 | [1, ...] | 访问令牌的过期时间 (秒)。 |
+| access_token_expires_leeway | integer | 否 | 0 | [0, ...] | 访问令牌续期的宽限时间 (秒)。设置后,将在令牌过期前 access_token_expires_leeway 秒进行续期,以避免访问令牌恰好在到达 OAuth 资源服务器时过期的错误。 |
+| refresh_token_expires_in | integer | 否 | 3600 | [1, ...] | 刷新令牌的过期时间 (秒)。 |
+| refresh_token_expires_leeway | integer | 否 | 0 | [0, ...] | 刷新令牌续期的宽限时间 (秒)。设置后,将在令牌过期前 refresh_token_expires_leeway 秒进行续期,以避免刷新令牌恰好在到达 OAuth 资源服务器时过期的错误。 |
| ssl_verify | boolean | 否 | true | | 设置为 `true` 时,验证 TLS 证书与主机名是否匹配。 |
-| cache_ttl_seconds | integer | 否 | 86400(相当于 24 小时) | 正整数 >= 1 | 插件缓存发现文档和用于向 Keycloak 认证的令牌的最长时间(秒)。 |
+| cache_ttl_seconds | integer | 否 | 86400(相当于 24 小时) | 正整数 >= 1 | 插件缓存发现文档和用于向 Keycloak 认证的令牌的最长时间 (秒)。 |
| keepalive | boolean | 否 | true | | 设置为 `true` 时,启用 HTTP keep-alive,保持连接在使用后不关闭。如果预期有大量请求发往 Keycloak,建议设为 `true`。 |
| keepalive_timeout | integer | 否 | 60000 | 正整数 >= 1000 | 已建立的 HTTP 连接在空闲多久后关闭。 |
| keepalive_pool | integer | 否 | 5 | 正整数 >= 1 | 连接池中的最大连接数。 |
| access_denied_redirect_uri | string | 否 | | [1, 2048] | 用于替代返回 `"error_description":"not_authorized"` 错误信息而重定向用户的 URI。 |
| password_grant_token_generation_incoming_uri | string | 否 | | /api/token | 设置此项以使用密码授权类型生成令牌。插件会将传入请求的 URI 与此值进行比较。 |
-注意:schema 中还定义了 `encrypt_fields = {"client_secret"}`,这意味着该字段将以加密方式存储在 etcd 中。请参阅[加密存储字段](../../../en/latest/plugin-develop.md#encrypted-storage-fields)。
+注意:schema 中还定义了 `encrypt_fields = {"client_secret"}`,这意味着该字段将以加密方式存储在 etcd 中。请参阅[加密存储字段](../plugin-develop.md#encrypted-storage-fields)。
### 发现文档与端点
@@ -156,7 +150,17 @@ admin_key=$(yq '.deployment.admin.admin_key[0].key' conf/config.yaml | sed 's/"/
#### 启动 Keycloak
-在 Docker 中以[开发模式](https://www.keycloak.org/server/configuration#_starting_keycloak_in_development_mode)启动一个名为 `apisix-quickstart-keycloak` 的 Keycloak 实例,管理员用户名为 `quickstart-admin`,密码为 `quickstart-admin-pass`:
+
+
+
+
+以 [开发模式](https://www.keycloak.org/server/configuration#_starting_keycloak_in_development_mode) 启动一个名为 `apisix-quickstart-keycloak` 的 Keycloak 实例,管理员用户名为 `quickstart-admin`,密码为 `quickstart-admin-pass`:
```shell
docker run -d --name "apisix-quickstart-keycloak" \
@@ -166,13 +170,103 @@ docker run -d --name "apisix-quickstart-keycloak" \
quay.io/keycloak/keycloak:18.0.2 start-dev
```
-将 Keycloak 的 IP 保存到环境变量,以供后续配置引用:
+
+
+
+
+将 Keycloak 部署到 Kubernetes:
+
+```yaml title="keycloak.yaml"
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+ namespace: aic
+ name: keycloak
+spec:
+ replicas: 1
+ selector:
+ matchLabels:
+ app: keycloak
+ template:
+ metadata:
+ labels:
+ app: keycloak
+ spec:
+ containers:
+ - name: keycloak
+ image: quay.io/keycloak/keycloak:18.0.2
+ args:
+ - start-dev
+ env:
+ - name: KEYCLOAK_ADMIN
+ value: quickstart-admin
+ - name: KEYCLOAK_ADMIN_PASSWORD
+ value: quickstart-admin-pass
+ ports:
+ - containerPort: 8080
+---
+apiVersion: v1
+kind: Service
+metadata:
+ namespace: aic
+ name: keycloak
+spec:
+ selector:
+ app: keycloak
+ ports:
+ - port: 8080
+ targetPort: 8080
+```
+
+应用清单:
+
+```shell
+kubectl apply -f keycloak.yaml
+```
+
+如需在本地打开 Keycloak 控制台,可以端口转发 Service:
+
+```shell
+kubectl port-forward -n aic svc/keycloak 8080:8080
+```
+
+
+
+
+
+#### 保存 Keycloak URL
+
+将 Keycloak URL 保存到环境变量,以供后续配置引用:
+
+
+
+
+
+```shell
+KEYCLOAK_URL=http://192.168.42.145:8080 # 替换为你的 Keycloak URL
+```
+
+
+
+
```shell
-KEYCLOAK_IP=192.168.42.145 # 替换为你的主机 IP
+KEYCLOAK_URL=http://keycloak.aic.svc.cluster.local:8080
```
-在浏览器中访问 `http://localhost:8080` 并点击 **Administration Console**:
+
+
+
+
+#### 创建 Realm、Client 和授权对象
+
+在浏览器中访问 `http://localhost:8080` 并点击 __Administration Console__:

@@ -180,43 +274,55 @@ KEYCLOAK_IP=192.168.42.145 # 替换为你的主机 IP

-#### 创建 Realm
+创建名为 `quickstart-realm` 的 Realm:
-在左侧菜单中,将鼠标悬停在 **Master** 上,然后在下拉菜单中选择 **Add realm**:
+
-
+创建名为 `apisix-quickstart-client` 的客户端:
-输入 Realm 名称 `quickstart-realm` 并点击 **Create**:
+
-
+在客户端设置页面,将访问类型选择为 `confidential`:
-#### 创建客户端
+
-点击 **Clients** > **Create** 打开 **Add Client** 页面:
+为客户端启用授权并保存配置。此操作会自动启用客户端服务账号并分配 `uma_protection` 角色:
-
+
-将 **Client ID** 填写为 `apisix-quickstart-client`,保持 **Client Protocol** 为 `openid-connect`,然后点击 **Save**:
+创建名为 `httpbin-access` 的客户端作用域:
-
+
-客户端 `apisix-quickstart-client` 创建成功。跳转到详情页后,将 **Access Type** 选择为 `confidential`:
+在客户端的 **Authorization** 部分,创建授权作用域 `access`:
-
+
-SSO 登录成功后,Keycloak 会携带 state 和 code 将客户端重定向到 **Valid Redirect URIs** 中的地址。为简化演示,输入通配符 `*` 接受任意重定向 URI:
+创建资源 `httpbin-anything`,URI 为 `/anything`,作用域为 `access`:
-
+
-为客户端启用授权,此操作会自动启用服务账号,并分配 `uma_protection` 角色:
+创建客户端作用域策略 `access-client-scope-policy`,要求客户端拥有 `httpbin-access` 作用域:
-
+
-点击 **Save** 应用自定义配置。
+创建基于作用域的权限 `access-scope-perm`,使用 `access` 作用域和 `access-client-scope-policy`:
-#### 保存 Client ID 和密钥
+
-点击 **Clients** > `apisix-quickstart-client` > **Credentials**,从 **Secret** 中复制客户端密钥:
+将 `httpbin-access` 添加到 `apisix-quickstart-client` 的默认客户端作用域:
+
+
+
+创建名为 `quickstart-user` 的用户:
+
+
+
+将密码设置为 `quickstart-user-pass` 并关闭 **Temporary**:
+
+
+
+从 **Clients** > `apisix-quickstart-client` > **Credentials** 保存客户端密钥:

@@ -227,35 +333,66 @@ OIDC_CLIENT_ID=apisix-quickstart-client
OIDC_CLIENT_SECRET=bSaIN3MV1YynmtXvU8lKkfeY0iwpr9cH # 替换为你的实际值
```
-#### 获取访问令牌
+:::tip
+
+如果 APISIX 在 Kubernetes 中运行,请在插件配置和令牌请求中保持使用相同的 Keycloak 主机名。否则,Keycloak 可能会因令牌签发者与配置的授权端点不匹配而拒绝持有者令牌。
+
+:::
+
+#### 请求访问令牌
-从 Keycloak 获取访问令牌:
+从 Keycloak 请求访问令牌并保存到 `ACCESS_TOKEN`:
+
+
+
+
```shell
-curl -i "http://$KEYCLOAK_IP:8080/realms/quickstart-realm/protocol/openid-connect/token" -X POST \
+ACCESS_TOKEN=$(curl -sS "$KEYCLOAK_URL/realms/quickstart-realm/protocol/openid-connect/token" \
-d 'grant_type=client_credentials' \
-d 'client_id='$OIDC_CLIENT_ID'' \
- -d 'client_secret='$OIDC_CLIENT_SECRET''
+ -d 'client_secret='$OIDC_CLIENT_SECRET'' | jq -r '.access_token')
```
-你应该会看到类似如下的响应:
+
-```text
-{"access_token":"eyJhbGciOiJSUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICJoT3ludlBPY2d6Y3VWWnYtTU42bXZKMUczb0dOX2d6MFo3WFl6S2FSa1NBIn0...","expires_in":300,"refresh_expires_in":0,"token_type":"Bearer","not-before-policy":0,"scope":"email profile"}
-```
+
-将令牌保存到环境变量:
+在 Keycloak Pod 内运行令牌请求并将结果保存到 `ACCESS_TOKEN`:
```shell
-# 替换为你的访问令牌
-ACCESS_TOKEN=
+ACCESS_TOKEN=$(kubectl exec -n aic deploy/keycloak -- env OIDC_CLIENT_SECRET="$OIDC_CLIENT_SECRET" sh -lc 'curl -sS "http://keycloak.aic.svc.cluster.local:8080/realms/quickstart-realm/protocol/openid-connect/token" \
+ -d grant_type=client_credentials \
+ -d client_id=apisix-quickstart-client \
+ -d client_secret="$OIDC_CLIENT_SECRET"' | jq -r '.access_token')
```
+
+
+
+
### 使用懒加载路径和资源注册端点
-以下示例演示如何配置插件,使用资源注册端点将请求 URI 动态解析为资源,而非使用静态权限。
+以下示例演示如何配置 `authz-keycloak` 使用资源注册端点将请求 URI 动态解析为一个或多个资源,而非使用静态权限。
+
+
+
+
-按如下方式创建路由 `authz-keycloak-route`:
+按如下方式创建路由 `authz-keycloak-route`:
```shell
curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \
@@ -264,28 +401,211 @@ curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \
"id": "authz-keycloak-route",
"uri": "/anything",
"plugins": {
+ # highlight-start
"authz-keycloak": {
"lazy_load_paths": true,
- "resource_registration_endpoint": "http://'"$KEYCLOAK_IP"':8080/realms/quickstart-realm/authz/protection/resource_set",
- "discovery": "http://'"$KEYCLOAK_IP"':8080/realms/quickstart-realm/.well-known/uma2-configuration",
+ "resource_registration_endpoint": "'"$KEYCLOAK_URL"'/realms/quickstart-realm/authz/protection/resource_set",
+ "discovery": "'"$KEYCLOAK_URL"'/realms/quickstart-realm/.well-known/uma2-configuration",
"client_id": "'"$OIDC_CLIENT_ID"'",
"client_secret": "'"$OIDC_CLIENT_SECRET"'"
}
+ # highlight-end
},
"upstream": {
"type": "roundrobin",
"nodes": {
- "httpbin.org": 1
+ "httpbin.org:80": 1
}
}
}'
```
+
+
+
+
+在 ADC 中配置 `authz-keycloak` 创建路由:
+
+```yaml title="adc.yaml"
+services:
+ - name: authz-keycloak-service
+ routes:
+ - name: authz-keycloak-route
+ uris:
+ - /anything
+ plugins:
+ # highlight-start
+ authz-keycloak:
+ lazy_load_paths: true
+ resource_registration_endpoint: ${KEYCLOAK_URL}/realms/quickstart-realm/authz/protection/resource_set
+ discovery: ${KEYCLOAK_URL}/realms/quickstart-realm/.well-known/uma2-configuration
+ client_id: ${OIDC_CLIENT_ID}
+ client_secret: ${OIDC_CLIENT_SECRET}
+ # highlight-end
+ upstream:
+ type: roundrobin
+ nodes:
+ - host: httpbin.org
+ port: 80
+ weight: 1
+```
+
+将配置同步到网关:
+
+```shell
+adc sync -f adc.yaml
+```
+
+
+
+
+
+在路由上配置 `authz-keycloak`:
+
+
+
+
+
+```yaml title="authz-keycloak-ic.yaml"
+apiVersion: v1
+kind: Service
+metadata:
+ namespace: aic
+ name: httpbin-external-domain
+spec:
+ type: ExternalName
+ externalName: httpbin.org
+---
+apiVersion: apisix.apache.org/v1alpha1
+kind: PluginConfig
+metadata:
+ namespace: aic
+ name: authz-keycloak-plugin-config
+spec:
+ plugins:
+ - name: authz-keycloak
+ config:
+ # highlight-start
+ lazy_load_paths: true
+ resource_registration_endpoint: http://keycloak.aic.svc.cluster.local:8080/realms/quickstart-realm/authz/protection/resource_set
+ discovery: http://keycloak.aic.svc.cluster.local:8080/realms/quickstart-realm/.well-known/uma2-configuration
+ client_id: apisix-quickstart-client
+ client_secret: replace-with-your-client-secret
+ # highlight-end
+---
+apiVersion: gateway.networking.k8s.io/v1
+kind: HTTPRoute
+metadata:
+ namespace: aic
+ name: authz-keycloak-route
+spec:
+ parentRefs:
+ - name: apisix
+ rules:
+ - matches:
+ - path:
+ type: Exact
+ value: /anything
+ filters:
+ - type: ExtensionRef
+ extensionRef:
+ group: apisix.apache.org
+ kind: PluginConfig
+ name: authz-keycloak-plugin-config
+ backendRefs:
+ - name: httpbin-external-domain
+ port: 80
+```
+
+将配置应用到集群:
+
+```shell
+kubectl apply -f authz-keycloak-ic.yaml
+```
+
+
+
+
+
+```yaml title="authz-keycloak-ic.yaml"
+apiVersion: apisix.apache.org/v2
+kind: ApisixUpstream
+metadata:
+ namespace: aic
+ name: httpbin-external-domain
+spec:
+ ingressClassName: apisix
+ externalNodes:
+ - type: Domain
+ name: httpbin.org
+---
+apiVersion: apisix.apache.org/v2
+kind: ApisixPluginConfig
+metadata:
+ namespace: aic
+ name: authz-keycloak-plugin-config
+spec:
+ ingressClassName: apisix
+ plugins:
+ - name: authz-keycloak
+ enable: true
+ config:
+ # highlight-start
+ lazy_load_paths: true
+ resource_registration_endpoint: http://keycloak.aic.svc.cluster.local:8080/realms/quickstart-realm/authz/protection/resource_set
+ discovery: http://keycloak.aic.svc.cluster.local:8080/realms/quickstart-realm/.well-known/uma2-configuration
+ client_id: apisix-quickstart-client
+ client_secret: replace-with-your-client-secret
+ # highlight-end
+---
+apiVersion: apisix.apache.org/v2
+kind: ApisixRoute
+metadata:
+ namespace: aic
+ name: authz-keycloak-route
+spec:
+ ingressClassName: apisix
+ http:
+ - name: authz-keycloak-route
+ match:
+ paths:
+ - /anything
+ methods:
+ - GET
+ upstreams:
+ - name: httpbin-external-domain
+ plugin_config_name: authz-keycloak-plugin-config
+```
+
+将配置应用到集群:
+
+```shell
+kubectl apply -f authz-keycloak-ic.yaml
+```
+
+
+
+
+
+
+
+
+
- 将 `lazy_load_paths` 设置为 `true`。
-- 将 `resource_registration_endpoint` 设置为 Keycloak 符合 UMA 规范的资源注册端点。当 `lazy_load_paths` 为 `true` 且未提供 `discovery` 时,此项必填。
+
+- 将 `resource_registration_endpoint` 设置为 Keycloak 符合 UMA 规范的资源注册端点。当 `lazy_load_paths` 为 `true` 时必填。
+
- 将 `discovery` 设置为 Keycloak 授权服务的发现文档端点。
+
- 将 `client_id` 设置为之前创建的客户端 ID。
-- 将 `client_secret` 设置为之前创建的客户端密钥。当 `lazy_load_paths` 为 `true` 时,此项必填。
+
+- 将 `client_secret` 设置为之前创建的客户端密钥。当 `lazy_load_paths` 为 `true` 时必填。
向路由发送请求:
@@ -303,188 +623,479 @@ curl "http://127.0.0.1:9080/anything" -H "Authorization: Bearer $ACCESS_TOKEN"
"form": {},
"headers": {
"Accept": "*/*",
- "Authorization": "Bearer eyJhbGciOiJSU...",
- ...
+ "Authorization": "Bearer eyJhbGciOiJSU..."
},
"json": null,
"method": "GET",
- "origin": "127.0.0.1, 108.180.51.111",
"url": "http://127.0.0.1/anything"
}
```
### 使用静态权限
-以下示例演示如何在 Keycloak 中配置与客户端作用域策略关联的基于作用域的权限,并配置 `authz-keycloak` 插件使用静态权限。
+以下示例演示如何配置 `authz-keycloak` 使用静态权限 `httpbin-anything#access`。
-#### 在 Keycloak 中创建作用域
+
-前往 **Clients** > **`apisix-quickstart-client`** > **Authorization** > **Authorization Scopes**,点击 **Create** 打开 **Add Scope** 页面:
+
-
+按如下方式创建路由 `authz-keycloak-route`:
+
+```shell
+curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \
+ -H "X-API-KEY: ${admin_key}" \
+ -d '{
+ "id": "authz-keycloak-route",
+ "uri": "/anything",
+ "plugins": {
+ # highlight-start
+ "authz-keycloak": {
+ "lazy_load_paths": false,
+ "discovery": "'"$KEYCLOAK_URL"'/realms/quickstart-realm/.well-known/uma2-configuration",
+ "permissions": ["httpbin-anything#access"],
+ "client_id": "'"$OIDC_CLIENT_ID"'"
+ }
+ # highlight-end
+ },
+ "upstream": {
+ "type": "roundrobin",
+ "nodes": {
+ "httpbin.org:80": 1
+ }
+ }
+ }'
+```
-输入作用域名称 `access` 并点击 **Save**:
+
+
+
+
+在 ADC 中配置 `authz-keycloak` 创建路由:
+
+```yaml title="adc.yaml"
+services:
+ - name: authz-keycloak-service
+ routes:
+ - name: authz-keycloak-route
+ uris:
+ - /anything
+ plugins:
+ # highlight-start
+ authz-keycloak:
+ lazy_load_paths: false
+ discovery: ${KEYCLOAK_URL}/realms/quickstart-realm/.well-known/uma2-configuration
+ permissions:
+ - "httpbin-anything#access"
+ client_id: ${OIDC_CLIENT_ID}
+ # highlight-end
+ upstream:
+ type: roundrobin
+ nodes:
+ - host: httpbin.org
+ port: 80
+ weight: 1
+```
-
+将配置同步到网关:
-#### 在 Keycloak 中创建资源
+```shell
+adc sync -f adc.yaml
+```
-前往 **Clients** > **`apisix-quickstart-client`** > **Authorization** > **Resources**,点击 **Create** 打开 **Add Resource** 页面:
+
-
+
-输入资源名称 `httpbin-anything`、URI `/anything`、作用域 `access`,然后点击 **Save**:
+在路由上配置 `authz-keycloak`:
-
+
-#### 在 Keycloak 中创建客户端作用域
+
-前往 **Client Scopes**,点击 **Create** 打开 **Add client scope** 页面:
+```yaml title="authz-keycloak-ic.yaml"
+apiVersion: v1
+kind: Service
+metadata:
+ namespace: aic
+ name: httpbin-external-domain
+spec:
+ type: ExternalName
+ externalName: httpbin.org
+---
+apiVersion: apisix.apache.org/v1alpha1
+kind: PluginConfig
+metadata:
+ namespace: aic
+ name: authz-keycloak-plugin-config
+spec:
+ plugins:
+ - name: authz-keycloak
+ config:
+ # highlight-start
+ lazy_load_paths: false
+ discovery: http://keycloak.aic.svc.cluster.local:8080/realms/quickstart-realm/.well-known/uma2-configuration
+ permissions:
+ - "httpbin-anything#access"
+ client_id: apisix-quickstart-client
+ # highlight-end
+---
+apiVersion: gateway.networking.k8s.io/v1
+kind: HTTPRoute
+metadata:
+ namespace: aic
+ name: authz-keycloak-route
+spec:
+ parentRefs:
+ - name: apisix
+ rules:
+ - matches:
+ - path:
+ type: Exact
+ value: /anything
+ filters:
+ - type: ExtensionRef
+ extensionRef:
+ group: apisix.apache.org
+ kind: PluginConfig
+ name: authz-keycloak-plugin-config
+ backendRefs:
+ - name: httpbin-external-domain
+ port: 80
+```
-
+将配置应用到集群:
-输入作用域名称 `httpbin-access` 并点击 **Save**:
+```shell
+kubectl apply -f authz-keycloak-ic.yaml
+```
-
+
+
+
+
+```yaml title="authz-keycloak-ic.yaml"
+apiVersion: apisix.apache.org/v2
+kind: ApisixUpstream
+metadata:
+ namespace: aic
+ name: httpbin-external-domain
+spec:
+ ingressClassName: apisix
+ externalNodes:
+ - type: Domain
+ name: httpbin.org
+---
+apiVersion: apisix.apache.org/v2
+kind: ApisixPluginConfig
+metadata:
+ namespace: aic
+ name: authz-keycloak-plugin-config
+spec:
+ ingressClassName: apisix
+ plugins:
+ - name: authz-keycloak
+ enable: true
+ config:
+ # highlight-start
+ lazy_load_paths: false
+ discovery: http://keycloak.aic.svc.cluster.local:8080/realms/quickstart-realm/.well-known/uma2-configuration
+ permissions:
+ - "httpbin-anything#access"
+ client_id: apisix-quickstart-client
+ # highlight-end
+---
+apiVersion: apisix.apache.org/v2
+kind: ApisixRoute
+metadata:
+ namespace: aic
+ name: authz-keycloak-route
+spec:
+ ingressClassName: apisix
+ http:
+ - name: authz-keycloak-route
+ match:
+ paths:
+ - /anything
+ methods:
+ - GET
+ upstreams:
+ - name: httpbin-external-domain
+ plugin_config_name: authz-keycloak-plugin-config
+```
+
+将配置应用到集群:
-#### 在 Keycloak 中创建策略
+```shell
+kubectl apply -f authz-keycloak-ic.yaml
+```
-前往 **Clients** > **`apisix-quickstart-client`** > **Authorization** > **Policies** > **Create Policies**,在下拉菜单中选择 **Client Scope** 打开 **Add Client Scope Policy** 页面:
+
-
+
-为客户端作用域 `httpbin-access` 输入策略名称 `access-client-scope-policy`,勾选 **Required**,然后点击 **Save**:
+
-
+
-#### 在 Keycloak 中创建权限
+- 将 `lazy_load_paths` 设置为 `false`。
-前往 **Clients** > **`apisix-quickstart-client`** > **Authorization** > **Permissions** > **Create Permissions**,在下拉菜单中选择 **Scope-Based** 打开 **Add Scope Permission** 页面:
+- 将 `discovery` 设置为 Keycloak 授权服务的发现文档端点。
+
+- 将 `permissions` 设置为资源 `httpbin-anything` 和作用域 `access`。
-
+向路由发送请求:
-输入权限名称 `access-scope-perm`,选择 `access` 作用域,应用策略 `access-client-scope-policy`,然后点击 **Save**:
+```shell
+curl "http://127.0.0.1:9080/anything" -H "Authorization: Bearer $ACCESS_TOKEN"
+```
-
+你应该会看到 `HTTP/1.1 200 OK` 响应。
-#### 分配客户端作用域
+如果你移除 `apisix-quickstart-client` 的客户端作用域 `httpbin-access`,访问该资源时将收到 `401 Unauthorized` 响应。
-前往 **Clients** > **`apisix-quickstart-client`** > **Client Scopes**,将 `httpbin-access` 添加到默认客户端作用域:
+### 在自定义令牌端点使用密码授权类型生成令牌
-
+以下示例演示如何配置 `authz-keycloak` 在自定义端点使用密码授权类型请求令牌。
-#### 配置 APISIX
+
-按如下方式创建路由 `authz-keycloak-route`:
+
+
+按如下方式创建路由 `authz-keycloak-route`:
```shell
curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \
-H "X-API-KEY: ${admin_key}" \
-d '{
"id": "authz-keycloak-route",
- "uri": "/anything",
+ "uri": "/api/*",
"plugins": {
+ # highlight-start
"authz-keycloak": {
- "lazy_load_paths": false,
- "discovery": "http://'"$KEYCLOAK_IP"':8080/realms/quickstart-realm/.well-known/uma2-configuration",
- "permissions": ["httpbin-anything#access"],
- "client_id": "apisix-quickstart-client"
+ "lazy_load_paths": true,
+ "resource_registration_endpoint": "'"$KEYCLOAK_URL"'/realms/quickstart-realm/authz/protection/resource_set",
+ "client_id": "'"$OIDC_CLIENT_ID"'",
+ "client_secret": "'"$OIDC_CLIENT_SECRET"'",
+ "token_endpoint": "'"$KEYCLOAK_URL"'/realms/quickstart-realm/protocol/openid-connect/token",
+ "password_grant_token_generation_incoming_uri": "/api/token"
}
+ # highlight-end
},
"upstream": {
"type": "roundrobin",
"nodes": {
- "httpbin.org": 1
+ "httpbin.org:80": 1
}
}
}'
```
-- 将 `lazy_load_paths` 设置为 `false`。
-- 将 `discovery` 设置为 Keycloak 授权服务的发现文档端点。
-- 将 `permissions` 设置为资源 `httpbin-anything` 和作用域 `access`。
+
+
+
+
+在 ADC 中配置 `authz-keycloak` 创建路由:
+
+```yaml title="adc.yaml"
+services:
+ - name: authz-keycloak-service
+ routes:
+ - name: authz-keycloak-route
+ uris:
+ - /api/*
+ plugins:
+ # highlight-start
+ authz-keycloak:
+ lazy_load_paths: true
+ resource_registration_endpoint: ${KEYCLOAK_URL}/realms/quickstart-realm/authz/protection/resource_set
+ client_id: ${OIDC_CLIENT_ID}
+ client_secret: ${OIDC_CLIENT_SECRET}
+ token_endpoint: ${KEYCLOAK_URL}/realms/quickstart-realm/protocol/openid-connect/token
+ password_grant_token_generation_incoming_uri: /api/token
+ # highlight-end
+ upstream:
+ type: roundrobin
+ nodes:
+ - host: httpbin.org
+ port: 80
+ weight: 1
+```
-向路由发送请求:
+将配置同步到网关:
```shell
-curl "http://127.0.0.1:9080/anything" -H "Authorization: Bearer $ACCESS_TOKEN"
+adc sync -f adc.yaml
```
-你应该会看到类似如下的 `HTTP/1.1 200 OK` 响应:
+
-```json
-{
- "args": {},
- "data": "",
- "files": {},
- "form": {},
- "headers": {
- "Accept": "*/*",
- "Authorization": "Bearer eyJhbGciOiJSU...",
- ...
- },
- "json": null,
- "method": "GET",
- "origin": "127.0.0.1, 108.180.51.111",
- "url": "http://127.0.0.1/anything"
-}
-```
+
-若你移除 `apisix-quickstart-client` 的客户端作用域 `httpbin-access`,访问该资源时将收到 `401 Unauthorized` 响应。
+在路由上配置 `authz-keycloak`:
-### 在自定义令牌端点使用密码授权类型生成令牌
+
-以下示例演示如何在自定义端点使用密码授权类型生成令牌。
+
-#### 在 Keycloak 中创建用户
+```yaml title="authz-keycloak-ic.yaml"
+apiVersion: v1
+kind: Service
+metadata:
+ namespace: aic
+ name: httpbin-external-domain
+spec:
+ type: ExternalName
+ externalName: httpbin.org
+---
+apiVersion: apisix.apache.org/v1alpha1
+kind: PluginConfig
+metadata:
+ namespace: aic
+ name: authz-keycloak-plugin-config
+spec:
+ plugins:
+ - name: authz-keycloak
+ config:
+ lazy_load_paths: true
+ resource_registration_endpoint: http://keycloak.aic.svc.cluster.local:8080/realms/quickstart-realm/authz/protection/resource_set
+ client_id: apisix-quickstart-client
+ client_secret: replace-with-your-client-secret
+ # highlight-start
+ token_endpoint: http://keycloak.aic.svc.cluster.local:8080/realms/quickstart-realm/protocol/openid-connect/token
+ password_grant_token_generation_incoming_uri: /api/token
+ # highlight-end
+---
+apiVersion: gateway.networking.k8s.io/v1
+kind: HTTPRoute
+metadata:
+ namespace: aic
+ name: authz-keycloak-route
+spec:
+ parentRefs:
+ - name: apisix
+ rules:
+ - matches:
+ - path:
+ type: PathPrefix
+ value: /api/
+ filters:
+ - type: ExtensionRef
+ extensionRef:
+ group: apisix.apache.org
+ kind: PluginConfig
+ name: authz-keycloak-plugin-config
+ backendRefs:
+ - name: httpbin-external-domain
+ port: 80
+```
-若要使用密码授权类型,需要先创建一个用户。
+将配置应用到集群:
-前往 **Users** > **Add user** 并点击 **Add user**:
+```shell
+kubectl apply -f authz-keycloak-ic.yaml
+```
-
+
+
+
+
+```yaml title="authz-keycloak-ic.yaml"
+apiVersion: apisix.apache.org/v2
+kind: ApisixUpstream
+metadata:
+ namespace: aic
+ name: httpbin-external-domain
+spec:
+ ingressClassName: apisix
+ externalNodes:
+ - type: Domain
+ name: httpbin.org
+---
+apiVersion: apisix.apache.org/v2
+kind: ApisixPluginConfig
+metadata:
+ namespace: aic
+ name: authz-keycloak-plugin-config
+spec:
+ ingressClassName: apisix
+ plugins:
+ - name: authz-keycloak
+ enable: true
+ config:
+ lazy_load_paths: true
+ resource_registration_endpoint: http://keycloak.aic.svc.cluster.local:8080/realms/quickstart-realm/authz/protection/resource_set
+ client_id: apisix-quickstart-client
+ client_secret: replace-with-your-client-secret
+ # highlight-start
+ token_endpoint: http://keycloak.aic.svc.cluster.local:8080/realms/quickstart-realm/protocol/openid-connect/token
+ password_grant_token_generation_incoming_uri: /api/token
+ # highlight-end
+---
+apiVersion: apisix.apache.org/v2
+kind: ApisixRoute
+metadata:
+ namespace: aic
+ name: authz-keycloak-route
+spec:
+ ingressClassName: apisix
+ http:
+ - name: authz-keycloak-route
+ match:
+ paths:
+ - /api/*
+ methods:
+ - GET
+ - POST
+ upstreams:
+ - name: httpbin-external-domain
+ plugin_config_name: authz-keycloak-plugin-config
+```
-将 **Username** 填写为 `quickstart-user` 并点击 **Save**:
+将配置应用到集群:
-
+```shell
+kubectl apply -f authz-keycloak-ic.yaml
+```
-点击 **Credentials**,将 **Password** 设置为 `quickstart-user-pass`。将 **Temporary** 切换为 `OFF`,这样首次登录时无需修改密码:
+
-
+
-#### 配置 APISIX
+
-按如下方式创建路由 `authz-keycloak-route`:
+
-```shell
-curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \
- -H "X-API-KEY: ${admin_key}" \
- -d '{
- "id": "authz-keycloak-route",
- "uri": "/api/*",
- "plugins": {
- "authz-keycloak": {
- "lazy_load_paths": true,
- "resource_registration_endpoint": "http://'"$KEYCLOAK_IP"':8080/realms/quickstart-realm/authz/protection/resource_set",
- "client_id": "'"$OIDC_CLIENT_ID"'",
- "client_secret": "'"$OIDC_CLIENT_SECRET"'",
- "token_endpoint": "http://'"$KEYCLOAK_IP"':8080/realms/quickstart-realm/protocol/openid-connect/token",
- "password_grant_token_generation_incoming_uri": "/api/token"
- }
- },
- "upstream": {
- "type": "roundrobin",
- "nodes": {
- "httpbin.org": 1
- }
- }
- }'
-```
+- 将 `token_endpoint` 设置为 Keycloak 令牌端点。当未提供发现文档时必填。
-- 将 `token_endpoint` 设置为 Keycloak 的令牌端点。当未提供发现文档时,此项必填。
-- 将 `password_grant_token_generation_incoming_uri` 设置为用户获取令牌的自定义 URI 路径。
+- 将 `password_grant_token_generation_incoming_uri` 设置为用户可获取令牌的自定义 URI 路径。
-向已配置的令牌端点发送请求。注意请求应使用 POST 方法,并将 `Content-Type` 设置为 `application/x-www-form-urlencoded`:
+向已配置的令牌端点发送请求。请求应使用 POST 方法,并将 `Content-Type` 设置为 `application/x-www-form-urlencoded`:
```shell
OIDC_USER=quickstart-user
@@ -499,6 +1110,12 @@ curl "http://127.0.0.1:9080/api/token" -X POST \
你应该会看到包含访问令牌的 JSON 响应,类似如下:
-```text
-{"access_token":"eyJhbGciOiJSUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICJ6U3FFaXN6VlpuYi1sRWMzZkp0UHNpU1ZZcGs4RGN3dXI1Mkx5V05aQTR3In0...","expires_in":300,"refresh_expires_in":1800,"refresh_token":"eyJhbGciOiJIUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICI0YjFiNTQ3Yi0zZmZjLTQ5YzQtYjE2Ni03YjdhNzIxMjk1ODcifQ...","token_type":"Bearer","not-before-policy":0,"session_state":"b16b262e-1056-4515-a455-f25e077ccb76","scope":"profile email"}
+```json
+{
+ "access_token": "eyJhbGciOiJSUzI1NiIsInR5cCIgOiAiSldUIi...",
+ "expires_in": 300,
+ "refresh_expires_in": 1800,
+ "token_type": "Bearer",
+ "scope": "profile email httpbin-access"
+}
```
diff --git a/docs/zh/latest/plugins/aws-lambda.md b/docs/zh/latest/plugins/aws-lambda.md
index 3514696f2294..433b623f3d4b 100644
--- a/docs/zh/latest/plugins/aws-lambda.md
+++ b/docs/zh/latest/plugins/aws-lambda.md
@@ -48,7 +48,7 @@ description: aws-lambda 插件支持 APISIX 与 AWS Lambda 和 Amazon API Gatewa
| authorization.iam.accesskey | string | 否 | | | IAM 用户访问密钥。当配置 `authorization.iam` 时必填。 |
| authorization.iam.secretkey | string | 否 | | | IAM 用户秘密访问密钥。当配置 `authorization.iam` 时必填。 |
| authorization.iam.aws_region | string | 否 | "us-east-1" | | 发送请求的 AWS 区域。 |
-| authorization.iam.service | string | 否 | "execute-api" | | 接收请求的服务。与 AWS API Gateway 集成时设置为 `execute-api`,直接与 Lambda 函数集成时设置为 `lambda`。 |
+| authorization.iam.service | string | 否 | "execute-api" | | 接收请求的服务。与 AWS API Gateway 集成时设置为 `execute-api`,直接与 Lambda 函数集成时设置为 `lambda`。 |
| timeout | integer | 否 | 3000 | [100,...] | 代理请求超时时间,单位为毫秒。 |
| ssl_verify | boolean | 否 | true | | 若为 true,执行 SSL 验证。 |
| keepalive | boolean | 否 | true | | 若为 true,保持连接以便复用。 |
@@ -59,7 +59,7 @@ description: aws-lambda 插件支持 APISIX 与 AWS Lambda 和 Amazon API Gatewa
以下示例演示如何针对不同场景配置 `aws-lambda` 插件。
-在操作前,请先登录 AWS 控制台并创建一个 Lambda 函数(使用任意运行时即可)。默认情况下,该函数被调用后应返回 `Hello from Lambda!`。
+在操作前,请先登录 AWS 控制台并创建一个 Lambda 函数 (使用任意运行时即可)。默认情况下,该函数被调用后应返回 `Hello from Lambda!`。
:::note
@@ -77,7 +77,7 @@ admin_key=$(yq '.deployment.admin.admin_key[0].key' conf/config.yaml | sed 's/"/
对于 IAM 访问密钥,请前往 **AWS Identity and Access Management (IAM)** 并选择要用于集成的用户。
-在 **安全凭证** 标签页中,选择 **创建访问密钥**:
+在 **安全凭证** 标签页中,选择 **创建访问密钥**:

@@ -89,26 +89,37 @@ admin_key=$(yq '.deployment.admin.admin_key[0].key' conf/config.yaml | sed 's/"/

-要创建 Lambda 函数 URL,请前往 Lambda 函数的 **配置** 标签页,在 **函数 URL** 下创建函数 URL:
+要创建 Lambda 函数 URL,请前往 Lambda 函数的 **配置** 标签页,在 **函数 URL** 下创建函数 URL:

最后,在 APISIX 中使用函数 URL 和 IAM 访问密钥创建路由:
+
+
+
+
```shell
curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \
-H "X-API-KEY: ${admin_key}" \
-d '{
- "id": "aws-lambda-iam-route",
+ "id": "aws-lambda-route",
"uri": "/aws-lambda",
"plugins": {
"aws-lambda": {
- "function_uri": "https:///",
+ "function_uri": "https://your-lambda-function-url.lambda-url.us-west-2.on.aws/",
"authorization": {
"iam": {
- "accesskey": "",
- "secretkey": "",
- "aws_region": "",
+ "accesskey": "YOUR_IAM_ACCESS_KEY",
+ "secretkey": "YOUR_IAM_SECRET_KEY",
+ "aws_region": "us-west-2",
"service": "lambda"
}
},
@@ -118,7 +129,142 @@ curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \
}'
```
-请将 `function_uri`、`accesskey`、`secretkey` 和 `aws_region` 替换为你的实际值。直接与 Lambda 函数集成时,将 `service` 设置为 `lambda`。
+- 替换为你的 Lambda 函数 URL
+
+- 替换为你的 IAM 访问密钥
+
+- 替换为你的 IAM 秘密访问密钥
+
+- 替换为 Lambda 函数所在的 AWS 区域
+
+- 直接与 Lambda 函数集成时设置为 `lambda`
+
+
+
+
+
+```yaml title="adc.yaml"
+services:
+ - name: aws-lambda-service
+ routes:
+ - name: aws-lambda-route
+ uris:
+ - /aws-lambda
+ plugins:
+ aws-lambda:
+ function_uri: https://your-lambda-function-url.lambda-url.us-west-2.on.aws/
+ authorization:
+ iam:
+ accesskey: YOUR_IAM_ACCESS_KEY
+ secretkey: YOUR_IAM_SECRET_KEY
+ aws_region: us-west-2
+ service: lambda
+```
+
+同步配置到网关:
+
+```shell
+adc sync -f adc.yaml
+```
+
+
+
+
+
+
+
+
+
+```yaml title="aws-lambda-ic.yaml"
+apiVersion: apisix.apache.org/v1alpha1
+kind: PluginConfig
+metadata:
+ namespace: aic
+ name: aws-lambda-plugin-config
+spec:
+ plugins:
+ - name: aws-lambda
+ config:
+ function_uri: https://your-lambda-function-url.lambda-url.us-west-2.on.aws/
+ authorization:
+ iam:
+ accesskey: YOUR_IAM_ACCESS_KEY
+ secretkey: YOUR_IAM_SECRET_KEY
+ aws_region: us-west-2
+ service: lambda
+ ssl_verify: false
+---
+apiVersion: gateway.networking.k8s.io/v1
+kind: HTTPRoute
+metadata:
+ namespace: aic
+ name: aws-lambda-route
+spec:
+ parentRefs:
+ - name: apisix
+ rules:
+ - matches:
+ - path:
+ type: Exact
+ value: /aws-lambda
+ filters:
+ - type: ExtensionRef
+ extensionRef:
+ group: apisix.apache.org
+ kind: PluginConfig
+ name: aws-lambda-plugin-config
+```
+
+
+
+
+
+```yaml title="aws-lambda-ic.yaml"
+apiVersion: apisix.apache.org/v2
+kind: ApisixRoute
+metadata:
+ namespace: aic
+ name: aws-lambda-route
+spec:
+ ingressClassName: apisix
+ http:
+ - name: aws-lambda-route
+ match:
+ paths:
+ - /aws-lambda
+ plugins:
+ - name: aws-lambda
+ enable: true
+ config:
+ function_uri: https://your-lambda-function-url.lambda-url.us-west-2.on.aws/
+ authorization:
+ iam:
+ accesskey: YOUR_IAM_ACCESS_KEY
+ secretkey: YOUR_IAM_SECRET_KEY
+ aws_region: us-west-2
+ service: lambda
+ ssl_verify: false
+```
+
+
+
+
+
+应用配置:
+
+```shell
+kubectl apply -f aws-lambda-ic.yaml
+```
+
+
+
+
向路由发送请求:
@@ -136,17 +282,24 @@ curl -i "http://127.0.0.1:9080/aws-lambda"
以下示例演示如何将 APISIX 与 Amazon API Gateway 集成,并配置网关触发 Lambda 函数的执行。
-要将 API Gateway 配置为 Lambda 触发器,请前往 Lambda 函数并选择 **添加触发器**:
+要将 API Gateway 配置为 Lambda 触发器,请前往 Lambda 函数并选择 **添加触发器**:

选择 **API Gateway** 作为触发器,**REST API** 作为 API 类型,完成触发器添加:
-
+