Skip to content

Commit

Permalink
internal/dag: Implement GatewayAllow for TLSRoutes
Browse files Browse the repository at this point in the history
Fixes #3695

Signed-off-by: Steve Sloka <slokas@vmware.com>
  • Loading branch information
stevesloka committed May 27, 2021
1 parent 287b9ae commit 88475bf
Show file tree
Hide file tree
Showing 3 changed files with 282 additions and 22 deletions.
252 changes: 250 additions & 2 deletions internal/dag/builder_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,25 @@ func TestDAGInsertGatewayAPI(t *testing.T) {
},
}

gatewayTLSRouteNoSelector := &gatewayapi_v1alpha1.Gateway{
ObjectMeta: metav1.ObjectMeta{
Name: "contour",
Namespace: "projectcontour",
},
Spec: gatewayapi_v1alpha1.GatewaySpec{
Listeners: []gatewayapi_v1alpha1.Listener{{
Port: 80,
Protocol: gatewayapi_v1alpha1.HTTPProtocolType,
Routes: gatewayapi_v1alpha1.RouteBindingSelector{
Kind: KindTLSRoute,
Namespaces: &gatewayapi_v1alpha1.RouteNamespaces{
From: routeSelectTypePtr(gatewayapi_v1alpha1.RouteSelectAll),
},
},
}},
},
}

gatewayWithSameNamespace := &gatewayapi_v1alpha1.Gateway{
ObjectMeta: metav1.ObjectMeta{
Name: "contour",
Expand Down Expand Up @@ -646,7 +665,7 @@ func TestDAGInsertGatewayAPI(t *testing.T) {
},
// HTTPRoute is in a different namespace than the Gateway,
// but will be allowed since "All" is set.
"RouteGateways with GatewayAllowType: All": {
"HTTPRoute: RouteGateways with GatewayAllowType: All": {
gateway: gatewayNoSelector,
objs: []interface{}{
&v1.Namespace{
Expand Down Expand Up @@ -736,7 +755,7 @@ func TestDAGInsertGatewayAPI(t *testing.T) {
},
Spec: gatewayapi_v1alpha1.HTTPRouteSpec{
Gateways: &gatewayapi_v1alpha1.RouteGateways{
Allow: gatewayAllowTypePtr(gatewayapi_v1alpha1.GatewayAllowAll),
Allow: gatewayAllowTypePtr(gatewayapi_v1alpha1.GatewayAllowSameNamespace),
},
Hostnames: []gatewayapi_v1alpha1.Hostname{
"test.projectcontour.io",
Expand Down Expand Up @@ -844,6 +863,235 @@ func TestDAGInsertGatewayAPI(t *testing.T) {
},
want: listeners(),
},
// TLSRoute is in a different namespace than the Gateway,
// but will be allowed since "All" is set.
"TLSRoute: RouteGateways with GatewayAllowType: All": {
gateway: gatewayTLSRouteNoSelector,
objs: []interface{}{
&v1.Namespace{
ObjectMeta: metav1.ObjectMeta{
Name: "custom",
Labels: map[string]string{
"app": "contour",
"type": "controller",
},
},
},
kuardServiceCustomNs,
&gatewayapi_v1alpha1.TLSRoute{
ObjectMeta: metav1.ObjectMeta{
Name: "basic",
Namespace: "custom",
},
Spec: gatewayapi_v1alpha1.TLSRouteSpec{
Gateways: &gatewayapi_v1alpha1.RouteGateways{
Allow: gatewayAllowTypePtr(gatewayapi_v1alpha1.GatewayAllowAll),
},
Rules: []gatewayapi_v1alpha1.TLSRouteRule{{
Matches: []gatewayapi_v1alpha1.TLSRouteMatch{{
SNIs: []gatewayapi_v1alpha1.Hostname{
"test.projectcontour.io",
},
}},
ForwardTo: tcpRouteForwardTo("kuard", 8080, 0),
}},
},
},
},
want: listeners(
&Listener{
Port: 443,
VirtualHosts: virtualhosts(
&SecureVirtualHost{
VirtualHost: VirtualHost{
Name: "test.projectcontour.io",
ListenerName: "ingress_https",
},
TCPProxy: &TCPProxy{
Clusters: clusters(service(kuardServiceCustomNs)),
},
},
),
},
),
},
// TLSRoute is in a different namespace than the Gateway,
// and is rejected since "SameNamespace" is set.
"TLSRoute doesn't match with RouteGateways.GatewayAllowType: SameNamespace": {
gateway: gatewayNoSelector,
objs: []interface{}{
&v1.Namespace{
ObjectMeta: metav1.ObjectMeta{
Name: "custom",
Labels: map[string]string{
"app": "contour",
"type": "controller",
},
},
},
kuardServiceCustomNs,
&gatewayapi_v1alpha1.TLSRoute{
ObjectMeta: metav1.ObjectMeta{
Name: "basic",
Namespace: "custom",
},
Spec: gatewayapi_v1alpha1.TLSRouteSpec{
Gateways: &gatewayapi_v1alpha1.RouteGateways{
Allow: gatewayAllowTypePtr(gatewayapi_v1alpha1.GatewayAllowSameNamespace),
},
Rules: []gatewayapi_v1alpha1.TLSRouteRule{{
Matches: []gatewayapi_v1alpha1.TLSRouteMatch{{
SNIs: []gatewayapi_v1alpha1.Hostname{
"test.projectcontour.io",
},
}},
ForwardTo: tcpRouteForwardTo("kuard", 8080, 0),
}},
},
},
},
want: listeners(),
},
// TLSRoute is in the same namespace of the Gateway,
// and is allowed since "SameNamespace" is set.
"TLSRoute matches with RouteGateways.GatewayAllowType: SameNamespace": {
gateway: gatewayTLSRouteNoSelector,
objs: []interface{}{
kuardService,
&gatewayapi_v1alpha1.TLSRoute{
ObjectMeta: metav1.ObjectMeta{
Name: "basic",
Namespace: "projectcontour",
},
Spec: gatewayapi_v1alpha1.TLSRouteSpec{
Gateways: &gatewayapi_v1alpha1.RouteGateways{
Allow: gatewayAllowTypePtr(gatewayapi_v1alpha1.GatewayAllowSameNamespace),
},
Rules: []gatewayapi_v1alpha1.TLSRouteRule{{
Matches: []gatewayapi_v1alpha1.TLSRouteMatch{{
SNIs: []gatewayapi_v1alpha1.Hostname{
"test.projectcontour.io",
},
}},
ForwardTo: tcpRouteForwardTo("kuard", 8080, 0),
}},
},
},
},
want: listeners(
&Listener{
Port: 443,
VirtualHosts: virtualhosts(
&SecureVirtualHost{
VirtualHost: VirtualHost{
Name: "test.projectcontour.io",
ListenerName: "ingress_https",
},
TCPProxy: &TCPProxy{
Clusters: clusters(service(kuardService)),
},
},
),
},
),
},
// TLSRoute references same Gateway is configured with
// in the FromList.
"TLSRoute matches with RouteGateways.GatewayAllowType: FromList": {
gateway: gatewayTLSRouteNoSelector,
objs: []interface{}{
&v1.Namespace{
ObjectMeta: metav1.ObjectMeta{
Name: "custom",
Labels: map[string]string{
"app": "contour",
"type": "controller",
},
},
},
kuardServiceCustomNs,
&gatewayapi_v1alpha1.TLSRoute{
ObjectMeta: metav1.ObjectMeta{
Name: "basic",
Namespace: "custom",
},
Spec: gatewayapi_v1alpha1.TLSRouteSpec{
Gateways: &gatewayapi_v1alpha1.RouteGateways{
Allow: gatewayAllowTypePtr(gatewayapi_v1alpha1.GatewayAllowFromList),
GatewayRefs: []gatewayapi_v1alpha1.GatewayReference{{
Name: "contour",
Namespace: "projectcontour",
}},
},
Rules: []gatewayapi_v1alpha1.TLSRouteRule{{
Matches: []gatewayapi_v1alpha1.TLSRouteMatch{{
SNIs: []gatewayapi_v1alpha1.Hostname{
"test.projectcontour.io",
},
}},
ForwardTo: tcpRouteForwardTo("kuard", 8080, 0),
}},
},
},
},
want: listeners(
&Listener{
Port: 443,
VirtualHosts: virtualhosts(
&SecureVirtualHost{
VirtualHost: VirtualHost{
Name: "test.projectcontour.io",
ListenerName: "ingress_https",
},
TCPProxy: &TCPProxy{
Clusters: clusters(service(kuardServiceCustomNs)),
},
},
),
},
),
},
// TLSRoute references different Gateway is configured with
// in the FromList.
"TLSRoute doesn't match with RouteGateways.GatewayAllowType: FromList": {
gateway: gatewayNoSelector,
objs: []interface{}{
&v1.Namespace{
ObjectMeta: metav1.ObjectMeta{
Name: "custom",
Labels: map[string]string{
"app": "contour",
"type": "controller",
},
},
},
kuardServiceCustomNs,
&gatewayapi_v1alpha1.TLSRoute{
ObjectMeta: metav1.ObjectMeta{
Name: "basic",
Namespace: "custom",
},
Spec: gatewayapi_v1alpha1.TLSRouteSpec{
Gateways: &gatewayapi_v1alpha1.RouteGateways{
Allow: gatewayAllowTypePtr(gatewayapi_v1alpha1.GatewayAllowFromList),
GatewayRefs: []gatewayapi_v1alpha1.GatewayReference{{
Name: "wrong",
Namespace: "reference",
}},
},
Rules: []gatewayapi_v1alpha1.TLSRouteRule{{
Matches: []gatewayapi_v1alpha1.TLSRouteMatch{{
SNIs: []gatewayapi_v1alpha1.Hostname{
"test.projectcontour.io",
},
}},
ForwardTo: tcpRouteForwardTo("kuard", 8080, 0),
}},
},
},
},
want: listeners(),
},
// Issue: https://github.com/projectcontour/contour/issues/3591
"one gateway with two httproutes, different hostnames": {
gateway: gatewayWithNamespaceSelector,
Expand Down
25 changes: 18 additions & 7 deletions internal/dag/gatewayapi_processor.go
Original file line number Diff line number Diff line change
Expand Up @@ -142,8 +142,7 @@ func (p *GatewayAPIProcessor) Run(dag *DAG, source *KubernetesCache) {
// the route to the set of matchingRoutes.
if selMatches && nsMatches {

gatewayAllowMatches := p.gatewayMatches(route.Spec.Gateways, route.Namespace)
if (listener.Routes.Selector != nil || listener.Routes.Namespaces != nil) && !gatewayAllowMatches {
if !p.gatewayMatches(route.Spec.Gateways, route.Namespace) {

// If a label selector or namespace selector matches, but the gateway Allow doesn't
// then set the "Admitted: false" for the route.
Expand All @@ -153,10 +152,8 @@ func (p *GatewayAPIProcessor) Run(dag *DAG, source *KubernetesCache) {
continue
}

if gatewayAllowMatches {
// Empty Selector matches all routes.
matchingHTTPRoutes = append(matchingHTTPRoutes, route)
}
// Empty Selector matches all routes.
matchingHTTPRoutes = append(matchingHTTPRoutes, route)
}
}
case KindTLSRoute:
Expand All @@ -182,6 +179,17 @@ func (p *GatewayAPIProcessor) Run(dag *DAG, source *KubernetesCache) {
}

if selMatches && nsMatches {

if !p.gatewayMatches(route.Spec.Gateways, route.Namespace) {

// If a label selector or namespace selector matches, but the gateway Allow doesn't
// then set the "Admitted: false" for the route.
routeAccessor, commit := p.dag.StatusCache.ConditionsAccessor(k8s.NamespacedNameOf(route), route.Generation, status.ResourceTLSRoute, route.Status.Gateways)
routeAccessor.AddCondition(gatewayapi_v1alpha1.ConditionRouteAdmitted, metav1.ConditionFalse, status.ReasonGatewayAllowMismatch, "Gateway RouteSelector matches, but GatewayAllow has mismatch.")
commit()
continue
}

// Empty Selector matches all routes.
matchingTLSRoutes = append(matchingTLSRoutes, route)
}
Expand Down Expand Up @@ -321,6 +329,10 @@ func (p *GatewayAPIProcessor) namespaceMatches(namespaces *gatewayapi_v1alpha1.R
// matches one from the list.
func (p *GatewayAPIProcessor) gatewayMatches(routeGateways *gatewayapi_v1alpha1.RouteGateways, namespace string) bool {

if routeGateways == nil || routeGateways.Allow == nil {
return true
}

switch *routeGateways.Allow {
case gatewayapi_v1alpha1.GatewayAllowAll:
return true
Expand All @@ -333,7 +345,6 @@ func (p *GatewayAPIProcessor) gatewayMatches(routeGateways *gatewayapi_v1alpha1.
case gatewayapi_v1alpha1.GatewayAllowSameNamespace:
return p.source.ConfiguredGateway.Namespace == namespace
}

return false
}

Expand Down
27 changes: 14 additions & 13 deletions test/e2e/gateway/008_tlsroute_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,23 +43,24 @@ func testTLSRoute(fx *e2e.Framework) {
Name: "tls-route-1",
},
Spec: gatewayv1alpha1.TLSRouteSpec{
Rules: []gatewayv1alpha1.TLSRouteRule{
{
Matches: []gatewayv1alpha1.TLSRouteMatch{
{
SNIs: []gatewayv1alpha1.Hostname{
gatewayv1alpha1.Hostname("tlsroute.gatewayapi.projectcontour.io"),
},
Gateways: &gatewayv1alpha1.RouteGateways{
Allow: gatewayAllowTypePtr(gatewayv1alpha1.GatewayAllowAll),
},
Rules: []gatewayv1alpha1.TLSRouteRule{{
Matches: []gatewayv1alpha1.TLSRouteMatch{
{
SNIs: []gatewayv1alpha1.Hostname{
gatewayv1alpha1.Hostname("tlsroute.gatewayapi.projectcontour.io"),
},
},
ForwardTo: []gatewayv1alpha1.RouteForwardTo{
{
ServiceName: stringPtr("echo"),
Port: portNumPtr(443),
},
},
ForwardTo: []gatewayv1alpha1.RouteForwardTo{
{
ServiceName: stringPtr("echo"),
Port: portNumPtr(443),
},
},
},
}},
},
}
fx.CreateTLSRouteAndWaitFor(route, tlsRouteAdmitted)
Expand Down

0 comments on commit 88475bf

Please sign in to comment.