Skip to content

Security

Introduction

Gateway API has been designed to enable granular authorization for each role in a typical organization.

Resources

Gateway API has 3 primary API resources:

  • GatewayClass defines a set of gateways with a common configuration and behavior.
  • Gateway requests a point where traffic can be translated to Services within the cluster.
  • Routes describe how traffic coming via the Gateway maps to the Services.

Roles and personas

There are 3 primary roles in Gateway API, as described in roles and personas:

  • Ian (he/him): Infrastructure Provider
  • Chihiro (they/them): Cluster Operator
  • Ana (she/her): Application Developer

RBAC

RBAC (role-based access control) is the standard used for Kubernetes authorization. This allows users to configure who can perform actions on resources in specific scopes. RBAC can be used to enable each of the roles defined above. In most cases, it will be desirable to have all resources be readable by most roles, so instead we'll focus on write access for this model.

Write Permissions for Simple 3 Tier Model

GatewayClass Gateway Route
Infrastructure Provider Yes Yes Yes
Cluster Operators No Yes Yes
Application Developers No No Yes

Write Permissions for Advanced 4 Tier Model

GatewayClass Gateway Route
Infrastructure Provider Yes Yes Yes
Cluster Operators Sometimes Yes Yes
Application Admins No In Specified Namespaces In Specified Namespaces
Application Developers No No In Specified Namespaces

Crossing Namespace Boundaries

Gateway API provides new ways to cross namespace boundaries. These cross-namespace capabilities are quite powerful but need to be used carefully to avoid accidental exposure. As a rule, every time we allow a namespace boundary to be crossed, we require a handshake between namespaces. There are 2 different ways that can occur:

1. Route Binding

Routes can be connected to Gateways in different namespaces. To accomplish this, The Gateway owner must explicitly allow Routes to bind from additional namespaces. This is accomplished by configuring allowedRoutes within a Gateway listener to look something like this:

namespaces:
  from: Selector
  selector:
    matchExpressions:
    - key: kubernetes.io/metadata.name
      operator: In
      values:
      - foo
      - bar

This will allow routes from the "foo" and "bar" namespaces to attach to this Gateway listener.

Risks of Other Labels

Although it's possible to use other labels with this selector, it is not quite as safe. While the kubernetes.io/metadata.name label is consistently set on namespaces to the name of the namespace, other labels do not have the same guarantee. If you used a custom label such as env, anyone that is able to label namespaces within your cluster would effectively be able to change the set of namespaces your Gateway supported.

2. ReferenceGrant

There are some cases where we allow other object references to cross namespace boundaries. This includes Gateways referencing Secrets and Routes referencing Backends (usually Services). In these cases, the required handshake is accomplished with a ReferenceGrant resource. This resource exists within a target namespace and can be used to allow references from other namespaces.

For example, the following ReferenceGrant allows references from HTTPRoutes in the "prod" namespace to Services that are deployed in the same namespace as the ReferenceGrant.

apiVersion: gateway.networking.k8s.io/v1beta1
kind: ReferenceGrant
metadata:
  name: allow-prod-traffic
spec:
  from:
  - group: gateway.networking.k8s.io
    kind: HTTPRoute
    namespace: prod
  to:
  - group: ""
    kind: Service

For more information on ReferenceGrant, refer to our detailed documentation for this resource.

Security considerations

Gateway controllers can be deployed in a multi-tenant environment, where different namespaces are used by different users and customers.

Some caution should be taken by the cluster administrators and Gateway owners to provide a safer environment.

Avoiding hostname/domain hijacking

On Gateway API, it is possible for distinct Routes and ListenerSets to claim the same hostname. Gateway controllers are responsible for the conflict resolution, that usually works on a first-come, first-served basis, where the first created resource wins in the conflict management.

The hostname definition on a Route or on ListenerSet is a list, and given the following scenario:

  • Gateway accepts routes from a set of namespaces
  • HTTPRoute with name route1 is created on namespace ns1, with creationTimestamp: 00:00:01 and defines hostname something.tld.
  • HTTPRoute with name route2 is created on namespace ns2, with creationTimestamp: 00:00:30 and defines hostname otherthing.tld.

If the owner of route1 adds later otherthing.tld to the list of hostnames, the route will be hijacked from route2 because route1 is older.

To avoid this situation, the following actions should be taken:

  • On Gateways, admins SHOULD ensure that hostnames are clearly delegated to a specific namespace or set of namespaces:
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
  name: gateway
spec:
  gatewayClassName: some-class
  listeners:
  - hostname: "something.tld"
    name: listener1
    port: 80
    protocol: HTTP
    allowedRoutes:
      namespaces:
        from: Selector
        selector:
          matchLabels:
            kubernetes.io/metadata.name: ns1
  - hostname: "otherthing.tld"
    name: listener2
    port: 80
    protocol: HTTP
    allowedRoutes:
      namespaces:
        from: Selector
        selector:
          matchLabels:
            kubernetes.io/metadata.name: ns2
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
  name: gateway
spec:
  gatewayClassName: some-class
  listeners:
  - name: listener1
    port: 80
    protocol: HTTP
    allowedRoutes:
      namespaces:
        from: All

More than 64 listeners

Gateway resource has a limitation of 64 listener entries. If you need more than 64 listeners, you should consider allowing your users to set their hostnames directly on the routes of each namespaces but limiting what namespace can use what hostname by relying on a mechanism like ValidatingAdmissionPolicy.

In case you opt to use (the still experimental) ListenerSet, a similar mechanism should also be considered to limit what hostnames a ListenerSet can claim.

Example of a ValidatingAdmissionPolicy

A ValidatingAdmissionPolicy can be used to add rules that limits what namespaces can use what domains.

Warning

The validation policy shown here IS AN EXAMPLE and the cluster-admin should do adjustments to their own environment! Do not copy/paste this example without proper adjustments

The policy exemplified here will:

  • Read the allowed domains from a comma-separated value of the annotation "domains" present on the namespace.
  • Validate if all of the hostnames within .spec.hostnames are contained on this annotation.
  • In case any of the entries are not authorized, the policy denies its admission.
apiVersion: admissionregistration.k8s.io/v1
kind: ValidatingAdmissionPolicy
metadata:
  name: httproute-hostname-policy
spec:
  failurePolicy: Fail
  matchConstraints:
    resourceRules:
    - apiGroups: ["gateway.networking.k8s.io"]
      apiVersions: ["v1", "v1beta1"]
      operations: ["CREATE", "UPDATE"]
      resources: ["httproutes"]
  variables:
  - name: allowed_hostnames_str
    expression: |
      has(namespaceObject.metadata.annotations) && 
      has(namespaceObject.metadata.annotations.domains) ?
      namespaceObject.metadata.annotations['domains'] : '' 
  - name: allowed_hostnames_list
    expression: |
      variables.allowed_hostnames_str.split(',').
      map(h, h.trim()).filter(h, size(h) > 0)
  validations:
  - expression: |
      !has(object.spec.hostnames) || 
      size(object.spec.hostnames) == 0 || 
      object.spec.hostnames.all(hostname, hostname in variables.allowed_hostnames_list)
    message: "HTTPRoute validation failed. It contains unauthorized hostnames"
---
apiVersion: admissionregistration.k8s.io/v1
kind: ValidatingAdmissionPolicyBinding
metadata:
  name: httproute-hostname-binding
spec:
  policyName: httproute-hostname-policy
  validationActions: ["Deny"]
  matchResources:
    namespaceSelector: {}

Once the policy is created, the cluster-admin should explicitly allow the usage of domains with a command like kubectl annotate ns default domains=www.dom1.tld,www.dom2.tld

Additionally, when dealing with environments that provide DNS record creations, admins should be aware and limit the DNS creation based on the same constraints above.

Limiting Cross-Namespace References

Owners of resources should be aware of the usage of ReferenceGrants.

ReferenceGrant allows resource owners to make their resources available to Gateway API resources in other namespaces. It may be beneficial to restrict where this can be done.

A ValidatingAdmissionPolicy can be used to limit what kind of resource and which namespace can create a ReferenceGrant.

Below is an EXAMPLE that can be used to limit the usage of a ReferenceGrant just on namespaces labeled with referencegrants=allow and only allowing objects of kind HTTPRoute to reference an object of kind Service that MUST have a name:

apiVersion: admissionregistration.k8s.io/v1
kind: ValidatingAdmissionPolicy
metadata:
  name: reference-grant-limit
spec:
  failurePolicy: Fail
  matchConstraints:
    resourceRules:
    - apiGroups: ["gateway.networking.k8s.io"]
      apiVersions: ["v1beta1"]
      operations: ["CREATE", "UPDATE"]
      resources: ["referencegrants"]
  variables:
  - name: allowed_grant_ns
    expression: |
      has(namespaceObject.metadata.labels) && 
      has(namespaceObject.metadata.labels.referencegrants) &&
      namespaceObject.metadata.labels['referencegrants'] == 'allow' 
  - name: allowed_from_kind
    expression: |
      object.spec.from.all(f, f.kind=='HTTPRoute')
  - name: allowed_to_kind
    expression: |
      object.spec.to.all(t, t.kind == 'Service' && has(t.name) && t.name != '')
  validations:
  - expression: |
      variables.allowed_grant_ns && variables.allowed_from_kind && variables.allowed_to_kind
    message: "ReferenceGrant must be explicitly allowed on the namespace, from an HTTPRoute to a named service"
---
apiVersion: admissionregistration.k8s.io/v1
kind: ValidatingAdmissionPolicyBinding
metadata:
  name: reference-grant-limit
spec:
  policyName: reference-grant-limit
  validationActions: ["Deny"]
  matchResources:
    namespaceSelector: {}

ReferenceGrant owners should ensure that the reference permissions being granted are as minimal as possible.

  • Specify to targets in all possible ways (group, kind, and name)
  • In particular, DO NOT leave name unspecified, even though it is optional, without a very good reason, as that is granting a blanket

Proper definition of Roles and RoleBinding

The creation of a new Gateway should be considered as a privileged permission. The unguarded creation of Gateways may increase costs, infrastructure modifications (like a LoadBalancer and a DNS record creation) and as in such case, admins should be aware of it and create Roles and RoleBindings that reflect proper user permissions.

Additionally, it is highly recommended that the strict permissions are given, not allowing regular users to modify a Gateway API status.

Usage and limit of GatewayClass

A cluster may have different GatewayClasses, with different purposes. As an example, one GatewayClass may enforce that Gateways attached to it can only use internal load balancers.

Cluster admins should be aware of these requirements, and define validation policies that limit the improper attachment of a Gateway to a GatewayClass by unauthorized users.

A ValidatingAdmissionPolicy can be used to limit what namespaces can use a GatewayClass.