-
Notifications
You must be signed in to change notification settings - Fork 5
feat: serve TLSRoute in Passthrough mode (backport apache/apisix-ingress-controller#2882) #485
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
fccc9ab
c586e86
2a425af
9b5b991
f224f78
6f9f3ca
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -20,6 +20,7 @@ package translator | |
| import ( | ||
| "fmt" | ||
|
|
||
| "k8s.io/utils/ptr" | ||
| gatewayv1 "sigs.k8s.io/gateway-api/apis/v1" | ||
|
|
||
| adctypes "github.com/apache/apisix-ingress-controller/api/adc" | ||
|
|
@@ -34,10 +35,7 @@ func (t *Translator) TranslateTLSRoute(tctx *provider.TranslateContext, tlsRoute | |
| result := &TranslateResult{} | ||
| rules := tlsRoute.Spec.Rules | ||
| labels := label.GenLabel(tlsRoute) | ||
| hosts := make([]string, 0, len(tlsRoute.Spec.Hostnames)) | ||
| for _, hostname := range tlsRoute.Spec.Hostnames { | ||
| hosts = append(hosts, string(hostname)) | ||
| } | ||
| snis := tlsRouteSNIs(tctx, tlsRoute) | ||
| for ruleIndex, rule := range rules { | ||
| service := adctypes.NewDefaultService() | ||
| service.Labels = labels | ||
|
|
@@ -143,16 +141,33 @@ func (t *Translator) TranslateTLSRoute(tctx *provider.TranslateContext, tlsRoute | |
| } | ||
| } | ||
|
|
||
| for _, host := range hosts { | ||
| for _, port := range t.l4StreamRoutePorts(tctx) { | ||
| streamRoute := adctypes.NewDefaultStreamRoute() | ||
| streamRouteName := adctypes.ComposeStreamRouteName(tlsRoute.Namespace, tlsRoute.Name, fmt.Sprintf("%d", ruleIndex), "TLS") | ||
| ruleKey := fmt.Sprintf("%d", ruleIndex) | ||
| if port != 0 { | ||
| // Include the port in the name key so multiple listeners produce | ||
| // distinct StreamRoute names/IDs instead of colliding. | ||
| ruleKey = fmt.Sprintf("%d-%d", ruleIndex, port) | ||
| streamRoute.ServerPort = port | ||
| } | ||
| streamRouteName := adctypes.ComposeStreamRouteName(tlsRoute.Namespace, tlsRoute.Name, ruleKey, "TLS") | ||
| streamRoute.Name = streamRouteName | ||
| streamRoute.ID = id.GenID(streamRouteName) | ||
| streamRoute.SNI = host | ||
| // A single SNI keeps using the singular form: it is what every | ||
| // APISIX version understands, and snis only earns its place once | ||
| // there is more than one to match. | ||
| if len(snis) == 1 { | ||
| streamRoute.SNI = snis[0] | ||
| } else { | ||
| streamRoute.SNIs = snis | ||
| } | ||
| if tlsPassthroughOnPort(tctx.Listeners, port) { | ||
| streamRoute.TLSPassthrough = ptr.To(true) | ||
| } | ||
|
Comment on lines
+159
to
+166
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🗄️ Data Integrity & Integration | 🟠 Major | 🏗️ Heavy lift 🔎 Supported by static analysis🏁 Script executed: sed -n '25,38p' Makefile
sed -n '150,170p' internal/adc/translator/tlsroute.go
rg -n 'ADC_VERSION|0\.29\.0|api7/adc' Makefile .github docs testRepository: api7/api7-ingress-controller Length of output: 2805 🏁 Script executed: #!/bin/bash
set -eu
printf '%s\n' '--- Makefile ADC/deployment ---'
sed -n '205,250p' Makefile
printf '%s\n' '--- conformance schema compatibility ---'
sed -n '1,60p' test/conformance/api7ee/conformance_test.go
printf '%s\n' '--- ADC type declarations ---'
sed -n '150,190p' api/adc/types.go
printf '%s\n' '--- relevant ADC references ---'
rg -n -C 3 'StreamRoute|snis|tls_passthrough|tls_passthrough|ADC_VERSION' test api internal Makefile .github docsRepository: api7/api7-ingress-controller Length of output: 50384 🌐 Web query:
💡 Result: <search_synthesis> <source_evidence> Citations:
🏁 Script executed: #!/bin/bash
set -eu
repo=https://api.github.com/repos/api7/adc
printf '%s\n' '--- release tags ---'
curl -fsSL "$repo/git/ref/tags/v0.29.0" | jq -c '{ref:.ref, object:.object}'
printf '%s\n' '--- tag dereference when annotated ---'
obj=$(curl -fsSL "$repo/git/ref/tags/v0.29.0")
type=$(printf '%s' "$obj" | jq -r '.object.type')
sha=$(printf '%s' "$obj" | jq -r '.object.sha')
if [ "$type" = tag ]; then
curl -fsSL "$repo/git/tags/$sha" | jq -c '{tag_object:.sha, target:.object}'
else
printf '%s\n' "$obj" | jq -c '{commit:.object.sha}'
fi
printf '%s\n' '--- release refs available ---'
git ls-remote --tags --refs https://github.com/api7/adc.git 'v0.2[89].0' 'v0.3*.0' | tail -20
printf '%s\n' '--- schema at supplied commit ---'
curl -fsSL https://raw.githubusercontent.com/api7/adc/2e14e5a2/libs/backend-apisix-standalone/src/typing.ts | rg -n -A24 -B2 'StreamRouteSchema'Repository: api7/api7-ingress-controller Length of output: 4408 🏁 Script executed: #!/bin/bash
set -eu
for ref in v0.30.0 main; do
printf '%s\n' "--- $ref StreamRoute schema ---"
curl -fsSL "https://raw.githubusercontent.com/api7/adc/$ref/libs/backend-apisix-standalone/src/typing.ts" |
sed -n '/const StreamRouteSchema =/,/export type StreamRoute/p' |
grep -E 'StreamRouteSchema|remote_addr|server_addr|server_port|sni|snis|tls_passthrough|service_id|plugins|protocol'
done
printf '%s\n' '--- v0.30.0 commit existence ---'
curl -fsSL https://api.github.com/repos/api7/adc/commits/55ea96a6a90d0a438f839a29cf3eb4ac55c911eb | jq -r '.sha'Repository: api7/api7-ingress-controller Length of output: 1025 🏁 Script executed: #!/bin/bash
set -eu
printf '%s\n' '--- ADC main ref ---'
curl -fsSL https://api.github.com/repos/api7/adc/git/ref/heads/main | jq -c '{ref:.ref,object:.object}'
printf '%s\n' '--- latest release ---'
curl -fsSL https://api.github.com/repos/api7/adc/releases/latest | jq -c '{tag_name,name,draft,prerelease}'
printf '%s\n' '--- version tags ---'
git ls-remote --tags --refs https://github.com/api7/adc.git | awk -F/ '$NF ~ /^v[0-9]+\\.[0-9]+\\.[0-9]+$/ {print $NF, $1}' | sort -V | tail -12
printf '%s\n' '--- recent commits changing schema file ---'
curl -fsSL 'https://api.github.com/repos/api7/adc/commits?path=libs/backend-apisix-standalone/src/typing.ts&per_page=20' |
jq -r '.[] | [.sha, .commit.author.date, .commit.message] | `@tsv`' | head -20Repository: api7/api7-ingress-controller Length of output: 1997 Update the ADC dependency before merge. ADC Update the ADC image and binary to a build containing ADC commit 🤖 Prompt for AI Agents |
||
| streamRoute.Labels = labels | ||
| // Attach L4RoutePolicy plugins at the stream_route level: the APISIX stream proxy | ||
| // applies plugins from the stream_route, not from the service. With multiple SNIs | ||
| // each stream_route carries its own copy of the plugins. | ||
| // applies plugins from the stream_route, not from the service. With multiple | ||
| // listener ports each stream_route carries its own copy of the plugins. | ||
| streamRoute.Plugins = make(adctypes.Plugins) | ||
| t.AttachL4RoutePolicyPlugins(tctx.L4RoutePolicies, tlsRoute.Namespace, tlsRoute.Name, "TLSRoute", streamRoute.Plugins, tctx.Secrets) | ||
| service.StreamRoutes = append(service.StreamRoutes, streamRoute) | ||
|
|
@@ -162,3 +177,64 @@ func (t *Translator) TranslateTLSRoute(tctx *provider.TranslateContext, tlsRoute | |
| } | ||
| return result, nil | ||
| } | ||
|
|
||
| // tlsRouteSNIs returns the SNIs the route's stream routes match on. | ||
| // | ||
| // A TLSRoute without hostnames matches everything its listeners accept, so it | ||
| // falls back to the matched listener hostnames and, when those carry none | ||
| // either, to the catch-all "*". Emitting nothing - which is what the per | ||
| // hostname loop used to do - left such a route attached but unserved. | ||
| func tlsRouteSNIs(tctx *provider.TranslateContext, tlsRoute *gatewayv1.TLSRoute) []string { | ||
| if len(tlsRoute.Spec.Hostnames) > 0 { | ||
| snis := make([]string, 0, len(tlsRoute.Spec.Hostnames)) | ||
| for _, hostname := range tlsRoute.Spec.Hostnames { | ||
| snis = append(snis, string(hostname)) | ||
| } | ||
| return snis | ||
| } | ||
|
|
||
| snis := make([]string, 0, len(tctx.Listeners)) | ||
| seen := make(map[string]struct{}, len(tctx.Listeners)) | ||
| for _, listener := range tctx.Listeners { | ||
| if listener.Hostname == nil || *listener.Hostname == "" { | ||
| continue | ||
| } | ||
| hostname := string(*listener.Hostname) | ||
| if _, ok := seen[hostname]; ok { | ||
| continue | ||
| } | ||
| seen[hostname] = struct{}{} | ||
| snis = append(snis, hostname) | ||
| } | ||
| if len(snis) == 0 { | ||
| return []string{"*"} | ||
| } | ||
| return snis | ||
| } | ||
|
|
||
| // tlsPassthroughOnPort reports whether the stream routes bound to port must | ||
| // forward the connection untouched instead of having the gateway terminate it. | ||
| // port 0 means the StreamRoute carries no server_port match, so every matched | ||
| // listener applies. | ||
| // | ||
| // Every matched TLS listener on the port has to agree. Within one Gateway a | ||
| // port carrying both modes is already reported ProtocolConflict and attaches | ||
| // no routes; across Gateways the combination is unrepresentable, since the | ||
| // physical stream listen has a single mode - so the terminating behaviour wins | ||
| // rather than a guess. | ||
| func tlsPassthroughOnPort(listeners []gatewayv1.Listener, port int32) bool { | ||
| matched := false | ||
| for _, listener := range listeners { | ||
| if listener.Protocol != gatewayv1.TLSProtocolType { | ||
| continue | ||
| } | ||
| if port != 0 && listener.Port != port { | ||
| continue | ||
| } | ||
| if listener.TLS == nil || listener.TLS.Mode == nil || *listener.TLS.Mode != gatewayv1.TLSModePassthrough { | ||
| return false | ||
| } | ||
| matched = true | ||
| } | ||
| return matched | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🎯 Functional Correctness | 🟠 Major | 🏗️ Heavy lift
🔎 Supported by static analysis
🏁 Script executed:
Repository: api7/api7-ingress-controller
Length of output: 15441
🏁 Script executed:
Repository: api7/api7-ingress-controller
Length of output: 50385
🏁 Script executed:
Repository: api7/api7-ingress-controller
Length of output: 50385
Derive the SNI set for each listener port.
A TLSRoute can attach to multiple listeners when its hostnames intersect each listener. If port 9110 accepts
a.example.comand port 9120 acceptsb.example.com, the translator emits one StreamRoute per port but assigns[a.example.com, b.example.com]to both routes. This allows each port-specific route to match a hostname that its listener does not accept.Intersect the route hostnames with only the listeners for the current port before setting
SNIorSNIs. Apply the same per-port filtering to the fallback set for routes without hostnames.🤖 Prompt for AI Agents