Using API Connect Vanity Endpoints running on K8S
This article describes one approach of using vanity API endpoints in API Connect v2018 or v10, specifically on K8S (because this was what I was working on). However, the concepts discussed here is applicable in other deployment, OpenShift or VMware deployment.
Before I continue, let us understand vanity endpoints in API Connect.
- What is vanity API endpoint?
- Why is it needed?
- How it is implemented?
From the API Connect documentation, an API has two endpoints
- The gateway endpoint at which the API is invoked.
- The endpoint that is visible to the consumer in the Developer Portal.
By default, these two endpoints are the same. E.g. if your provider organization is acme, your catalog is public, your API base path is /v1/app and the resource path is /customers; then the application will consume the API using this endpoint
https://apigateway/acme/public/v1/app/customers
However, in most cases, company would like their public APIs to be known differently. Using the example above, they would like to “hide” the provider organization and catalog in API endpoint.
https://apigateway/v1/app/customer
So, the way to do this in API Connect is using vanity API endpoint, which can be configured in the Catalog. In doing so, this vanity API endpoint will be published to the Developer Portal and is used by the application to invoke the API.
However, what is not clear in the documentation is you have to handle the traffic routing so that when the application invokes the published vanity API endpoint, it gets routed to the actual API invocation endpoint of the gateway services.
There are various ways to implement this — and this article describe how this can be done using ingress in K8S. The following is an example of the ingress object.
apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
annotations:
ingress.kubernetes.io/proxy-read-timeout: "240"
ingress.kubernetes.io/proxy-send-timeout: "240"
ingress.kubernetes.io/ssl-redirect: "true"
ingress.kubernetes.io/rewrite-target: /${porg_name}/${catalog_name}/\$1
ingress.kubernetes.io/ssl-prefer-server-ciphers: "true"
ingress.kubernetes.io/ssl-ciphers: "ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES128-GCM-SHA256"
kubernetes.io/ingress.class: nginx
meta.helm.sh/release-name: ${release_name}
meta.helm.sh/release-namespace: ${namespace}
nginx.ingress.kubernetes.io/backend-protocol: HTTPS
nginx.ingress.kubernetes.io/proxy-read-timeout: "240"
nginx.ingress.kubernetes.io/proxy-send-timeout: "240"
nginx.ingress.kubernetes.io/proxy-ssl-server-name: "on"
nginx.ingress.kubernetes.io/ssl-redirect: "true"
nginx.ingress.kubernetes.io/rewrite-target: /${porg_name}/${catalog_name}/\$1
nginx.ingress.kubernetes.io/ssl-prefer-server-ciphers: "true"
nginx.ingress.kubernetes.io/ssl-ciphers: "ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES128-GCM-SHA256"
name: ${name}
namespace: ${namespace}
spec:
rules:
- host: ${endpoint_name}
http:
paths:
- backend:
serviceName: ${release_name}-dynamic-gateway-service-ingress
servicePort: 9443
path: /(.*)
tls:
- hosts:
- ${endpoint_name}
secretName: ${secret_name}
EOF
The above ingress does the following
- Terminates the TLS using the server certificate and key defined a secret. In this case, the secret contains the tls.crt and tls.key.
- Defines a list of preferred ciphers
- Rewrite any incoming URI path to /${porg_name}/${catalog_name}/${incoming_uri_path}
Another variation of this configuration is to terminate MTLS traffic. This ingress object shows how this can be done.
apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
annotations:
ingress.kubernetes.io/proxy-read-timeout: "240"
ingress.kubernetes.io/proxy-send-timeout: "240"
ingress.kubernetes.io/ssl-redirect: "true"
ingress.kubernetes.io/rewrite-target: /${porg_name}/${catalog_name}/\$1
ingress.kubernetes.io/ssl-prefer-server-ciphers: "true"
ingress.kubernetes.io/ssl-ciphers: "ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES128-GCM-SHA256"
ingress.kubernetes.io/auth-tls-verify-client: "on"
ingress.kubernetes.io/auth-tls-secret: "${namespace}/${secret_name}"
ingress.kubernetes.io/auth-tls-verify-depth: "2"
ingress.kubernetes.io/auth-tls-pass-certificate-to-upstream: "true"
kubernetes.io/ingress.class: nginx
meta.helm.sh/release-name: ${release_name}
meta.helm.sh/release-namespace: ${namespace}
nginx.ingress.kubernetes.io/backend-protocol: HTTPS
nginx.ingress.kubernetes.io/proxy-read-timeout: "240"
nginx.ingress.kubernetes.io/proxy-send-timeout: "240"
nginx.ingress.kubernetes.io/proxy-ssl-server-name: "on"
nginx.ingress.kubernetes.io/ssl-redirect: "true"
nginx.ingress.kubernetes.io/rewrite-target: /${porg_name}/${catalog_name}/\$1
nginx.ingress.kubernetes.io/ssl-prefer-server-ciphers: "true"
nginx.ingress.kubernetes.io/ssl-ciphers: "ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES128-GCM-SHA256"
nginx.ingress.kubernetes.io/auth-tls-verify-client: "on"
nginx.ingress.kubernetes.io/auth-tls-secret: "${namespace}/${secret_name}"
nginx.ingress.kubernetes.io/auth-tls-verify-depth: "2"
nginx.ingress.kubernetes.io/auth-tls-pass-certificate-to-upstream: "true"
name: ${name}
namespace: ${namespace}
spec:
rules:
- host: ${endpoint_name}
http:
paths:
- backend:
serviceName: ${release_name}-dynamic-gateway-service-ingress
servicePort: 9443
path: /(.*)
tls:
- hosts:
- ${endpoint_name}
secretName: ${secret_name}
The above ingress does the following
- Terminates the MTLS using the server certificate and key defined a secret, and contains the signer certificate in truststore. In this case, the secret contains the tls.crt, tls.key and ca.crt.
- Defines a list of preferred ciphers
- Pass on the client certificate to downstream application as header
ssl-client-cert
in URL encoded format. So, if downstream application wants to use the certification, it needs to do URL decode. - Rewrite any incoming URI path to /${porg_name}/${catalog_name}/${incoming_uri_path}
- Finally, it is important to tune the verify depth parameter you can use the signer certificate to do client certificate verification.
One benefit of this approach is you can isolate TLS-only APIs from the MTLS-only APIs while sharing the same gateway service. You can publish TLS-only APIs to a TLS-only catalog and MTLS-only APIs to a MTLS-only catalog.
Let me know what you think in the comment section.
References
https://www.ibm.com/docs/en/api-connect/10.0.1.x?topic=catalogs-creating-configuring
Disclaimer:
All opinions expressed here are very much my own and not of IBM. All codes/scripts/artifacts are provided as-is with no support unless otherwise stated.