diff --git a/go.mod b/go.mod index e213f97d..5f27b3d4 100644 --- a/go.mod +++ b/go.mod @@ -17,6 +17,7 @@ require ( github.com/iancoleman/strcase v0.3.0 github.com/martinlindhe/base36 v1.1.1 github.com/open-policy-agent/opa v0.70.0 + github.com/pomerium/csrf v1.7.0 github.com/pomerium/pomerium v0.28.1-0.20241213191330-3d53f26d181c github.com/rs/zerolog v1.33.0 github.com/sergi/go-diff v1.3.1 @@ -164,7 +165,6 @@ require ( github.com/pkg/errors v0.9.1 // indirect github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 // indirect github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect - github.com/pomerium/csrf v1.7.0 // indirect github.com/pomerium/datasource v0.18.2-0.20221108160055-c6134b5ed524 // indirect github.com/pomerium/protoutil v0.0.0-20240813175624-47b7ac43ff46 // indirect github.com/pomerium/webauthn v0.0.0-20240603205124-0428df511172 // indirect diff --git a/model/ingress_config.go b/model/ingress_config.go index 6877b47b..2620fdfb 100644 --- a/model/ingress_config.go +++ b/model/ingress_config.go @@ -32,6 +32,8 @@ const ( UseServiceProxy = "service_proxy_upstream" // TCPUpstream indicates this route is a TCP service https://www.pomerium.com/docs/tcp/ TCPUpstream = "tcp_upstream" + // UDPUpstream indicates this route is a UDP service https://www.pomerium.com/docs/capabilities/udp/ + UDPUpstream = "udp_upstream" // SubtleAllowEmptyHost is a required annotation when creating an ingress containing // rules with an empty (catch-all) host, as it can cause unexpected behavior SubtleAllowEmptyHost = "subtle_allow_empty_host" @@ -121,6 +123,11 @@ func (ic *IngressConfig) IsTCPUpstream() bool { return ic.IsAnnotationSet(TCPUpstream) } +// IsUDPUpstream returns true is this route represents a UDP service https://www.pomerium.com/docs/capabilities/tcp/ +func (ic *IngressConfig) IsUDPUpstream() bool { + return ic.IsAnnotationSet(UDPUpstream) +} + // IsPathRegex returns true if paths in the Ingress spec should be treated as regular expressions func (ic *IngressConfig) IsPathRegex() bool { return ic.IsAnnotationSet(PathRegex) diff --git a/pomerium/ingress_annotations.go b/pomerium/ingress_annotations.go index b89cea59..1cbf3d7a 100644 --- a/pomerium/ingress_annotations.go +++ b/pomerium/ingress_annotations.go @@ -70,6 +70,7 @@ var ( model.PathRegex, model.SecureUpstream, model.TCPUpstream, + model.UDPUpstream, model.UseServiceProxy, model.SubtleAllowEmptyHost, }) diff --git a/pomerium/ingress_to_route.go b/pomerium/ingress_to_route.go index 1a51dd1b..bce69222 100644 --- a/pomerium/ingress_to_route.go +++ b/pomerium/ingress_to_route.go @@ -153,6 +153,16 @@ func setRoutePath(r *pb.Route, p networkingv1.HTTPIngressPath, ic *model.Ingress return nil } + if ic.IsUDPUpstream() { + if *p.PathType != networkingv1.PathTypeImplementationSpecific { + return fmt.Errorf("udp services must have %s path type", networkingv1.PathTypeImplementationSpecific) + } + if p.Path != "" { + return fmt.Errorf("udp services must not specify path, got %s", r.Path) + } + return nil + } + switch *p.PathType { case networkingv1.PathTypeImplementationSpecific: if ic.IsPathRegex() { @@ -187,6 +197,15 @@ func setRouteFrom(r *pb.Route, host string, p networkingv1.HTTPIngressPath, ic * u.Scheme = "tcp+https" } + if ic.IsUDPUpstream() { + _, _, port, err := getServiceFromPath(p, ic) + if err != nil { + return err + } + u.Host = net.JoinHostPort(u.Host, fmt.Sprint(port)) + u.Scheme = "udp+https" + } + r.From = u.String() return nil } @@ -261,6 +280,8 @@ func getPathServiceHosts(r *pb.Route, p networkingv1.HTTPIngressPath, ic *model. func getUpstreamScheme(ic *model.IngressConfig) string { if ic.IsTCPUpstream() { return "tcp" + } else if ic.IsUDPUpstream() { + return "udp" } else if ic.IsSecureUpstream() { return "https" }