-
Notifications
You must be signed in to change notification settings - Fork 820
feat(translator): Security policies for TLS gateways #9464
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: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -506,9 +506,9 @@ func (t *Translator) processSecurityPolicyForRoute( | |
| // 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 currTarget.Kind == resource.KindTCPRoute || currTarget.Kind == resource.KindTLSRoute { | ||
| validator = validateSecurityPolicyForTCPAndTLS | ||
| errMsg = "invalid SecurityPolicy for TCP/TLS route" | ||
| } | ||
| if err := validator(policy); err != nil { | ||
| status.SetTranslationErrorForPolicyAncestors(&policy.Status, | ||
|
|
@@ -938,36 +938,36 @@ func validateSecurityPolicy(p *egv1a1.SecurityPolicy) error { | |
| return nil | ||
| } | ||
|
|
||
| // validateSecurityPolicyForTCP ensures SecurityPolicy usage on TCP is compatible. | ||
| // validateSecurityPolicyForTCPAndTLS ensures SecurityPolicy usage on TCP/TLS is compatible. | ||
| // | ||
| // TCP supports Authorization with ClientCIDRs ONLY. | ||
| // TCP and TLS support 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. | ||
| // - Empty/no Authorization is allowed and results in no-op on TCP/TLS. | ||
| // Returns an error when any HTTP-only field is present or CIDRs are invalid. | ||
| func validateSecurityPolicyForTCP(p *egv1a1.SecurityPolicy) error { | ||
| func validateSecurityPolicyForTCPAndTLS(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)") | ||
| return fmt.Errorf("only authorization is supported for TCP/TLS (routes/listeners)") | ||
| } | ||
| if p.Spec.Authorization == nil || len(p.Spec.Authorization.Rules) == 0 { | ||
| return nil | ||
| } | ||
| for i := range p.Spec.Authorization.Rules { | ||
| rule := &p.Spec.Authorization.Rules[i] | ||
| if rule.CEL != nil { | ||
| return fmt.Errorf("rule %d: CEL not supported for TCP", i) | ||
| return fmt.Errorf("rule %d: CEL not supported for TCP/TLS", i) | ||
| } | ||
| if rule.Principal == nil { | ||
| continue | ||
| } | ||
| if rule.Principal.JWT != nil { | ||
| return fmt.Errorf("rule %d: JWT not supported for TCP", i) | ||
| return fmt.Errorf("rule %d: JWT not supported for TCP/TLS", i) | ||
| } | ||
| if len(rule.Principal.Headers) > 0 { | ||
| return fmt.Errorf("rule %d: headers not supported for TCP", i) | ||
| return fmt.Errorf("rule %d: headers not supported for TCP/TLS", i) | ||
|
asHasnain marked this conversation as resolved.
|
||
| } | ||
| if len(rule.Principal.ClientIPGeoLocations) > 0 { | ||
| return fmt.Errorf("rule %d: clientIPGeoLocations not supported for TCP", i) | ||
| return fmt.Errorf("rule %d: clientIPGeoLocations not supported for TCP/TLS", i) | ||
| } | ||
| if err := validateCIDRs(rule.Principal.ClientCIDRs); err != nil { | ||
| return fmt.Errorf("rule %d: %w", i, err) | ||
|
|
@@ -1342,16 +1342,26 @@ func (t *Translator) translateSecurityPolicyForRoute( | |
|
|
||
| irKey := t.getIRKey(gtwCtx.Gateway) | ||
| switch route.GetRouteType() { | ||
| case resource.KindTCPRoute: | ||
| case resource.KindTCPRoute, resource.KindTLSRoute: | ||
|
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.
Adding TLSRoute to this branch makes Useful? React with 👍 / 👎. |
||
| for _, listener := range parentRefCtx.listeners { | ||
| // If targetListener is set, only apply to that exact listener. | ||
| if targetListener != nil && targetListenerName != irListenerName(listener) { | ||
| continue | ||
| } | ||
| tl := xdsIR[irKey].GetTCPListener(irListenerName(listener)) | ||
|
|
||
| for _, r := range tl.Routes { | ||
| // For TLSRoute: filter by route metadata to handle multiple routes per listener | ||
| if target.Kind == resource.KindTLSRoute { | ||
| if r.Metadata == nil || | ||
| r.Metadata.Namespace != string(target.Namespace) || | ||
| r.Metadata.Name != string(target.Name) { | ||
| continue | ||
| } | ||
| } | ||
|
|
||
| // If target.SectionName is specified it must match the route-rule section name | ||
| // in the IR. For HTTP/GRPC routes this is r.Metadata.SectionName; for TCP | ||
| // in the IR. For HTTP/GRPC routes this is r.Metadata.SectionName; for TCP/TLS | ||
| // routes the section name is currently stored on r.Destination.Metadata.SectionName. | ||
| if target.SectionName != nil && string(*target.SectionName) != r.Destination.Metadata.SectionName { | ||
| continue | ||
|
|
@@ -1360,7 +1370,7 @@ func (t *Translator) translateSecurityPolicyForRoute( | |
| if r.Authorization != nil { | ||
| continue | ||
| } | ||
| // Only authorization for TCP | ||
| // Only authorization for TCP/TLS | ||
| if authorization != nil { | ||
| authCopy := *authorization | ||
| r.Authorization = &authCopy | ||
|
|
@@ -1708,7 +1718,8 @@ func (t *Translator) translateSecurityPolicyForListeners( | |
| tcpAuthorization = &authCopy | ||
| } | ||
|
|
||
| // Apply to TCP listeners (Authorization only). | ||
| // Apply to TCP/TLS listeners (Authorization only). | ||
| // Both TCPRoute and TLSRoute translate to TCP listeners in the IR. | ||
| if tcpAuthorization != nil { | ||
| for _, tl := range x.TCP { | ||
| if tl == nil || len(tl.Routes) == 0 { | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,87 @@ | ||
| gateways: | ||
| - apiVersion: gateway.networking.k8s.io/v1 | ||
| kind: Gateway | ||
| metadata: | ||
| namespace: envoy-gateway | ||
| name: gateway-tls | ||
| spec: | ||
| gatewayClassName: envoy-gateway-class | ||
| listeners: | ||
| - name: tls-passthrough | ||
| protocol: TLS | ||
| port: 8443 | ||
| tls: | ||
| mode: Passthrough | ||
| allowedRoutes: | ||
| namespaces: | ||
| from: All | ||
| tlsRoutes: | ||
| - apiVersion: gateway.networking.k8s.io/v1alpha2 | ||
| kind: TLSRoute | ||
| metadata: | ||
| namespace: default | ||
| name: tlsroute-a | ||
| spec: | ||
| parentRefs: | ||
| - namespace: envoy-gateway | ||
| name: gateway-tls | ||
| sectionName: tls-passthrough | ||
| hostnames: | ||
| - "a.example.com" | ||
| rules: | ||
| - backendRefs: | ||
| - name: service-1 | ||
| port: 8443 | ||
| - apiVersion: gateway.networking.k8s.io/v1alpha2 | ||
| kind: TLSRoute | ||
| metadata: | ||
| namespace: default | ||
| name: tlsroute-b | ||
| spec: | ||
| parentRefs: | ||
| - namespace: envoy-gateway | ||
| name: gateway-tls | ||
| sectionName: tls-passthrough | ||
| hostnames: | ||
| - "b.example.com" | ||
| rules: | ||
| - backendRefs: | ||
| - name: service-2 | ||
| port: 8443 | ||
| securityPolicies: | ||
| - apiVersion: gateway.envoyproxy.io/v1alpha1 | ||
| kind: SecurityPolicy | ||
| metadata: | ||
| namespace: default | ||
| name: sp-tlsroute-a | ||
| spec: | ||
| targetRef: | ||
| group: gateway.networking.k8s.io | ||
| kind: TLSRoute | ||
| name: tlsroute-a | ||
| authorization: | ||
| defaultAction: Deny | ||
| rules: | ||
| - action: Allow | ||
| name: allow-route-a | ||
| principal: | ||
| clientCIDRs: | ||
| - 192.168.0.0/16 | ||
| - apiVersion: gateway.envoyproxy.io/v1alpha1 | ||
| kind: SecurityPolicy | ||
| metadata: | ||
| namespace: default | ||
| name: sp-tlsroute-b | ||
| spec: | ||
| targetRef: | ||
| group: gateway.networking.k8s.io | ||
| kind: TLSRoute | ||
| name: tlsroute-b | ||
| authorization: | ||
| defaultAction: Deny | ||
| rules: | ||
| - action: Allow | ||
| name: allow-route-b | ||
| principal: | ||
| clientCIDRs: | ||
| - 10.0.0.0/8 |
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.
Can you please add a release note as well for this new feautre?
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.
A release note is added.