Introduction
This article documents the lab validation results for the use case API Gateway → External Service (via HTTPRoute and Terminating Gateway) in Consul Enterprise on Kubernetes environment.
During testing, an inconsistency was observed, while registering the external service using the ServiceDefaults method, it resulted in a cluster name mismatch between API Gateway and the Terminating Gateway (TGW). However, the same use case functions correctly when the external service is registered using the Consul Catalog method.
This document provides detailed configuration steps, observed behaviour, and key implementation considerations.
Expected Outcome
Once the configuration is completed as described in this article:
API Gateway deployed in Kubernetes should successfully route traffic to an external HTTPS service via a Terminating Gateway.
The external service should be reachable through the API Gateway LoadBalancer endpoint.
Service-to-service communication should be governed by Consul intentions.
TLS should be properly handled for outbound connections from the Terminating Gateway to the external service.
Prerequisites
This guide applies to the following environment and components:
HashiCorp Consul Enterprise
Kubernetes cluster
Consul deployed via Helm
Consul Namespaces enabled
ACLs enabled and managed
TLS enabled with Auto Encrypt
Consul CNI enabled
Connect Injection enabled (Transparent Proxy default enabled)
Gateway API (Kubernetes Gateway API v1beta1)
Terminating Gateway enabled
API Gateway managed via Consul GatewayClass
Use Case
The objective is to route traffic from:
Client → API Gateway → HTTPRoute → MeshService → Terminating Gateway → External HTTPS Service
Key validation scenarios:
Validate external service registration using the Consul Catalog method (Working scenario).
Validate external service registration using the ServiceDefaults method (Observed cluster mismatch issue).
Validate API Gateway deployment within Kubernetes (without requiring a separate VM).
Validate outbound TLS from TGW to external HTTPS service.
Procedure
Step 1 – Deploy Consul on Kubernetes
Deploy Consul Enterprise using the following values.yaml configuration:
global:
enabled: true
name: consul
datacenter: dc1
enableConsulNamespaces: true
image: "hashicorp/consul-enterprise:1.21.7-ent"
acls:
manageSystemACLs: true
tls:
enabled: true
enableAutoEncrypt: true
verify: true
enterpriseLicense:
secretName: consul-license
secretKey: license
server:
enabled: true
replicas: 1
extraConfig: |
{
"log_level": "DEBUG"
}
connectInject:
transparentProxy:
defaultEnabled: true
enabled: true
default: true
apiGateway:
managedGatewayClass:
serviceType: LoadBalancer
cni:
enabled: true
ui:
enabled: true
service:
type: LoadBalancer
terminatingGateways:
enabled: true
gateways:
- name: test-terminating-gateway
replicas: 1
consulNamespace: "test"This configuration enables:
Consul Namespaces
ACL management
TLS and Auto Encrypt
CNI plugin
API Gateway managed class
Terminating Gateways registered in different Consul namespaces (named -
test).
Step 2 – Create TerminatingGateway CRD
Create the following resource in namespace test:
apiVersion: consul.hashicorp.com/v1alpha1
kind: TerminatingGateway
metadata:
name: test-terminating-gateway
namespace: test
spec:
services:
- name: example-https
namespace: test
sni: example.com
caFile: /etc/ssl/certs/ca-certificates.crtThis configuration ensures:
Traffic destined for
example-httpsis routed throughtest-terminating-gateway.TLS SNI is set to
example.com.Outbound TLS validation uses the specified CA bundle.
Step 3 – Register External Service (Consul Catalog Method – Recommended)
Register the external service using the Consul Catalog API:
{
"Node": "example_com",
"Address": "example.com",
"Namespace": "test",
"NodeMeta": {
"external-node": "true",
"external-probe": "true"
},
"Service": {
"Address": "example.com",
"ID": "example-https",
"Service": "example-https",
"Port": 443
}
}
Step 4 – Create MeshService (Required for Gateway API Integration)
Because API Gateway follows the Kubernetes Gateway API specification, create a MeshService resource:
apiVersion: consul.hashicorp.com/v1alpha1
kind: MeshService
metadata:
name: example-https
namespace: test
spec:
name: example-https
Step 5 – Deploy API Gateway and HTTPRoute
Create the Gateway:
apiVersion: gateway.networking.k8s.io/v1beta1
kind: Gateway
metadata:
name: api-gateway
namespace: test
spec:
gatewayClassName: consul
listeners:
- protocol: HTTP
port: 80
name: http
allowedRoutes:
namespaces:
from: AllCreate the HTTPRoute:
apiVersion: gateway.networking.k8s.io/v1beta1
kind: HTTPRoute
metadata:
name: routes
namespace: test
spec:
parentRefs:
- name: api-gateway
namespace: test
rules:
- matches:
- path:
type: PathPrefix
value: /
backendRefs:
- kind: MeshService
group: consul.hashicorp.com
name: example-https
namespace: test
port: 443
Step 6 – Configure Service Intentions
Allow API Gateway to communicate with the external service:
apiVersion: consul.hashicorp.com/v1alpha1
kind: ServiceIntentions
metadata:
name: api-gateway-to-example-https
namespace: test
spec:
destination:
name: example-https
namespace: test
sources:
- name: api-gateway
namespace: test
action: allow
Step 7 – Validate Connectivity
Access the API Gateway LoadBalancer endpoint:
curl <API_GW_LB_Endpoint>
Expected Result:
HTML response from example.com:
<!doctype html><html lang="en"><head><title>Example Domain</title><meta name="viewport" content="width=device-width, initial-scale=1"><style>body{background:#eee;width:60vw;margin:15vh auto;font-family:system-ui,sans-serif}h1{font-size:1.5em}div{opacity:0.8}a:link,a:visited{color:#348}</style><body><div><h1>Example Domain</h1><p>This domain is for use in documentation examples without needing permission. Avoid use in operations.<p><a href="https://iana.org/domains/example">Learn more</a></div></body></html>
This confirms:
API Gateway → MeshService → TGW → External HTTPS routing works correctly
TLS outbound is properly configured
Intentions are functioning as expected
Observations
ServiceDefaults Method Issue
When registering the external service using the ServiceDefaults method, a cluster name mismatch was observed between:
API Gateway cluster
Terminating Gateway cluster
This mismatch prevents successful routing to the external service.
However, the issue does not occur when using the Consul Catalog registration method.
This behaviour suggests a potential bug or configuration limitation related to cluster identity resolution when ServiceDefaults is used for external services behind a Terminating Gateway.
Important Note - Consul Engineering team is aware and this is already in pipeline and has remediation planned in future.