Deploy API gateway listeners in Kubernetes
This topic describes how to deploy Consul API gateway listeners to Kubernetes-orchestrated environments. If you want to implement API gateway listeners on VMs, refer to Deploy API gateway listeners to virtual machines.
Overview
API gateways have one or more listeners that serve as ingress points for requests to services in a Consul service mesh. Create an API gateway configuration and define listeners that expose ports on the endpoint for ingress. Apply the configuration to direct Kubernetes to start API gateway services.
Routes
After deploying the gateway, attach HTTP or TCP routes to listeners defined in the gateway to control how requests route to services in the network.
Intentions
Configure Consul intentions to allow or prevent traffic between gateway listeners and services in the mesh. Refer to Service intentions for additional information.
Requirements
- Verify that your environment meets the requirements specified in Technical specifications for Kubernetes.
- Verify that the Consul API gateway CRDs were applied. Refer to Installation for details.
- If your Kubernetes-orchestrated network runs on OpenShift, verify that OpenShift is enabled for your Consul installation. Refer to OpenShift requirements for additional information.
Define the API gateways and listeners
Review When to choose the Standard API gateway or Consul API gateway for guidance on selecting the right gateway type.
Create an API gateway values file that defines the gateway and listeners.
- Specify the following fields:
apiVersion: Specifies the Kubernetes gateway API version. Must begateway.networking.k8s.io/v1for Standard API gateway orconsul.hashicorp.com/v1beta1for Consul API gateway. For more details on API version refer Consul API gateway reference objects.kind: Specifies the type of configuration entry to implement. This must beGateway.metadata.name: Specify a name for the gateway configuration. The name is metadata that you can use to reference the configuration when performing Consul operations.metadata.label.component: Specify the component name asapi-gatewayfor Standard API gateway orapi-gateway-consulfor Consul API gateway.spec.gatewayClassName: Specify the name of agatewayClassobject. Gateway classes are template-like resources in Kubernetes for instantiating gateway services. Specifyconsulfor Standard API gateway orconsul-customfor Consul API gateway to use the default gateway class shipped with Consul. Refer to the GatewayClass configuration reference for additional information.spec.listeners: Specify a list of listener configurations. Each listener is map containing the following fields:port: Specifies the port that the listener receives traffic on.name: Specifies a unique name for the listener.protocol: You can set eithertcporhttp- To serve HTTP/2 or gRPC traffic, add a listener protocol annotation to the
Gateway. Refer to HTTP/2 and gRPC listeners for additional information. allowedRoutes.namespaces: Contains configurations for determining which namespaces are allowed to attach a route to the listener.
- Configure any additional fields necessary for your use case, such as the namespace or admin partition. Refer to the API gateway configuration entry reference for additional information.
- Save the configuration.
In the following example, the API gateway specifies an HTTP listener on port 80:
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
name: my-gateway
namespace: consul
labels:
component: api-gateway
spec:
gatewayClassName: consul
listeners:
- protocol: HTTP
port: 80
name: http
allowedRoutes:
namespaces:
from: "All"
Kubernetes SDS implementation
This section reflects the current API gateway SDS behavior in consul-k8s:
- Listener-level SDS from Gateway annotations and listener
tls.options - Backend-level SDS override from
RouteTLSSDSFilter
Listener SDS source of truth
Listener TLS SDS is resolved from these sources:
- Listener
tls.options(per-listener override) - Gateway
metadata.annotations(gateway-wide defaults)
Supported keys:
api-gateway.consul.hashicorp.com/tls_sds_cluster_nameapi-gateway.consul.hashicorp.com/tls_sds_cert_resource
SDS resolution behavior
For each listener:
- If both SDS keys are set in listener
tls.options, listener values are used. - Otherwise, Gateway annotations are used as defaults.
- If neither source sets SDS, listener SDS is unset.
Listener validation behavior
- If only one SDS field is provided, listener validation fails.
- SDS and
tls.certificateRefsare validated independently and can both be configured on the same listener.
Translation to Consul entries
- Resolved listener SDS is mapped to
APIGatewayListener.TLS.SDS. - Listener certificate refs are mapped to
APIGatewayListener.TLS.Certificates. - When both are configured, translated listener TLS includes both SDS and Certificates.
Example: Gateway listener SDS via annotations and listener tls.options
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
name: sds-gateway
namespace: default
annotations:
api-gateway.consul.hashicorp.com/tls_sds_cluster_name: sds-cluster
api-gateway.consul.hashicorp.com/tls_sds_cert_resource: wildcard.ingress.consul
labels:
component: api-gateway
spec:
gatewayClassName: consul
listeners:
- name: https
protocol: HTTPS
port: 8443
hostname: a.example.test
tls:
mode: Terminate
options:
api-gateway.consul.hashicorp.com/tls_sds_cluster_name: sds-cluster
api-gateway.consul.hashicorp.com/tls_sds_cert_resource: wil.ingr.consul
TLS SDS precedence
Effective certificate selection precedence is:
- Route service override
TLS.SDS - Listener
TLS.SDS - Gateway-level
TLS.SDS
Route service SDS overrides (Kubernetes)
RouteTLSSDSFilter remains the backend-level SDS override mechanism for HTTP routes.
- Kind:
RouteTLSSDSFilter - Spec fields:
spec.sds.clusterName(optional when inherited from listener/global SDS)spec.sds.certResource(required)
Inheritance behavior:
- If
spec.sds.clusterNameis omitted, it is accepted when the route resolves exactly one listener-level SDS cluster from parentRefs. - If inherited cluster resolution is missing or ambiguous, validation rejects the filter.
Usage pattern:
- Attach from
HTTPRoute.rules[].backendRefs[].filters[]withtype: ExtensionRef. - Use
extensionRef.group: consul.hashicorp.com. - Use
extensionRef.kind: RouteTLSSDSFilter. - Use
extensionRef.name: <filter-name>.
Validation and translation:
- Translation maps this to
HTTPService.TLS.SDS. - Missing filter object, wrong type, missing/empty
spec.sds.certResource, or missing/ambiguous inheritedclusterNameare rejected. - Rule-level placement (
HTTPRoute.rules[].filters[]) is invalid for this override; backendRef filter placement is required.
Example: HTTPRoute backendRef override using RouteTLSSDSFilter
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: api-route-override
namespace: default
spec:
parentRefs:
- name: sds-gateway
sectionName: https
hostnames:
- b.example.test
rules:
- backendRefs:
- name: svc-b
port: 5678
filters:
- type: ExtensionRef
extensionRef:
group: consul.hashicorp.com
kind: RouteTLSSDSFilter
name: route-sds-override-http
---
apiVersion: consul.hashicorp.com/v1alpha1
kind: RouteTLSSDSFilter
metadata:
name: route-sds-override-http
namespace: default
spec:
sds:
clusterName: sds-cluster-2
certResource: foo.example.com
Troubleshooting checklist
- Confirm both SDS keys are set together (listener
tls.optionsor Gateway annotation defaults). - If
tls.certificateRefsare used, verify referenced Secrets exist and are referenceable. - For backend overrides, ensure
RouteTLSSDSFilteris attached underbackendRefs[].filters[]. - Ensure
RouteTLSSDSFilter.spec.sds.certResourceis non-empty andclusterNameis set or inherited. - Verify rendered config contains
APIGatewayListener.TLS.SDS,APIGatewayListener.TLS.Certificates, and/orHTTPService.TLS.SDSas applicable. - For local probes on non-8443 ports (for example
18443), preserve hostname routing withHostand--resolve.
Refer to Define API gateway routes in Kubernetes, RouteTLSSDSFilter reference (Kubernetes), and Route Resource Configuration for route and filter configuration details.
Deploy the API gateway and listeners
Apply the configuration to your cluster using the kubectl command. The following command applies the configuration to the consul namespace:
$ kubectl apply -f my-gateway.yaml -n consul
Terminate HTTPS with Consul-managed certificates
You can configure the API gateway to terminate downstream HTTPS with its Consul service mesh leaf certificate instead of a certificate that you store in a Kubernetes Secret. Consul issues and rotates the leaf certificate automatically, so you do not need to create a Secret or configure tls.certificateRefs on the listener.
The leaf certificate is signed by the Consul service mesh certificate authority (CA) and includes wildcard DNS subject alternative names (SANs), such as *.api-gateway.consul and *.api-gateway.<datacenter>.consul. Consul also registers each service that a bound route targets in Consul DNS at <service>.api-gateway.<domain>. As a result, clients that trust the Consul CA root certificate can reach the service over verified HTTPS without additional certificate configuration. Refer to Certificate DNS names for the complete list of SANs, including the namespace-scoped SANs for Consul Enterprise.
Requirements for Consul-managed certificates
- Consul v2.1 or later
- Consul Helm chart v2.1.0 or later
- To resolve
<service>.api-gateway.<domain>names from pods, enable Consul DNS with thedns.enabledHelm value and configure your cluster DNS to forward theconsuldomain to Consul. Refer to Resolve Consul DNS requests in Kubernetes for instructions.
Enable Consul-managed TLS on the gateway
Add the consul.hashicorp.com/tls-enabled: "true" annotation to the Gateway resource and configure an HTTPS listener without tls.certificateRefs. Consul on Kubernetes translates the annotation into the TLS.Enabled field of the API gateway configuration entry. The annotation is supported on both the Standard API gateway (gateway.networking.k8s.io/v1) and the Consul API gateway (consul.hashicorp.com/v1beta1).
The following example configures an HTTPS listener on port 8443:
my-gateway.yaml
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
name: my-gateway
annotations:
consul.hashicorp.com/tls-enabled: "true"
labels:
component: api-gateway
spec:
gatewayClassName: consul
listeners:
- name: https
protocol: HTTPS
port: 8443
tls:
mode: Terminate
options:
api-gateway.consul.hashicorp.com/tls_min_version: "TLS_AUTO"
allowedRoutes:
namespaces:
from: "All"
Kubernetes Gateway API v1.4 and later CRDs require a Terminate listener to set either tls.certificateRefs or tls.options. When you omit tls.certificateRefs, set a tls.options key, such as api-gateway.consul.hashicorp.com/tls_min_version: "TLS_AUTO", to satisfy the validation rule. The TLS_AUTO value does not change the gateway's TLS behavior.
Bind an HTTPRoute to the listener and create service intentions that allow traffic from the gateway to the destination service. The following example routes traffic to the static-server service:
my-route.yaml
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: my-route
spec:
parentRefs:
- name: my-gateway
rules:
- backendRefs:
- name: static-server
port: 80
Apply the configurations to your cluster:
$ kubectl apply --filename my-gateway.yaml --filename my-route.yaml
Certificates that you configure on a listener with tls.certificateRefs or SDS take precedence for requests whose server name indication (SNI) matches the certificate hosts. The leaf certificate serves all other requests. When you remove the annotation, Consul sets TLS.Enabled to false in the API gateway configuration entry.
Verify DNS resolution
When the HTTPRoute binds to the gateway, Consul maps each service that the route targets to the gateway in the gateway-services catalog mapping, which is the same mapping that ingress gateways use. Consul resolves <service>.api-gateway.<domain> queries from this mapping. Refer to API gateway service mapping for more information.
Complete the following steps to verify the mapping and DNS resolution:
Confirm that Consul mapped the service to the gateway. The gateway name in the mapping is the name of the
Gatewayresource. If ACLs are enabled, setCONSUL_HTTP_TOKENto a token withservice:readpermission on the gateway. The following example assumes that you installed Consul in theconsulnamespace withglobal.tls.enabledset totrue.$ kubectl exec --namespace consul consul-server-0 -- \ curl --silent --cacert /consul/tls/ca/tls.crt \ --header "X-Consul-Token: $CONSUL_HTTP_TOKEN" \ https://localhost:8501/v1/catalog/gateway-services/my-gateway \ | jq '[.[] | {Service: .Service.Name, GatewayKind, Port}]'The output includes an entry for the service with
"GatewayKind": "api-gateway"and the listener port:[ { "Service": "static-server", "GatewayKind": "api-gateway", "Port": 8443 } ]Resolve the service name from a pod in the cluster. The A record returns the address of each healthy gateway instance, and the SRV record returns the listener port.
$ kubectl run dns-test --rm --stdin --restart=Never --image=anubhavmishra/tiny-tools -- \ dig +short static-server.api-gateway.consul A static-server.api-gateway.consul SRVIf the lookup does not return any records, verify that the
HTTPRoutestatus shows that the route is accepted and bound to the gateway, and that your cluster DNS forwards theconsuldomain to Consul DNS. Refer to Verify API gateway DNS resolution for additional troubleshooting steps.
Verify HTTPS traffic
Complete the following steps to verify that the gateway terminates HTTPS with a certificate that clients can trust:
Retrieve the active Consul service mesh CA root certificate from a Consul server. The following example assumes that you installed Consul in the
consulnamespace withglobal.tls.enabledset totrue. If TLS is not enabled, usehttp://localhost:8500instead.$ kubectl exec --namespace consul consul-server-0 -- \ curl --silent --cacert /consul/tls/ca/tls.crt https://localhost:8501/v1/connect/ca/roots \ | jq --raw-output '.Roots[] | select(.Active) | .RootCert' > consul-ca.pemCopy the CA root certificate to a client pod that can resolve Consul DNS. Replace
CLIENT_PODwith the name of the pod.$ kubectl cp consul-ca.pem CLIENT_POD:/tmp/consul-ca.pemSend an HTTPS request to the service through the gateway and verify the certificate with the CA root certificate:
$ kubectl exec CLIENT_POD -- curl --cacert /tmp/consul-ca.pem https://static-server.api-gateway.consul:8443The request succeeds without the
--insecureflag because the gateway certificate includes the*.api-gateway.consulSAN and chains to the Consul service mesh CA.
In Consul Enterprise, when you enable Kubernetes namespace mirroring, services in a non-default namespace use namespace-scoped SANs, such as *.api-gateway.<namespace>.consul. To verify the gateway certificate, clients must send a server name that matches these SANs, for example static-server.api-gateway.<namespace>.consul.
HTTP/2 and gRPC listeners
By default, Consul on Kubernetes configures HTTP and HTTPS listeners to use HTTP/1.1 for the connection from the gateway to the backend service. To serve HTTP/2 or gRPC traffic from the client to the backend service, add the api-gateway.consul.hashicorp.com/listener-<sectionName>-protocol annotation to the Gateway. Replace <sectionName> with the name of the listener and set the value to http2 or grpc. Each listener reads only its own annotation, so listeners on the same Gateway can use different protocols.
Requirements for HTTP/2 and gRPC listeners
- Consul v2.1 or later
- Consul Helm chart v2.1.0 or later
- A
GatewayandHTTPRoutein thegateway.networking.k8s.ioAPI group
Configure HTTP/2 and gRPC listeners
Complete the following steps to route HTTP/2 and gRPC traffic through an API gateway:
Set the protocol of each backend service in a
ServiceDefaultsresource. The following example sets thegrpc-serverprotocol togrpcand theh2-serverprotocol tohttp2:servicedefaults.yaml
apiVersion: consul.hashicorp.com/v1alpha1 kind: ServiceDefaults metadata: name: grpc-server spec: protocol: grpc --- apiVersion: consul.hashicorp.com/v1alpha1 kind: ServiceDefaults metadata: name: h2-server spec: protocol: http2Define the
Gatewaywith a listener for each protocol, and add a protocol annotation for eachhttp2orgrpclistener. Set the listenerprotocolfield toHTTPorHTTPS:my-gateway.yaml
apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: name: my-gateway annotations: api-gateway.consul.hashicorp.com/listener-grpc-listener-protocol: "grpc" api-gateway.consul.hashicorp.com/listener-h2-listener-protocol: "http2" spec: gatewayClassName: consul listeners: - name: http-listener protocol: HTTP port: 80 allowedRoutes: namespaces: from: All - name: grpc-listener protocol: HTTP port: 9080 allowedRoutes: namespaces: from: All - name: h2-listener protocol: HTTP port: 9090 allowedRoutes: namespaces: from: AllThe
http-listenerlistener does not have an annotation, so it uses thehttpprotocol.Bind an
HTTPRouteto each listener with thesectionNamefield. The following example routes traffic from thegrpc-listenerlistener to thegrpc-serverservice:grpc-route.yaml
apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: name: grpc-route spec: parentRefs: - name: my-gateway sectionName: grpc-listener rules: - backendRefs: - name: grpc-server port: 9000Apply the configurations:
$ kubectl apply --filename servicedefaults.yaml --filename my-gateway.yaml --filename grpc-route.yaml
You can add, change, or remove the annotation on an existing Gateway. Consul on Kubernetes updates the listener protocol in the API gateway configuration entry and does not change the other listeners.
Consul on Kubernetes ignores an annotation value that is not http, http2, grpc, or tcp, and uses the protocol derived from the listener protocol field. When the <sectionName> in the annotation key does not match the name of any listener, the controller logs a warning that lists the ignored annotation keys.
Verify HTTP/2 and gRPC listeners
Complete the following steps to verify that the gateway uses the configured protocols:
Read the API gateway configuration entry to confirm that each listener has the expected
Protocolvalue. Run the command from a host where theconsulCLI can reach your Consul servers, and setCONSUL_HTTP_TOKENif ACLs are enabled.$ consul config read -kind api-gateway -name my-gatewayForward the Envoy admin port of the gateway pod to your local machine:
$ kubectl port-forward \ $(kubectl get pods --selector gateway.consul.hashicorp.com/name=my-gateway --output jsonpath='{.items[0].metadata.name}') \ 19000:19000In a separate terminal, confirm that the upstream cluster for the
grpc-serverservice uses HTTP/2. The output includeshttp2_protocol_options:$ curl --silent localhost:19000/config_dump \ | jq '.configs[] | select(."@type" | contains("ClustersConfigDump")) | .dynamic_active_clusters[] | select(.cluster.name | startswith("grpc-server.")) | .cluster.typed_extension_protocol_options'Run the same command for the
h2-servercluster. Upstream clusters for services that you route through anhttplistener do not includehttp2_protocol_options.List the HTTP filters on each gateway listener. The listener for the
grpc-listenerlistener includes thegrpc_statsandgrpc_http1_bridgeHTTP filters:$ curl --silent localhost:19000/config_dump \ | jq '.configs[] | select(."@type" | contains("ListenersConfigDump")) | .dynamic_listeners[] | {name: .name, filters: [.active_state.listener.filter_chains[].filters[].typed_config.http_filters[]?.name]}'