Skip to content

Commit a17baf3

Browse files
authored
Support mixed externalname and local services in single route (#4188)
1 parent b41c973 commit a17baf3

File tree

10 files changed

+332
-76
lines changed

10 files changed

+332
-76
lines changed

examples/externalname-service/README.md

Lines changed: 130 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -7,16 +7,24 @@ This example demonstrates how to use NGINX Gateway Fabric to route traffic to ex
77
In this example, we will:
88

99
1. Deploy NGINX Gateway Fabric with DNS resolver configuration
10-
2. Create an ExternalName service pointing to an external API
11-
3. Configure HTTP and TLS routes to route traffic to this external service
12-
4. Test both HTTP and TLS routing functionality
10+
2. Create an ExternalName service pointing to an external API (httpbin.org)
11+
3. Deploy an internal service (coffee) to demonstrate mixed routing
12+
4. Configure an HTTPRoute that routes to both external and internal services
13+
5. (Optional) Configure BackendTLSPolicy for HTTPS connections to external services
14+
6. Test HTTP routing with both external and internal services
15+
7. (Optional) Test HTTPS backend connections and TLS passthrough
1316

1417
## Running the Example
1518

1619
## 1. Deploy NGINX Gateway Fabric
1720

1821
1. Follow the [installation instructions](https://docs.nginx.com/nginx-gateway-fabric/install/) to deploy NGINX Gateway Fabric.
1922

23+
**Note**: To use BackendTLSPolicy or TLSRoute for HTTPS connections to external services, you must:
24+
25+
- Install NGINX Gateway Fabric with `enableExperimental=true`
26+
- Install the experimental Gateway APIs on the cluster
27+
2028
## 2. Deploy the Gateway with DNS Resolver
2129

2230
Create the Gateway and NginxProxy configuration that enables DNS resolution for ExternalName services:
@@ -30,30 +38,64 @@ This creates:
3038
- A Gateway with HTTP and TLS listeners
3139
- An NginxProxy resource with DNS resolver configuration
3240

33-
## 3. Deploy ExternalName Services
41+
## 3. Deploy Services
3442

35-
Create the ExternalName service that points to external APIs:
43+
Create the ExternalName service and internal service:
3644

3745
```shell
38-
kubectl apply -f external-service.yaml
46+
kubectl apply -f cafe.yaml
3947
```
4048

41-
This creates an ExternalName service which points to `httpbin.org` with both HTTP and HTTPS ports
49+
This creates:
50+
51+
- An ExternalName service which points to `httpbin.org` with both HTTP and HTTPS ports
52+
- A coffee deployment and service for internal routing
4253

4354
## 4. Configure Routing
4455

45-
Create the HTTPRoute and TLSRoute that route traffic to the external service:
56+
Create the HTTPRoute and TLSRoute that route traffic to the services:
4657

4758
```shell
4859
kubectl apply -f route.yaml
4960
```
5061

5162
This creates:
5263

53-
- An HTTPRoute that routes HTTP traffic to the httpbin service
54-
- A TLSRoute that routes TLS traffic to the httpbin service
64+
- An HTTPRoute with two paths:
65+
- `/external/*` - Routes to the ExternalName service (httpbin.org) with a URLRewrite filter that strips the `/external` prefix
66+
- `/coffee` - Routes to the internal coffee service
67+
- A TLSRoute for TLS passthrough to the external httpbin service (commented out by default)
68+
69+
## 5. (Optional) Configure HTTPS Backend
70+
71+
If your external service requires HTTPS connections (like `https://httpbin.org`), you need to:
72+
73+
1. Update the HTTPRoute to use port 443 for the httpbin service in `route.yaml`:
74+
75+
```yaml
76+
backendRefs:
77+
- name: httpbin
78+
port: 443 # Change from 80 to 443
79+
```
5580
56-
## 5. Test the Configuration
81+
2. Create a BackendTLSPolicy:
82+
83+
```shell
84+
kubectl apply -f backendtlspolicy.yaml
85+
```
86+
87+
This configures NGINX Gateway Fabric to:
88+
89+
- Establish TLS connections to the backend service
90+
- Verify the backend's TLS certificate using system CA certificates
91+
- Match the certificate's hostname against the external service hostname
92+
93+
**Note**: BackendTLSPolicy is different from TLSRoute:
94+
95+
- **BackendTLSPolicy**: NGF terminates client TLS/HTTP and establishes HTTPS to the backend. Allows HTTP-level routing (paths, headers, etc.)
96+
- **TLSRoute**: TLS passthrough where the client establishes TLS directly with the backend. No HTTP-level routing possible.
97+
98+
## 6. Test the Configuration
5799

58100
Wait for the Gateway to be ready:
59101

@@ -66,43 +108,106 @@ Save the public IP address and ports of the NGINX Service into shell variables:
66108
```text
67109
GW_IP=XXX.YYY.ZZZ.III
68110
GW_PORT_HTTP=<port number>
69-
GW_PORT_HTTPS=<port number>
70111
```
71112

72-
Test the httpbin service via HTTP:
113+
Test the ExternalName service (httpbin.org) via HTTP:
73114

74115
```shell
75-
curl --resolve httpbin.example.com:$GW_PORT_HTTP:$GW_IP http://httpbin.example.com:$GW_PORT_HTTP/get
116+
curl --resolve cafe.example.com:$GW_PORT_HTTP:$GW_IP http://cafe.example.com:$GW_PORT_HTTP/external/get
76117
```
77118

78-
Test the httpbin service via TLS passthrough:
79-
80-
```shell
81-
curl -k --resolve httpbin.example.com:$GW_PORT_HTTPS:$GW_IP https://httpbin.example.com:$GW_PORT_HTTPS/get
82-
```
83-
84-
You should see a JSON response from httpbin.org showing request details e.g.
119+
You should see a JSON response from httpbin.org. Notice the `Host` header is correctly set to `httpbin.org`:
85120

86121
```json
87122
{
88123
"args": {},
89124
"headers": {
90125
"Accept": "*/*",
91-
"Host": "httpbin.example.com",
126+
"Host": "httpbin.org",
92127
"User-Agent": "curl/8.7.1",
93-
"X-Amzn-Trace-Id": "Root=1-68a49086-1e1dabb51155e05c1ebc1f63"
128+
"X-Amzn-Trace-Id": "Root=1-6901e342-2ce43cf518ee439d4e4d4867",
129+
"X-Forwarded-Host": "cafe.example.com"
94130
},
95131
"origin": "xxx.xxx.xxx.xx",
96-
"url": "https://httpbin.example.com/get"
132+
"url": "http://cafe.example.com/get"
97133
}
98134
```
99135

136+
Test the internal coffee service:
137+
138+
```shell
139+
curl --resolve cafe.example.com:$GW_PORT_HTTP:$GW_IP http://cafe.example.com:$GW_PORT_HTTP/coffee
140+
```
141+
142+
You should see a response from the coffee service:
143+
144+
```text
145+
Server address: 10.244.0.7:8080
146+
Server name: coffee-<pod-id>
147+
Date: ...
148+
URI: /coffee
149+
Request ID: ...
150+
```
151+
152+
You can also test other httpbin.org endpoints:
153+
154+
```shell
155+
# Test /anything endpoint
156+
curl --resolve cafe.example.com:$GW_PORT_HTTP:$GW_IP http://cafe.example.com:$GW_PORT_HTTP/external/anything
157+
158+
# Test /headers endpoint
159+
curl --resolve cafe.example.com:$GW_PORT_HTTP:$GW_IP http://cafe.example.com:$GW_PORT_HTTP/external/headers
160+
```
161+
162+
### Testing HTTPS Backend (Optional)
163+
164+
If you configured BackendTLSPolicy in step 5, NGF will establish HTTPS connections to the external service. The client connection remains HTTP, but the backend connection uses HTTPS:
165+
166+
```shell
167+
curl --resolve cafe.example.com:$GW_PORT_HTTP:$GW_IP http://cafe.example.com:$GW_PORT_HTTP/external/get
168+
```
169+
170+
The response will show the request was successfully proxied to `https://httpbin.org` with proper TLS verification.
171+
172+
### Testing TLS Passthrough (Optional)
173+
174+
To test TLS passthrough, first uncomment the TLSRoute section in `route.yaml` and reapply:
175+
176+
```shell
177+
kubectl apply -f route.yaml
178+
```
179+
180+
Then save the HTTPS port:
181+
182+
```text
183+
GW_PORT_HTTPS=<port number>
184+
```
185+
186+
Test the httpbin service via TLS passthrough:
187+
188+
```shell
189+
curl -k --resolve httpbin.example.com:$GW_PORT_HTTPS:$GW_IP https://httpbin.example.com:$GW_PORT_HTTPS/get
190+
```
191+
192+
You should see a JSON response from httpbin.org via HTTPS.
193+
194+
## How It Works
195+
196+
This example demonstrates key features for routing to external services:
197+
198+
1. **DNS Resolution**: The NginxProxy resource configures DNS resolvers (8.8.8.8, 1.1.1.1) so NGINX can resolve external hostnames
199+
2. **Host Header Handling**: NGF automatically detects ExternalName services and sets the `Host` header to the external hostname (`httpbin.org`) instead of the Gateway hostname (`cafe.example.com`), ensuring external services receive the correct Host header
200+
3. **URL Rewriting**: The URLRewrite filter strips the `/external` prefix before proxying to httpbin.org, so `/external/get` becomes `/get` on the external service
201+
4. **Mixed Routing**: The same HTTPRoute can route to both ExternalName services and internal Kubernetes services seamlessly
202+
5. **HTTPS Backends**: BackendTLSPolicy enables secure HTTPS connections to external services while allowing HTTP-level routing based on paths, headers, etc.
203+
6. **TLS Passthrough**: The TLSRoute allows direct TLS connections to external services without termination at the Gateway (no HTTP-level routing)
204+
100205
## Cleanup
101206

102207
Remove all resources created in this example:
103208

104209
```shell
105210
kubectl delete -f route.yaml
106-
kubectl delete -f external-service.yaml
211+
kubectl delete -f cafe.yaml
107212
kubectl delete -f gateway.yaml
108213
```
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
apiVersion: gateway.networking.k8s.io/v1alpha3
2+
kind: BackendTLSPolicy
3+
metadata:
4+
name: httpbin-tls
5+
spec:
6+
targetRefs:
7+
- group: ""
8+
kind: Service
9+
name: httpbin
10+
validation:
11+
hostname: httpbin.org
12+
wellKnownCACertificates: System
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
apiVersion: v1
2+
kind: Service
3+
metadata:
4+
name: httpbin
5+
spec:
6+
type: ExternalName
7+
externalName: httpbin.org
8+
ports:
9+
- port: 80
10+
targetPort: 80
11+
protocol: TCP
12+
name: http
13+
- port: 443
14+
targetPort: 443
15+
protocol: TCP
16+
name: https
17+
---
18+
apiVersion: apps/v1
19+
kind: Deployment
20+
metadata:
21+
name: coffee
22+
spec:
23+
replicas: 1
24+
selector:
25+
matchLabels:
26+
app: coffee
27+
template:
28+
metadata:
29+
labels:
30+
app: coffee
31+
spec:
32+
containers:
33+
- name: coffee
34+
image: nginxdemos/nginx-hello:plain-text
35+
ports:
36+
- containerPort: 8080
37+
---
38+
apiVersion: v1
39+
kind: Service
40+
metadata:
41+
name: coffee
42+
spec:
43+
ports:
44+
- port: 80
45+
targetPort: 8080
46+
protocol: TCP
47+
name: http
48+
selector:
49+
app: coffee

examples/externalname-service/external-service.yaml

Lines changed: 0 additions & 16 deletions
This file was deleted.

examples/externalname-service/route.yaml

Lines changed: 29 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -6,27 +6,39 @@ spec:
66
parentRefs:
77
- name: external-gateway
88
hostnames:
9-
- httpbin.example.com
9+
- cafe.example.com
1010
rules:
1111
- matches:
1212
- path:
1313
type: PathPrefix
14-
value: /
14+
value: /external
15+
filters:
16+
- type: URLRewrite
17+
urlRewrite:
18+
path:
19+
type: ReplacePrefixMatch
20+
replacePrefixMatch: /
1521
backendRefs:
1622
- name: httpbin
1723
port: 80
18-
19-
---
20-
apiVersion: gateway.networking.k8s.io/v1alpha2
21-
kind: TLSRoute
22-
metadata:
23-
name: httpbin-tls-route
24-
spec:
25-
parentRefs:
26-
- name: external-gateway
27-
hostnames:
28-
- httpbin.example.com
29-
rules:
30-
- backendRefs:
31-
- name: httpbin
32-
port: 443
24+
- matches:
25+
- path:
26+
type: PathPrefix
27+
value: /coffee
28+
backendRefs:
29+
- name: coffee
30+
port: 80
31+
# ---
32+
# apiVersion: gateway.networking.k8s.io/v1alpha2
33+
# kind: TLSRoute
34+
# metadata:
35+
# name: httpbin-tls-route
36+
# spec:
37+
# parentRefs:
38+
# - name: external-gateway
39+
# hostnames:
40+
# - httpbin.example.com
41+
# rules:
42+
# - backendRefs:
43+
# - name: httpbin
44+
# port: 443

0 commit comments

Comments
 (0)