From 75460c5ea17048f51a1838b2665e62d74a256aae Mon Sep 17 00:00:00 2001 From: Caleb Doxsey Date: Mon, 16 Dec 2024 15:34:44 -0700 Subject: [PATCH] add support for udp routes --- go.mod | 12 ++++++------ go.sum | 24 ++++++++++++------------ model/ingress_config.go | 7 +++++++ pomerium/ingress_annotations.go | 1 + pomerium/ingress_to_route.go | 21 +++++++++++++++++++++ 5 files changed, 47 insertions(+), 18 deletions(-) diff --git a/go.mod b/go.mod index f04284d6..5f27b3d4 100644 --- a/go.mod +++ b/go.mod @@ -18,7 +18,7 @@ require ( 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.20241203161734-699679bc574c + 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 github.com/spf13/cobra v1.8.1 @@ -27,7 +27,7 @@ require ( github.com/stretchr/testify v1.10.0 go.uber.org/mock v0.5.0 go.uber.org/zap v1.27.0 - golang.org/x/sync v0.9.0 + golang.org/x/sync v0.10.0 google.golang.org/grpc v1.68.0 google.golang.org/protobuf v1.35.2 gopkg.in/yaml.v3 v3.0.1 @@ -210,14 +210,14 @@ require ( go.opentelemetry.io/otel/trace v1.32.0 // indirect go.uber.org/automaxprocs v1.6.0 // indirect go.uber.org/multierr v1.11.0 // indirect - golang.org/x/crypto v0.29.0 // indirect + golang.org/x/crypto v0.31.0 // indirect golang.org/x/exp v0.0.0-20240808152545-0cdaa3abc0fa // indirect golang.org/x/mod v0.21.0 // indirect golang.org/x/net v0.31.0 // indirect golang.org/x/oauth2 v0.24.0 // indirect - golang.org/x/sys v0.27.0 // indirect - golang.org/x/term v0.26.0 // indirect - golang.org/x/text v0.20.0 // indirect + golang.org/x/sys v0.28.0 // indirect + golang.org/x/term v0.27.0 // indirect + golang.org/x/text v0.21.0 // indirect golang.org/x/time v0.8.0 // indirect golang.org/x/tools v0.26.0 // indirect gomodules.xyz/jsonpatch/v2 v2.4.0 // indirect diff --git a/go.sum b/go.sum index 30102b8d..0dcd5a77 100644 --- a/go.sum +++ b/go.sum @@ -574,8 +574,8 @@ github.com/pomerium/csrf v1.7.0 h1:Qp4t6oyEod3svQtKfJZs589mdUTWKVf7q0PgCKYCshY= github.com/pomerium/csrf v1.7.0/go.mod h1:hAPZV47mEj2T9xFs+ysbum4l7SF1IdrryYaY6PdoIqw= github.com/pomerium/datasource v0.18.2-0.20221108160055-c6134b5ed524 h1:3YQY1sb54tEEbr0L73rjHkpLB0IB6qh3zl1+XQbMLis= github.com/pomerium/datasource v0.18.2-0.20221108160055-c6134b5ed524/go.mod h1:7fGbUYJnU8RcxZJvUvhukOIBv1G7LWDAHMfDxAf5+Y0= -github.com/pomerium/pomerium v0.28.1-0.20241203161734-699679bc574c h1:4zCQVk1u7LT+gwryAk0SKXzS0IIUaDc/6H5mWJ2f9zg= -github.com/pomerium/pomerium v0.28.1-0.20241203161734-699679bc574c/go.mod h1:inTl0u0Ei8dSTRlR4UUKuDsvzWxIHeelH3AnxrrvR04= +github.com/pomerium/pomerium v0.28.1-0.20241213191330-3d53f26d181c h1:4kRKaeiLnVors55G11vStzlo4UxnZGWKEygNcfJ4O/4= +github.com/pomerium/pomerium v0.28.1-0.20241213191330-3d53f26d181c/go.mod h1:G/CZJOfXst1ChkakyWhL2XeCckPV4jt+v9sup19BNjg= github.com/pomerium/protoutil v0.0.0-20240813175624-47b7ac43ff46 h1:NRTg8JOXCxcIA1lAgD74iYud0rbshbWOB3Ou4+Huil8= github.com/pomerium/protoutil v0.0.0-20240813175624-47b7ac43ff46/go.mod h1:QqZmx6ZgPxz18va7kqoT4t/0yJtP7YFIDiT/W2n2fZ4= github.com/pomerium/webauthn v0.0.0-20240603205124-0428df511172 h1:TqoPqRgXSHpn+tEJq6H72iCS5pv66j3rPprThUEZg0E= @@ -785,8 +785,8 @@ golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= -golang.org/x/crypto v0.29.0 h1:L5SG1JTTXupVV3n6sUqMTeWbjAyfPwoda2DLX8J8FrQ= -golang.org/x/crypto v0.29.0/go.mod h1:+F4F4N5hv6v38hfeYwTdx20oUvLLc+QfrE9Ax9HtgRg= +golang.org/x/crypto v0.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U= +golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= @@ -887,8 +887,8 @@ golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20220601150217-0de741cfad7f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.9.0 h1:fEo0HyrW1GIgZdpbhCRO0PkJajUS5H9IFUztCgEo2jQ= -golang.org/x/sync v0.9.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ= +golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -945,16 +945,16 @@ golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.27.0 h1:wBqf8DvsY9Y/2P8gAfPDEYNuS30J4lPHJxXSb/nJZ+s= -golang.org/x/sys v0.27.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA= +golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.3.0/go.mod h1:q750SLmJuPmVoN1blW3UFBPREJfb1KmY3vwxfr+nFDA= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk= -golang.org/x/term v0.26.0 h1:WEQa6V3Gja/BhNxg540hBip/kkaYtRg3cxg4oXSw4AU= -golang.org/x/term v0.26.0/go.mod h1:Si5m1o57C5nBNQo5z1iq+XDijt21BDBDp2bK0QI8e3E= +golang.org/x/term v0.27.0 h1:WP60Sv1nlK1T6SupCHbXzSaN0b9wUmsPoRS9b61A23Q= +golang.org/x/term v0.27.0/go.mod h1:iMsnZpn0cago0GOrHO2+Y7u7JPn5AylBrcoWkElMTSM= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -966,8 +966,8 @@ golang.org/x/text v0.5.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= -golang.org/x/text v0.20.0 h1:gK/Kv2otX8gz+wn7Rmb3vT96ZwuoxnQlY+HlJVj7Qug= -golang.org/x/text v0.20.0/go.mod h1:D4IsuqiFMhST5bX19pQ9ikHC2GsaKyk/oF+pn3ducp4= +golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo= +golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= 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" }