Apply policies to APIs
Apply policies to control traffic behavior to your API products.
About
Generally, policies work the same for API products that you expose in the developer portal as described in the following docs:
- Conceptual information about policy enforcement
- Guides to route traffic such as with matchers and direct responses
- Policy guides for traffic management, security, and resiliency
Sometimes, the way you configure routing and policies differs when your API products are exposed with Portal. Portal provides extra flexibility to describe your API products that subsequent policies can take advantage of. Refer to the following guides for these special use cases:
- Usage plans that bundle rate limiting and external auth policies into flexible plans that you can use to protect and monetize your API products
- Path rewrites so you can decouple your app development from the paths of your API product exposed in the developer portal
- Custom metadata from your API products that you can add to requests and responses by using a header manipulation policy
Add custom metadata from API products to requests and responses
You can set custom metadata for your API products when you bundle your APIs in a route table. Then, the custom metadata is available for your developer portal and analytics. You can also add the custom metadata in subsequent requests and responses to your APIs by using a header manipulation policy.
Install or upgrade Portal. In the
istioInstallations
section of thegloo-platform
Helm release, enable the following access logs for the ingress gateway. The Monitor Portal analytics guide includes an example of enabling access logs in the ingress gateway. For more information about access logs, see Access logs.- The
user_id
,api_id
,api_product_id
,api_product_name
,usage_plan
, andcustom_metadata
come from the Portal metadata that you configure later in the route table for your API products. - The
"x_tenant_id": "%REQ(X-TENANT-ID)%"
is a request header that you append later in the header manipulation policy.
istioInstallations: controlPlane: enabled: true installations: - istioOperatorSpec: meshConfig: # Enable access logging to /dev/stdout accessLogFile: /dev/stdout # Encoding for the access log (TEXT or JSON). Default value is TEXT. accessLogEncoding: JSON # If empty, the default log format is used. # See the default log format at https://istio.io/latest/docs/tasks/observability/logs/access-log/#default-access-log-format # To change the format, see https://www.envoyproxy.io/docs/envoy/latest/configuration/observability/access_log/usage#format-rules accessLogFormat: | { "timestamp": "%START_TIME%", "server_name": "%REQ(:AUTHORITY)%", "response_duration": "%DURATION%", "request_command": "%REQ(:METHOD)%", "request_uri": "%REQ(X-ENVOY-ORIGINAL-PATH?:PATH)%", "request_protocol": "%PROTOCOL%", "status_code": "%RESPONSE_CODE%", "client_address": "%DOWNSTREAM_REMOTE_ADDRESS_WITHOUT_PORT%", "x_forwarded_for": "%REQ(X-FORWARDED-FOR)%", "bytes_sent": "%BYTES_SENT%", "bytes_received": "%BYTES_RECEIVED%", "user_agent": "%REQ(USER-AGENT)%", "downstream_local_address": "%DOWNSTREAM_LOCAL_ADDRESS%", "requested_server_name": "%REQUESTED_SERVER_NAME%", "request_id": "%REQ(X-REQUEST-ID)%", "response_flags": "%RESPONSE_FLAGS%", "route_name": "%ROUTE_NAME%", "upstream_cluster": "%UPSTREAM_CLUSTER%", "upstream_host": "%UPSTREAM_HOST%", "upstream_local_address": "%UPSTREAM_LOCAL_ADDRESS%", "upstream_service_time": "%REQ(x-envoy-upstream-service-time)%", "upstream_transport_failure_reason": "%UPSTREAM_TRANSPORT_FAILURE_REASON%", "correlation_id": "%REQ(X-CORRELATION-ID)%", "user_id": "%DYNAMIC_METADATA(envoy.filters.http.ext_authz:userId)%", "api_id": "%DYNAMIC_METADATA(io.solo.gloo.apimanagement:api_id)%", "api_product_id": "%DYNAMIC_METADATA(io.solo.gloo.apimanagement:api_product_id)%", "api_product_name": "%DYNAMIC_METADATA(io.solo.gloo.apimanagement:api_product_name)%", "usage_plan": "%DYNAMIC_METADATA(envoy.filters.http.ext_authz:usagePlan)%", "custom_metadata": "%DYNAMIC_METADATA(io.solo.gloo.apimanagement.custom_metadata)%", "x_tenant_id": "%REQ(X-TENANT-ID)%" } revision: auto
- The
Bundle your APIs into API products by creating a route table. In the
portalMetadata
section of the route table, make sure to your custom metadata, such ascompatability
andtenant
in the following example.portalMetadata: apiProductId: "tracks" apiProductDisplayName: "Catstronauts Course Tracks" apiVersion: "v1" title: "Catstronauts REST API" description: "REST API for Catstronauts to retrieve data for tracks, authors and modules." termsOfService: "You must authenticate to use this API! And other Terms of Service." contact: "support@example.com" license: "License info, such as MIT" lifecycle: "Supported" customMetadata: compatibility: "None" tenantId: "tenant-us-east2"
Apply a header manipulation policy that appends the custom metadata from the API product to the request header. The policy gets the custom metadata from the
%DYNAMIC_METADATA%
in the access logs of the ingress gateway. For more information and other options such as removing request or response headers, see the Header manipulation guide or API reference docs. In the following example, the policy:- Appends the
tenantId
as a request header so that subsequent processing can take place based on the tenant ID. - Appends the
compatibility
as a response header so that the client can tell whether the API product is backwards compatible.
kubectl apply -f - << EOF apiVersion: trafficcontrol.policy.gloo.solo.io/v2 kind: HeaderManipulationPolicy metadata: name: portal-custom-metadata namespace: tracks spec: applyToRoutes: - route: labels: api: tracks config: appendRequestHeaders: x-tenant-id: "%DYNAMIC_METADATA(io.solo.gloo.apimanagement.custom_metadata:tenantId)%" appendResponseHeaders: x-compatibility: "%DYNAMIC_METADATA(io.solo.gloo.apimanagement.custom_metadata:compatibility)%" EOF
- Appends the
Get the external address of your ingress gateway. The steps vary depending on the type of load balancer that backs the ingress gateway.
export INGRESS_GW_ADDRESS=$(kubectl get svc -n gloo-mesh-gateways istio-ingressgateway -o jsonpath="{.status.loadBalancer.ingress[0]['hostname','ip']}") echo $INGRESS_GW_ADDRESS
Note: Depending on your environment, you might see
<pending>
instead of an external IP address. For example, if you are testing locally in kind or minikube, or if you have insufficient permissions in your cloud platform, you can instead port-forward the service port of the ingress gateway:kubectl -n gloo-mesh-gateways port-forward deploy/istio-ingressgateway-1-20 8081
Send a request to the Tracks app. Note that if you already included the Tracks app in a usage plan, you might need to authenticate such as with an API key.
curl -vik http://$INGRESS_GW_ADDRESS:80/trackapi/tracks -H "host: api.example.com:80"
In the response, note that the
x-compatibility
response header is returned.x-compatibility: None
Check the ingress gateway logs for the
x_tenant_id
request header that you appended to requests sent to the upstream service.kubectl logs -n gloo-mesh-gateways -l app=istio-ingressgateway
Example output:
{ "api_product_id": "tracks", "request_command": "GET", "api_product_name": "Catstronauts Course", "user_agent": "curl/8.1.2", "server_name": "api.example.com", "custom_metadata": { "tenantId": "tenant-us-east2", "compatibility": "None" }, "response_duration": 11, "x_forwarded_for": "10.0.11.54", "status_code": 200, "usage_plan": "gold", "user_id": "user1", "api_id": "tracks", "request_uri": "/trackapi/tracks", "x_tenant_id": "tenant-us-east2" ... }
Cleanup
You can optionally remove the resources that you set up as part of this guide.
kubectl delete HeaderManipulationPolicy portal-custom-metadata -n tracks