Skip to content
Merged
Show file tree
Hide file tree
Changes from 31 commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
9ed4c7b
feat(securitypolicy): Update API to support tcp security policy. Brea…
davem-git Oct 8, 2025
76c8c9e
Merge branch 'main' into feat-tcp-security-policy-api
davem-git Oct 8, 2025
c574ec1
feat(securitypolicy: Update gatewayAPI to suppor tcp security policy.…
davem-git Oct 8, 2025
1b86ebf
Merge branch 'main' of github.com:envoyproxy/gateway into feat-tcp-se…
davem-git Oct 8, 2025
9d7efb3
Merge branch 'main' of github.com:envoyproxy/gateway into feat-tcp-se…
davem-git Oct 9, 2025
a28a214
feat(securitypolicy): added comments to clarify TCPRoute only support…
davem-git Oct 9, 2025
b1a5ba8
Merge branch 'feat-tcp-security-policy-api' of github.com:davem-git/g…
davem-git Oct 9, 2025
b7838a8
Merge branch 'main' of github.com:envoyproxy/gateway into feat-tcp-se…
davem-git Oct 9, 2025
414cf38
Merge branch 'main' into feat-tcp-security-policy-api
davem-git Oct 9, 2025
2e13b1e
feat(securitypolicy): update securitypolicy.go to comments on prevous PR
davem-git Oct 9, 2025
da8909e
Merge branch 'main' of github.com:envoyproxy/gateway into feat-tcp-se…
davem-git Oct 9, 2025
96cbe84
fixed lint error
davem-git Oct 9, 2025
43fe55a
Merge branch 'feat-tcp-security-policy-api' of github.com:davem-git/g…
davem-git Oct 9, 2025
52ac1fa
make manifests
davem-git Oct 9, 2025
b61caa6
feat(securitypolicy): emoved unneeded line in securitypolicy.go
davem-git Oct 9, 2025
f0520a4
ran gen-check
davem-git Oct 10, 2025
220b334
Merge branch 'main' of github.com:envoyproxy/gateway into feat-tcp-se…
davem-git Oct 11, 2025
45e57b4
feat(securitypolicy): Removed failing test. The test was to catch emp…
davem-git Oct 11, 2025
6bc7270
added more unit tests
davem-git Oct 13, 2025
f78696c
Merge branch 'main' of github.com:envoyproxy/gateway into feat-tcp-se…
davem-git Oct 13, 2025
03970e1
renamed from security.Authorization to authorization
davem-git Oct 14, 2025
8c0649e
Merge branch 'main' of github.com:envoyproxy/gateway into feat-tcp-se…
davem-git Oct 14, 2025
67814ea
Merge branch 'feat-tcp-security-policy-api' of github.com:davem-git/g…
davem-git Oct 14, 2025
36a928d
broke out tests and reverted changs that weren't needed
davem-git Oct 15, 2025
3ffcae5
Merge branch 'main' of github.com:envoyproxy/gateway into feat-tcp-se…
davem-git Oct 15, 2025
253d5f6
make validator section easier to read and run
davem-git Oct 15, 2025
07a37dc
updated tcpSecurityFeatures to tcpAuthorization, and fixed newline diffs
davem-git Oct 15, 2025
9bd4d86
added more comments and fixed naming issue with testdata file
davem-git Oct 15, 2025
25ff0a4
Merge branch 'main' of github.com:envoyproxy/gateway into feat-tcp-se…
davem-git Oct 16, 2025
5c9694e
added debug for clarity
davem-git Oct 16, 2025
f08d348
Updated PR to address comments
davem-git Oct 16, 2025
5156577
Merge branch 'main' of github.com:envoyproxy/gateway into feat-tcp-se…
davem-git Oct 17, 2025
f0e49e1
Updated PR to address comments
davem-git Oct 17, 2025
32aa8c2
Merge branch 'main' of github.com:envoyproxy/gateway into feat-tcp-se…
davem-git Oct 18, 2025
8a68216
Merge branch 'main' of github.com:envoyproxy/gateway into feat-tcp-se…
davem-git Oct 20, 2025
6753bca
replaced gwapiv1a2.Group(gwapiv1a2 to with 1.Group(gwapiv1
davem-git Oct 20, 2025
a56cc92
Merge branch 'main' of github.com:envoyproxy/gateway into feat-tcp-se…
davem-git Oct 20, 2025
84a79cc
Merge branch 'main' of github.com:envoyproxy/gateway into feat-tcp-se…
davem-git Oct 21, 2025
b58186c
removed defensive programing to limit the changes
davem-git Oct 21, 2025
a989171
Merge branch 'main' of github.com:envoyproxy/gateway into feat-tcp-se…
davem-git Oct 22, 2025
7e601e5
Merge branch 'main' of github.com:envoyproxy/gateway into feat-tcp-se…
davem-git Oct 22, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
138 changes: 128 additions & 10 deletions internal/gatewayapi/securitypolicy.go
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ func (t *Translator) ProcessSecurityPolicies(securityPolicies []*egv1a1.Security
// 3. Then translate Policies targeting Listeners
// 4. Finally, the policies targeting Gateways

// Process the policies targeting RouteRules
// Process the policies targeting RouteRules (HTTP + TCP)
for _, currPolicy := range securityPolicies {
policyName := utils.NamespacedName(currPolicy)
targetRefs := getPolicyTargetRefs(currPolicy.Spec.PolicyTargetReferences, routes, currPolicy.Namespace)
Expand All @@ -107,12 +107,12 @@ func (t *Translator) ProcessSecurityPolicies(securityPolicies []*egv1a1.Security
res = append(res, policy)
}

t.processSecurityPolicyForHTTPRoute(resources, xdsIR,
t.processSecurityPolicyForRoute(resources, xdsIR,
routeMap, gatewayRouteMap, policy, currTarget)
}
}
}
// Process the policies targeting xRoutes
// Process the policies targeting xRoutes (HTTP + TCP)
for _, currPolicy := range securityPolicies {
policyName := utils.NamespacedName(currPolicy)
targetRefs := getPolicyTargetRefs(currPolicy.Spec.PolicyTargetReferences, routes, currPolicy.Namespace)
Expand All @@ -125,7 +125,7 @@ func (t *Translator) ProcessSecurityPolicies(securityPolicies []*egv1a1.Security
res = append(res, policy)
}

t.processSecurityPolicyForHTTPRoute(resources, xdsIR,
t.processSecurityPolicyForRoute(resources, xdsIR,
routeMap, gatewayRouteMap, policy, currTarget)
}
}
Expand Down Expand Up @@ -176,7 +176,7 @@ func (t *Translator) ProcessSecurityPolicies(securityPolicies []*egv1a1.Security
return res
}

func (t *Translator) processSecurityPolicyForHTTPRoute(
func (t *Translator) processSecurityPolicyForRoute(
resources *resource.Resources,
xdsIR resource.XdsIRMap,
routeMap map[policyTargetRouteKey]*policyRouteTargetContext,
Expand Down Expand Up @@ -244,12 +244,20 @@ func (t *Translator) processSecurityPolicyForHTTPRoute(
return
}

if err := validateSecurityPolicy(policy); err != nil {
// Protocol-specific validation: pick the appropriate validator and message,
// then run it once to keep the flow linear and easier to read.
validator := validateSecurityPolicy
errMsg := "invalid SecurityPolicy"
if currTarget.Kind == resource.KindTCPRoute {
validator = validateSecurityPolicyForTCP
errMsg = "invalid SecurityPolicy for TCP route"
}
if err := validator(policy); err != nil {
status.SetTranslationErrorForPolicyAncestors(&policy.Status,
parentGateways,
t.GatewayControllerName,
policy.Generation,
status.Error2ConditionMsg(fmt.Errorf("invalid SecurityPolicy: %w", err)),
status.Error2ConditionMsg(fmt.Errorf("%s: %w", errMsg, err)),
)

return
Expand Down Expand Up @@ -392,6 +400,44 @@ func validateSecurityPolicy(p *egv1a1.SecurityPolicy) error {
return nil
}

// validateSecurityPolicyForTCP ensures SecurityPolicy usage on TCP is compatible.
//
// TCP supports Authorization with ClientCIDRs ONLY.
// - Principals.JWT => invalid (HTTP-only)
// - Principals.Headers => invalid (HTTP-only)
// - Empty/no Authorization is allowed and results in no-op on TCP.
// Returns an error when any HTTP-only field is present or CIDRs are invalid.
func validateSecurityPolicyForTCP(p *egv1a1.SecurityPolicy) error {
if p.Spec.CORS != nil || p.Spec.JWT != nil || p.Spec.OIDC != nil || p.Spec.APIKeyAuth != nil || p.Spec.BasicAuth != nil || p.Spec.ExtAuth != nil {
return fmt.Errorf("only authorization is supported for TCP (routes/listeners)")
}
if p.Spec.Authorization == nil || len(p.Spec.Authorization.Rules) == 0 {
return nil
}
for i, rule := range p.Spec.Authorization.Rules {
if rule.Principal.JWT != nil {
return fmt.Errorf("rule %d: JWT not supported for TCP", i)
}
if len(rule.Principal.Headers) > 0 {
return fmt.Errorf("rule %d: headers not supported for TCP", i)
}
if err := validateCIDRs(rule.Principal.ClientCIDRs); err != nil {
return fmt.Errorf("rule %d: %w", i, err)
}
}
return nil
}

// validateCIDRs validates CIDR strings for TCP authorization rules.
func validateCIDRs(cidrs []egv1a1.CIDR) error {
for _, c := range cidrs {
if _, _, err := net.ParseCIDR(string(c)); err != nil {
return fmt.Errorf("invalid ClientCIDR %q: %w", c, err)
}
}
return nil
}

func validateAPIKeyAuth(apiKeyAuth *egv1a1.APIKeyAuth) error {
for _, keySource := range apiKeyAuth.ExtractFrom {
// only one of headers, params or cookies is supposed to be specified.
Expand Down Expand Up @@ -657,10 +703,46 @@ func (t *Translator) translateSecurityPolicyForRoute(
}

irKey := t.getIRKey(gtwCtx.Gateway)
for _, listener := range parentRefCtx.listeners {
irListener := xdsIR[irKey].GetHTTPListener(irListenerName(listener))
if irListener != nil {
switch route.GetRouteType() {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

curious why this code is needed when its NA for TCPRoute, which is checked in validateSecurityPolicyForTCP

Copy link
Contributor Author

@davem-git davem-git Oct 21, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

which part are you referring to, why we do switch.route? TCP is applied differently with expectedTCPRouteName := strings.TrimSuffix(prefix, "/") , but i don't see that getting applied just continueing if it doesn't match

then there's this line

if target.SectionName != nil && string(*target.SectionName) != r.Destination.Metadata.SectionName {
  continue
}```
vs this one for httproute
```go

if target.SectionName != nil && string(*target.SectionName) != r.Metadata.SectionName {
continue
}

those have to be different.

let me see if i can reduce the difference

case resource.KindTCPRoute:
// Only client-IP Authorization is applicable for TCP routes.
// TCP IR route names are flat. The computed prefix includes a trailing
// '/' (e.g. "tcproute/default/tcp-app-2/"), so trim the suffix to get
// the exact TCP route name used in the IR:
// prefix == "tcproute/default/tcp-app-2/" -> expectedTCPRouteName == "tcproute/default/tcp-app-2"
expectedTCPRouteName := strings.TrimSuffix(prefix, "/")
for _, listener := range parentRefCtx.listeners {
tl := xdsIR[irKey].GetTCPListener(irListenerName(listener))
if tl == nil {
continue
}
for _, r := range tl.Routes {
if r == nil {
continue
}
// if already set - there's a specific level policy, so skip.
if r.Authorization != nil {
continue
}
// exact match only (TCP route names are flat in IR)
if r.Name != expectedTCPRouteName {
continue
}
// Only authorization for TCP
authCopy := *authorization
r.Authorization = &authCopy
}
}
case resource.KindHTTPRoute, resource.KindGRPCRoute:
for _, listener := range parentRefCtx.listeners {
irListener := xdsIR[irKey].GetHTTPListener(irListenerName(listener))
if irListener == nil {
continue
}
for _, r := range irListener.Routes {
if r == nil {
continue
}
// If specified the sectionName must match route rule from ir route metadata.
if target.SectionName != nil && string(*target.SectionName) != r.Metadata.SectionName {
continue
Expand Down Expand Up @@ -828,6 +910,42 @@ func (t *Translator) translateSecurityPolicyForGateway(
}
}
}

// Pre-create a TCP-only authorization object to avoid re-allocation
var tcpAuthorization *ir.Authorization
if authorization != nil {
authCopy := *authorization
tcpAuthorization = &authCopy
}

// Apply to TCP listeners (Authorization only). Support metadata nil fallback by parsing section name from listener name suffix.
if tcpAuthorization != nil {
for _, tl := range x.TCP {
if tl == nil || len(tl.Routes) == 0 {
continue
}
// TCPListener name has same format namespace/gatewayName/listenerName
gatewayNameEnd := strings.LastIndex(tl.Name, "/")
gatewayName := tl.Name[0:gatewayNameEnd]
if t.MergeGateways && gatewayName != policyTarget {
continue
}
// If specified the sectionName must match listenerName from ir listener metadata.
if target.SectionName != nil && string(*target.SectionName) != tl.Metadata.SectionName {
continue
}
// A Policy targeting the specific scope(xRoute rule, xRoute, Gateway listener) wins over a policy
// targeting a lesser specific scope(Gateway).
for _, r := range tl.Routes {
// if already set - there's a specific level policy, so skip.
if r.Authorization != nil {
continue
}
r.Authorization = tcpAuthorization
}
}
}

return errs
}

Expand Down
Loading
Loading