Administration

Secure your Gateway API deployment

To ensure everyone in the organization is following your recommended deployment patterns, you can use the Kubernetes built-in RBAC features to restrict who is allowed to add GatewayClasses and Gateways, and limit the namespaces where each GatewayClass can be used. In the sections that follow we’ll restrict access and allow only specific access at different levels, separated by responsibility. We’ll use ClusterRoles and ClusterRoleBindings for cluster-level resources and Roles and RoleBindings for namespace-scoped resources.

Following the official guidelines for Gateway API usage, we’ll implement trust boundaries for three personas:

Persona Description
Cluster admin
  • This could be an infra team or a platform team.
  • This persona will manage the infrastructure for all clusters.
  • They’ll manage GatewayClass or what kinds of gateways exist.
  • If you are using a cloud provider, the responsibilities of this role may fall to your cloud provider. In this case, your cluster operators, those within your company who manage day-to-day operations of the cluster, may have reduced permissions in cloud-managed clusters.
  • If you are running on-premises or otherwise managing the physical infrastructure of your cluster within your organization, this persona will need cluster admin permissions that allow cluster-wide access to resources. We will explain how to configure this for those super-users.
Cluster operator
  • This could be a ClusterOps, DevOps, or SRE team.
  • This persona will manage operations within a cluster or several clusters.
  • They’ll manage Gateways, ReferenceGrants, Namespaces, Roles (ClusterRoles), RoleBindings (ClusterRoleBindings), TLS Secrets, NetworkPolicies, and other networking configurations.
Application developer
  • This could be a developer team or other application owners.
  • This persona will deploy and use resources only in specific, designated namespaces.
  • They’ll manage their own Routes and resources related to their applications in their designated namespace.

In the examples that follow, we’ll implement the resources and policies to effectively enforce a separation of concerns for these personas, including two separate developer teams: Dev Team A and Dev Team B.

Cluster admin permissions Jump to heading

Cluster admnins can manage all aspects of a cluster.

To effectively deploy, configure, and manage an HAProxy Unified Gateway deployment, cluster admins need access to a number of resource types. You can build custom Roles or ClusterRoles (for cluster-scoped resources) in their entirety that include these permissions, or you can extend built-in roles with additional permissions where required. The cluster-admin role, which is essentially root on the cluster, provides all of these permissions, but use it with caution. We’ll help you get started by showing you how to use the default cluster-admin ClusterRole, as well as showing you how to build a custom ClusterRole upon the default admin ClusterRole.

User and group management

Permissions can be defined per-user or per-group. Kubernetes itself doesn’t manage users and groups. You must either use a third-party solution for authentication and authorization or manage your users’ certificates. If you are managing the certificates your users use to acccess the cluster, the username is defined by the CN (Common Name) attribute and groups are defined by O (Organization) certificate attributes. For more information, see Authenticating in the official Kubernetes documentation.

Use the built-in cluster-admin role Jump to heading

Cluster admins, which could be members of an infra or platform team managing on-premises clusters, will need elevated permissions to be able to carry out their duties. You can use the cluster-admin default ClusterRole, which allows for full access within the cluster, which is effectively root access on the cluster.

Caution

Exercise caution when assigning the cluster-admin ClusterRole. It enables open access to everything in the cluster for users or groups to which it’s bound. Only a handful of infrastructure or platform team members should have these permissions. In some cases, and depending on the security policies of your organization, it may be better to create a custom role (or set of roles separated by responsibility) that requires only the minimum amount of access.

Info

This example assumes that you’ve configured users for your cluster and have designated that they belong to the cluster-admin group. If you are managing the certificates your users use to acccess the cluster, groups are defined by O (Organization) certificate attributes.

To assign the cluster-admin ClusterRole to the group named cluster-admins, thus enabling users of that group to have root access to the cluster:

  1. Create a ClusterRoleBinding named cluster-admins-access that will bind the default cluster-admin ClusterRole to each user listed in subjects. Here we use the group cluster-admins, which will apply the ClusterRole to all users in that group.

    cluster-admins-access.yaml
    yaml
    apiVersion: rbac.authorization.k8s.io/v1
    kind: ClusterRoleBinding
    metadata:
    name: cluster-admins-access
    subjects:
    - kind: Group
    name: cluster-admins
    apiGroup: rbac.authorization.k8s.io
    roleRef:
    kind: ClusterRole
    name: cluster-admin
    apiGroup: rbac.authorization.k8s.io
    cluster-admins-access.yaml
    yaml
    apiVersion: rbac.authorization.k8s.io/v1
    kind: ClusterRoleBinding
    metadata:
    name: cluster-admins-access
    subjects:
    - kind: Group
    name: cluster-admins
    apiGroup: rbac.authorization.k8s.io
    roleRef:
    kind: ClusterRole
    name: cluster-admin
    apiGroup: rbac.authorization.k8s.io

    Here we use roleRef to specify the ClusterRole we are binding is the cluster-admin role.

  2. Use kubectl to create the RoleBinding:

    nix
    kubectl apply -f cluster-admins-access.yaml
    nix
    kubectl apply -f cluster-admins-access.yaml
    output
    text
    clusterrolebinding.rbac.authorization.k8s.io/cluster-admins-access created
    output
    text
    clusterrolebinding.rbac.authorization.k8s.io/cluster-admins-access created

All users of the cluster-admins group now have full access to the cluster. For more information about the cluster-admin role, see User-facing roles in the official Kubernetes documentation.

Create a customized cluster admin Jump to heading

While the built-in ClusterRole cluster-admin gives full permissions to all resources across the cluster, it’s more secure to use a more restrictive policy, following the principles of least privilege. To deploy and manage HAProxy Unified Gateway and to manage Gateway API-related resources, you can create your own cluster admin ClusterRole that allows access to the minimum amount of resources. Note that the custom ClusterRole we create in this example is more strict that the default cluster-admin role. You may need to add additional permission to it depending on your setup. We’ll use the default admin ClusterRole as the starting point for our custom ClusterRole.

To add permissions to the cluster-admins group:

  1. Create a ClusterRoleBinding named cluster-admins-access that will bind the default admin ClusterRole to each user listed in subjects. The admin ClusterRole includes the following permissions, among others, and can be applied as either a ClusterRole (cluster-wide) or as a Role (per-namespace).

    • Pods, Deployments, Daemonsets, Statefulsets, and Replicasets
    • Jobs, Cronjobs
    • Services, Endpoints
    • Configmaps, Secrets
    • Ingress, NetworkPolicies
    • ServiceAccounts (including impersonate ability for kubectl auth can-i requests)
    • Events
    • Roles / RoleBindings
    • Leases
    • EndpointSlices

    Here we use the group cluster-admins, which will apply the ClusterRole to all users in that group.

    cluster-admins-access.yaml
    yaml
    apiVersion: rbac.authorization.k8s.io/v1
    kind: ClusterRoleBinding
    metadata:
    name: cluster-admins-access
    subjects:
    - kind: Group
    name: cluster-admins
    apiGroup: rbac.authorization.k8s.io
    roleRef:
    kind: ClusterRole
    name: admin
    apiGroup: rbac.authorization.k8s.io
    cluster-admins-access.yaml
    yaml
    apiVersion: rbac.authorization.k8s.io/v1
    kind: ClusterRoleBinding
    metadata:
    name: cluster-admins-access
    subjects:
    - kind: Group
    name: cluster-admins
    apiGroup: rbac.authorization.k8s.io
    roleRef:
    kind: ClusterRole
    name: admin
    apiGroup: rbac.authorization.k8s.io

    Since we used a ClusterRoleBinding, the ClusterRole applies cluster-wide. We used roleRef to specify the ClusterRole we are binding is the admin role.

    For more information about the default admin ClusterRole, see User-facing roles in the official Kubernetes documentation. Note that if you want to restrict the permissions to only a subset of the default admin ClusterRole, you’ll need to define an entirely new ClusterRole (or several Roles / ClusterRoles), ensuring that you include all of the required permissions above.

  2. Apply the ClusterRoleBinding:

    nix
    kubectl apply -f cluster-admins-access.yaml
    nix
    kubectl apply -f cluster-admins-access.yaml
    output
    text
    clusterrolebinding.rbac.authorization.k8s.io/cluster-admins-access created
    output
    text
    clusterrolebinding.rbac.authorization.k8s.io/cluster-admins-access created
  3. The admin ClusterRole doesn’t include permissions for CustomResourceDefinitions (CRDs). Gateway API resources are CRDs, so we’ll give cluster admins an additional ClusterRole that adds those permissions. It also doesn’t include any of the following, so we’ll add the required access to those as well:

    • Namespaces (only get/list/watch is include with the default admin ClusterRole)
    • Gateway API resources: GatewayClasses, Gateways, HTTPRoutes, ReferenceGrants, GRPCRoutes, and TCPRoutes
    • Gateway API resources Statues
    • IngressClasses
    • Ingress Status
    • ClusterRoles / ClusterRoleBindings
    • CustomResourceDefinitions (CRDs)
    • Nodes (get, list, watch, patch, and update)
    • Custom Resources belonging to core.haproxy.org
    • Custom Resources belonging to ingress.v1.haproxy.org
    • Custom Resources belonging to gate.v3.haproxy.org

    Define a ClusterRole named cluster-admins-hug that will add the additional permissions. These permissions don’t include delete permissions; you can add them if you wish, but generally, only the highest-level admins should have delete permissions.

    cluster-admins-hug.yaml
    yaml
    apiVersion: rbac.authorization.k8s.io/v1
    kind: ClusterRole
    metadata:
    name: cluster-admins-hug
    subjects:
    - kind: Group
    name: cluster-admins
    rules:
    # This allows your to manage ClusterRoles and ClusterRoleBindings
    - apiGroups: ["rbac.authorization.k8s.io"]
    resources: ["clusterroles", "clusterrolebindings"]
    verbs: ["get", "list", "watch", "create", "patch", "update"]
    # This allows you to create the required HUG CRDs in your cluster
    - apiGroups: ["apiextensions.k8s.io"]
    resources: ["customresourcedefinitions"]
    verbs: ["get", "list", "watch", "create", "update"]
    # This allows you to create namespaces for your teams
    - apiGroups: [""]
    resources: ["namespaces"]
    verbs: ["get", "list", "watch", "create", "patch", "update"]
    # This allows you to label your nodes; useful for assigning the HUG pods to high-performance hardware
    - apiGroups: [""]
    resources: ["nodes"]
    verbs: ["get", "list", "watch", "create", "patch", "update"]
    # Required by HUG
    - apiGroups: ["networking.k8s.io"]
    resources: ["ingressclasses"]
    verbs: ["get", "list", "watch", "create", "patch", "update"]
    - apiGroups: ["networking.k8s.io"]
    resources: ["ingresses/status"]
    verbs: ["get", "update", "patch"]
    - apiGroups: ["core.haproxy.org"]
    resources: ["*"]
    verbs: ["get", "list", "watch", "create", "patch", "update"]
    - apiGroups: ["ingress.v1.haproxy.org"]
    resources: ["*"]
    verbs: ["get", "list", "watch", "create", "patch", "update"]
    - apiGroups: ["gate.v3.haproxy.org"]
    resources: ["*"]
    verbs: ["get", "list", "watch", "create", "patch", "update"]
    # This allows you to manage Gateway API resources
    - apiGroups: ["gateway.networking.k8s.io"]
    resources:
    - gatewayclasses
    - gateways
    - httproutes
    - tcproutes
    - grpcroutes
    - referencegrants
    verbs: ["get", "list", "watch", "create", "update", "patch", "delete"]
    cluster-admins-hug.yaml
    yaml
    apiVersion: rbac.authorization.k8s.io/v1
    kind: ClusterRole
    metadata:
    name: cluster-admins-hug
    subjects:
    - kind: Group
    name: cluster-admins
    rules:
    # This allows your to manage ClusterRoles and ClusterRoleBindings
    - apiGroups: ["rbac.authorization.k8s.io"]
    resources: ["clusterroles", "clusterrolebindings"]
    verbs: ["get", "list", "watch", "create", "patch", "update"]
    # This allows you to create the required HUG CRDs in your cluster
    - apiGroups: ["apiextensions.k8s.io"]
    resources: ["customresourcedefinitions"]
    verbs: ["get", "list", "watch", "create", "update"]
    # This allows you to create namespaces for your teams
    - apiGroups: [""]
    resources: ["namespaces"]
    verbs: ["get", "list", "watch", "create", "patch", "update"]
    # This allows you to label your nodes; useful for assigning the HUG pods to high-performance hardware
    - apiGroups: [""]
    resources: ["nodes"]
    verbs: ["get", "list", "watch", "create", "patch", "update"]
    # Required by HUG
    - apiGroups: ["networking.k8s.io"]
    resources: ["ingressclasses"]
    verbs: ["get", "list", "watch", "create", "patch", "update"]
    - apiGroups: ["networking.k8s.io"]
    resources: ["ingresses/status"]
    verbs: ["get", "update", "patch"]
    - apiGroups: ["core.haproxy.org"]
    resources: ["*"]
    verbs: ["get", "list", "watch", "create", "patch", "update"]
    - apiGroups: ["ingress.v1.haproxy.org"]
    resources: ["*"]
    verbs: ["get", "list", "watch", "create", "patch", "update"]
    - apiGroups: ["gate.v3.haproxy.org"]
    resources: ["*"]
    verbs: ["get", "list", "watch", "create", "patch", "update"]
    # This allows you to manage Gateway API resources
    - apiGroups: ["gateway.networking.k8s.io"]
    resources:
    - gatewayclasses
    - gateways
    - httproutes
    - tcproutes
    - grpcroutes
    - referencegrants
    verbs: ["get", "list", "watch", "create", "update", "patch", "delete"]
  4. Create the cluster-admins-hug ClusterRole:

    nix
    kubectl apply -f cluster-admins-hug.yaml
    nix
    kubectl apply -f cluster-admins-hug.yaml
    output
    text
    clusterrole.rbac.authorization.k8s.io/cluster-admins-hug created
    output
    text
    clusterrole.rbac.authorization.k8s.io/cluster-admins-hug created
  5. Create a ClusterRoleBinding to bind the cluster-admins-hug ClusterRole to your cluster-admins group:

    cluster-admins-hug-access.yaml
    yaml
    apiVersion: rbac.authorization.k8s.io/v1
    kind: ClusterRoleBinding
    metadata:
    name: cluster-admins-hug-access
    subjects:
    - kind: Group
    name: cluster-admins
    apiGroup: rbac.authorization.k8s.io
    roleRef:
    kind: ClusterRole
    name: cluster-admins-hug
    apiGroup: rbac.authorization.k8s.io
    cluster-admins-hug-access.yaml
    yaml
    apiVersion: rbac.authorization.k8s.io/v1
    kind: ClusterRoleBinding
    metadata:
    name: cluster-admins-hug-access
    subjects:
    - kind: Group
    name: cluster-admins
    apiGroup: rbac.authorization.k8s.io
    roleRef:
    kind: ClusterRole
    name: cluster-admins-hug
    apiGroup: rbac.authorization.k8s.io

    Since we used a ClusterRoleBinding, the ClusterRole applies cluster-wide. This includes the resources that are namespaced-scoped; the permissions are active in all namespaces.

  6. Apply the cluster-admins-hug-access ClusterRoleBinding:

    nix
    kubectl apply -f cluster-admins-hug-access.yaml
    nix
    kubectl apply -f cluster-admins-hug-access.yaml
    output
    text
    clusterrolebinding.rbac.authorization.k8s.io/cluster-admins-hug-access created
    output
    text
    clusterrolebinding.rbac.authorization.k8s.io/cluster-admins-hug-access created
  7. You can use kubectl auth commands to check the permissions of your users. Replace the user (the placeholder value in --as=placeholder) with the name of an existing user in the cluster-admins group:

    nix
    kubectl auth can-i create gatewayclasses.gateway.networking.k8s.io -n dev-a --as-group=cluster-admins --as=placeholder
    nix
    kubectl auth can-i create gatewayclasses.gateway.networking.k8s.io -n dev-a --as-group=cluster-admins --as=placeholder
    output
    text
    yes
    output
    text
    yes

Anyone belonging to the cluster-admins group can now administer and manage HAProxy Unified Gateway and related administration items.

Create a limited cluster admin Jump to heading

Cluster admins have full access, but they can delegate some responsibilities to others. In this section, we’ll create a different class of cluster admins named infra admins with members that can manage Gateway API resources like GatewayClasses and Gateways, but not other resources like roles, namespaces, CRDs, and nodes.

GatewayClasses determine what implementation of Gateway API can be used, for example, HAProxy Unified Gateway. This could impact billing, IP address usage, port exposure, and if not restricted, could result in users bypassing security and cost controls. As such, it’s important that only the infrastructure or platform team is allowed to add GatewayClasses.

  1. Create a ClusterRoleBinding named infra-access that will bind the default admin ClusterRole to each user listed in subjects. Here we use the group infra-admin, which will apply the ClusterRole to all users in that group:

    infra-access.yaml
    yaml
    apiVersion: rbac.authorization.k8s.io/v1
    kind: ClusterRoleBinding
    metadata:
    name: infra-access
    subjects:
    - kind: Group
    name: infra-admin
    apiGroup: rbac.authorization.k8s.io
    roleRef:
    kind: ClusterRole
    name: admin
    apiGroup: rbac.authorization.k8s.io
    infra-access.yaml
    yaml
    apiVersion: rbac.authorization.k8s.io/v1
    kind: ClusterRoleBinding
    metadata:
    name: infra-access
    subjects:
    - kind: Group
    name: infra-admin
    apiGroup: rbac.authorization.k8s.io
    roleRef:
    kind: ClusterRole
    name: admin
    apiGroup: rbac.authorization.k8s.io

    Since we used a ClusterRoleBinding, the ClusterRole applies cluster-wide. Here we use roleRef to specify the ClusterRole we are binding is the admin role.

  2. Use kubectl to create the RoleBinding:

    nix
    kubectl apply -f infra-access.yaml
    nix
    kubectl apply -f infra-access.yaml
    output
    text
    clusterrolebinding.rbac.authorization.k8s.io/infra-access created
    output
    text
    clusterrolebinding.rbac.authorization.k8s.io/infra-access created
  3. The admin ClusterRole doesn’t include permissions for custom resources, of which Gateway API resources are, so we’ll attach those permissions to that ClusterRole by creating an aggregated ClusterRole. Define a ClusterRole named admin-gatewayclasses that will add the label rbac.authorization.k8s.io/aggregate-to-admin: "true". This is a special label that tells Kubernetes to inject the additional rules into the admin ClusterRole. Note that any rules you add will apply to all RoleBindings (and ClusterRoleBindings) that reference the role:

    admin-gateways.yaml
    yaml
    apiVersion: rbac.authorization.k8s.io/v1
    kind: ClusterRole
    metadata:
    name: admin-gatewayclasses
    labels:
    rbac.authorization.k8s.io/aggregate-to-admin: "true"
    rules:
    - apiGroups: ["gateway.networking.k8s.io"]
    resources:
    - gatewayclasses
    - gateways
    - grpcroutes
    - httproutes
    - referencegrants
    - tcproutes
    verbs: ["get", "list", "watch", "create", "update", "patch", "delete"]
    ... <add more rules here>
    admin-gateways.yaml
    yaml
    apiVersion: rbac.authorization.k8s.io/v1
    kind: ClusterRole
    metadata:
    name: admin-gatewayclasses
    labels:
    rbac.authorization.k8s.io/aggregate-to-admin: "true"
    rules:
    - apiGroups: ["gateway.networking.k8s.io"]
    resources:
    - gatewayclasses
    - gateways
    - grpcroutes
    - httproutes
    - referencegrants
    - tcproutes
    verbs: ["get", "list", "watch", "create", "update", "patch", "delete"]
    ... <add more rules here>

    The additional rules include permissions for GatewayClasses, Gateways, the different types of Routes, and ReferenceGrants. You can adjust these according to the needs of your cluster operators. You can extend this aggregated ClusterRole to include any other permissions you need that aren’t included in the default admin ClusterRole, or use this pattern to create additional aggregated ClusterRoles.

  4. You can use kubectl auth commands to check the permissions of your users. Replace the user (the placeholder value in --as=placeholder) with the name of an existing user in the infra-admin group:

    nix
    kubectl auth can-i create gatewayclasses.gateway.networking.k8s.io --as-group infra-admin --as=placeholder
    nix
    kubectl auth can-i create gatewayclasses.gateway.networking.k8s.io --as-group infra-admin --as=placeholder
    output
    text
    Warning: resource 'gatewayclasses' isn't namespace scoped in group 'gateway.networking.k8s.io'
    yes
    output
    text
    Warning: resource 'gatewayclasses' isn't namespace scoped in group 'gateway.networking.k8s.io'
    yes

    The warning is normal and indicates that you are checking RBAC permissions on a cluster-scoped resource.

    Verify that users not in the infra-admin group can’t create GatewayClasses:

    nix
    kubectl auth can-i create gatewayclasses.gateway.networking.k8s.io --as bob@example.com
    nix
    kubectl auth can-i create gatewayclasses.gateway.networking.k8s.io --as bob@example.com
    output
    text
    Warning: resource 'gatewayclasses' isn't namespace scoped in group 'gateway.networking.k8s.io'
    no
    output
    text
    Warning: resource 'gatewayclasses' isn't namespace scoped in group 'gateway.networking.k8s.io'
    no

Cluster operator permissions Jump to heading

Cluster operators can manage Gateways, Routes, and ReferenceGrants in one or more namespaces.

Create namespaces for teams Jump to heading

Gateway API relies on namespace boundaries.

  • HAProxy Unified Gateway, which is the Gateway controller and implements the Gateway API, lives in its own namespace, haproxy-unified-gateway.
  • Gateway resources are scoped to namespaces; These can be the same namespaces where teams deploy their applications, but for better separation they may instead live in their own namespaces (separate from the controller).

When you define a Gateway, you’ll set which types of Routes can attach to the Gateway and the namespaces from which they can attach. As such, developer teams should have their own namespaces where they deploy their applications and define their Routes. We’ll create two namespaces for two teams: dev-a and dev-b. Only developers with permissions in a particular namespace, Dev Team A in the dev-a namespace, for example, can access and use resources in that namespace.

To create the namespaces:

  1. Use kubectl to create the namespaces dev-a and dev-b:

    nix
    kubectl create namespace dev-a
    kubectl create namespace dev-b
    nix
    kubectl create namespace dev-a
    kubectl create namespace dev-b
    output
    text
    namespace/dev-a created
    namespace/dev-b created
    output
    text
    namespace/dev-a created
    namespace/dev-b created
  2. (Optional) Give each namespace a label. You must use namespace labels for Route, Gateway, and Network Policy scoping.

    nix
    kubectl label namespace dev-a team=dev-a
    kubectl label namespace dev-b team=dev-b
    nix
    kubectl label namespace dev-a team=dev-a
    kubectl label namespace dev-b team=dev-b
    output
    text
    namespace/dev-a labeled
    namespace/dev-b labeled
    output
    text
    namespace/dev-a labeled
    namespace/dev-b labeled

    Tip

    Kubernetes automatically adds the label kubernetes.io/metadata.name with the namespace name as the value to each namespace. This label is safe to use in various namespaceSelector matchLabels fields.

In the next sections, we’ll restrict access within and to each namespace by defining Roles and RoleBindings.

Allow admin access in a namespace Jump to heading

Gateway API permissions

With a default ClusterRole applied such as edit or admin, users can’t use Gateway API resources without adding additional permissions. This effectively restricts who can access which Gateway API resources.

A Gateway is namespaced and is usually owned by cluster operators. Developer teams don’t usually have access to create and modify Gateways. Gateways expose cluster IPs, terminate TLS, and link to firewalls. As such, it’s important that access to Gateways is restricted to avoid exposing private endpoints, leaking certificates, or impacting traffic to other teams.

Cluster operators will need permissions to be able to carry out their duties. You can use the default admin ClusterRole, which allows for full control within a namespace, but doesn’t allow access to cluster-scoped resources, or you can use the cluster-admin ClusterRole, which is effectively root access on the cluster.

Caution

Exercise caution when assigning the cluster-admin ClusterRole. It enables open access to everything in the cluster for users or groups to which it’s bound. Only a handful of infrastructure or platform team members should have these permissions. It’s probably better to create a custom role that requires only the minimum amount of access.

For this example, we’ll assume that cluster operators need only operate within one or a few namespaces, but don’t need full cluster access. For this, we can use the admin ClusterRole and apply it in the namespaces in which the cluster ops team will deploy Gateways.

  1. Create a RoleBinding named cluster-ops-access in the desired development namespace where the Gateway will be deployed. This RoleBinding will bind the default admin ClusterRole to each user listed in subjects. Here we use the group cluster-ops, which will apply the ClusterRole to all users in that group. In this example, we’ll assume that the Cluster Ops team manages all Gateway definitions in a namespace belonging to Dev Team A.

    dev-a-access.yaml
    yaml
    apiVersion: rbac.authorization.k8s.io/v1
    kind: RoleBinding
    metadata:
    name: cluster-ops-access
    namespace: dev-a
    subjects:
    - kind: Group
    name: cluster-ops
    apiGroup: rbac.authorization.k8s.io
    roleRef:
    kind: ClusterRole
    name: admin
    apiGroup: rbac.authorization.k8s.io
    dev-a-access.yaml
    yaml
    apiVersion: rbac.authorization.k8s.io/v1
    kind: RoleBinding
    metadata:
    name: cluster-ops-access
    namespace: dev-a
    subjects:
    - kind: Group
    name: cluster-ops
    apiGroup: rbac.authorization.k8s.io
    roleRef:
    kind: ClusterRole
    name: admin
    apiGroup: rbac.authorization.k8s.io

    Since we used a RoleBinding, the ClusterRole only applies in the specified namespace. Here we use roleRef to specify the ClusterRole we are binding is the admin role.

    Tip

    If you want cluster-ops to have this access in all namespaces (cluster-wide), create a ClusterRoleBinding instead of a RoleBinding. You won’t specify a namespace for the ClusterRoleBinding.

  2. Use kubectl to create the RoleBinding:

    nix
    kubectl apply -f cluster-ops-access.yaml
    nix
    kubectl apply -f cluster-ops-access.yaml
    output
    text
    rolebinding.rbac.authorization.k8s.io/cluster-ops-access created
    output
    text
    rolebinding.rbac.authorization.k8s.io/cluster-ops-access created
  3. The admin ClusterRole doesn’t include permissions for custom resources, of which Gateway API resources are, so we’ll attach those permissions to that ClusterRole by creating an aggregated ClusterRole. Define a ClusterRole named admin-gateways that will add the label rbac.authorization.k8s.io/aggregate-to-admin: "true". This is a special label that tells Kubernetes to inject the additional rules into the admin ClusterRole. Note that any rules you add will apply to all RoleBindings (and ClusterRoleBindings) that reference the role.

    admin-gateways.yaml
    yaml
    apiVersion: rbac.authorization.k8s.io/v1
    kind: ClusterRole
    metadata:
    name: admin-gateways
    labels:
    rbac.authorization.k8s.io/aggregate-to-admin: "true"
    rules:
    - apiGroups: ["gateway.networking.k8s.io"]
    resources:
    - gateways
    - grpcroutes
    - httproutes
    - referencegrants
    - tcproutes
    verbs: ["get", "list", "watch", "create", "update", "patch", "delete"]
    admin-gateways.yaml
    yaml
    apiVersion: rbac.authorization.k8s.io/v1
    kind: ClusterRole
    metadata:
    name: admin-gateways
    labels:
    rbac.authorization.k8s.io/aggregate-to-admin: "true"
    rules:
    - apiGroups: ["gateway.networking.k8s.io"]
    resources:
    - gateways
    - grpcroutes
    - httproutes
    - referencegrants
    - tcproutes
    verbs: ["get", "list", "watch", "create", "update", "patch", "delete"]

    The additional rules include permissions for Gateways, the different types of Routes, and ReferenceGrants. We don’t assign permission to manage GatewayClasses. You can adjust these according to the needs of your cluster operators. ReferenceGrants are included as well so that cluster operators can enable cross-namespace resource sharing, such as to Restrict access to TLS secrets, if necessary.

  4. Use kubectl to apply the ClusterRole:

    nix
    kubectl apply -f admin-gateways.yaml
    nix
    kubectl apply -f admin-gateways.yaml
    output
    text
    clusterrole.rbac.authorization.k8s.io/admin-gateways created
    output
    text
    clusterrole.rbac.authorization.k8s.io/admin-gateways created
  5. You can use kubectl auth commands to check the permissions of your users. Replace the user (the placeholder value in --as=placeholder) with the name of an existing user in the cluster-ops group:

    nix
    kubectl auth can-i create gateways.gateway.networking.k8s.io -n dev-a --as-group=cluster-ops --as=placeholder
    nix
    kubectl auth can-i create gateways.gateway.networking.k8s.io -n dev-a --as-group=cluster-ops --as=placeholder
    output
    text
    yes
    output
    text
    yes

    Verify that users not in the cluster-ops group can’t create Gateways:

    nix
    kubectl auth can-i create gateways.gateway.networking.k8s.io -n dev-a --as bob@example.com
    nix
    kubectl auth can-i create gateways.gateway.networking.k8s.io -n dev-a --as bob@example.com
    output
    text
    no
    output
    text
    no

If you want to remove any of the permissions that the default admin ClusterRole provides, you may need to define your own Role or ClusterRole, as you can’t remove permissions from the default ClusterRoles, you can only add.

Application developer permissions Jump to heading

Application developers can manage Routes for their applications.

Allow edit access in a namespace Jump to heading

User and group management

Permissions can be defined per-user or per-group. Kubernetes itself doesn’t manage users and groups. You must either use a third-party solution for authentication and authorization or manage your users’ certificates. If you are managing the certificates your users use to acccess the cluster, the username is defined by the CN (Common Name) value and groups are defined by O (Organization) attributes.

We want to make sure that Dev Team A can only manage Gateway API resources (and other resources) in the dev-a namespace. They shouldn’t be allowed access to other namespaces. To accomplish this, we’ll use the default edit ClusterRole and use a RoleBinding to apply it to users for a specific namespace. This role will include permissions for most namespace-scoped resources that developers may need to deploy and configure their applications. To define Dev Team A’s access to the dev-a namespace only:

  1. Create a RoleBinding named dev-a-access in the namespace dev-a that will bind the default edit ClusterRole to each user listed in subjects. Be sure to add an entry for each member of Dev Team A:

    dev-a-access.yaml
    yaml
    apiVersion: rbac.authorization.k8s.io/v1
    kind: RoleBinding
    metadata:
    name: dev-a-access
    namespace: dev-a
    subjects:
    - kind: User
    name: alice@example.com
    apiGroup: rbac.authorization.k8s.io
    - kind: User
    name: bob@example.com
    apiGroup: rbac.authorization.k8s.io
    roleRef:
    kind: ClusterRole
    name: edit
    apiGroup: rbac.authorization.k8s.io
    dev-a-access.yaml
    yaml
    apiVersion: rbac.authorization.k8s.io/v1
    kind: RoleBinding
    metadata:
    name: dev-a-access
    namespace: dev-a
    subjects:
    - kind: User
    name: alice@example.com
    apiGroup: rbac.authorization.k8s.io
    - kind: User
    name: bob@example.com
    apiGroup: rbac.authorization.k8s.io
    roleRef:
    kind: ClusterRole
    name: edit
    apiGroup: rbac.authorization.k8s.io

    Since we used a RoleBinding, the ClusterRole only applies in the specified namespace. Here we use roleRef to specify the ClusterRole we are binding is the edit role.

    Tip

    You can also specify a group in the subjects instead of listing each user. Either way, the format of the user names or groups will depend on your authentication and authorization implementation (a third-party service, a Webhook, manually-generated certificates, and so on)

    yaml
    ...
    subjects:
    - kind: Group
    name: dev-a
    apiGroup: rbac.authorization.k8s.io
    ...
    yaml
    ...
    subjects:
    - kind: Group
    name: dev-a
    apiGroup: rbac.authorization.k8s.io
    ...
  2. Use kubectl to create the RoleBinding:

    nix
    kubectl apply -f dev-a-access.yaml
    nix
    kubectl apply -f dev-a-access.yaml
    output
    text
    rolebinding.rbac.authorization.k8s.io/dev-a-access created
    output
    text
    rolebinding.rbac.authorization.k8s.io/dev-a-access created
  3. You can use kubectl auth commands to check the permissions of your users:

    nix
    kubectl auth can-i create deployments -n dev-a --as alice@example.com
    nix
    kubectl auth can-i create deployments -n dev-a --as alice@example.com
    output
    text
    yes
    output
    text
    yes

    Verify that users don’t have permissions to access restricted resources:

    nix
    kubectl auth can-i view nodes -n dev-a --as alice@example.com
    nix
    kubectl auth can-i view nodes -n dev-a --as alice@example.com
    output
    text
    no
    output
    text
    no
  4. Repeat the steps for members of Dev Team B, creating another RoleBinding for those users and the dev-b namespace.

If you want to remove any of the permissions that the default edit ClusterRole provides, you may need to define your own Role or ClusterRole, as you can’t remove permissions from the default ClusterRoles, you can only add. In the sections that follow, we’ll add additional permissions to these Roles that allow users to access Gateway API resources.

Allow adding a Route Jump to heading

Routes are safe for self-service as they don’t expose the infrastructure and they don’t create external IPs. App teams can safely create and define their own Routes within their own namespaces. Gateways should most likely live in their own namespaces, or otherwise be separate from developer namespaces. Within the Gateway, you can restrict the types of Routes developers can attach to the Gateway and from what namespaces.

In the section Allow edit access in a namespace, we created a RoleBinding that bound the default edit ClusterRole to dev team users. The edit ClusterRole doesn’t include permissions for custom resources, of which Gateway API resources are, so we’ll attach those permissions to that ClusterRole by creating an aggregated ClusterRole.

  1. Define a ClusterRole named edit-routes that will add the label rbac.authorization.k8s.io/aggregate-to-edit: "true". This is a special label that tells Kubernetes to inject the additional rules you define into the edit ClusterRole. Note that any rules you add will apply to all RoleBindings (and ClusterRoleBindings) that reference the role:

    edit-routes.yaml
    yaml
    apiVersion: rbac.authorization.k8s.io/v1
    kind: ClusterRole
    metadata:
    name: edit-routes
    labels:
    rbac.authorization.k8s.io/aggregate-to-edit: "true"
    rules:
    - apiGroups: ["gateway.networking.k8s.io"]
    resources:
    - httproutes
    - grpcroutes
    - tcproutes
    verbs: ["get", "list", "watch", "create", "update", "patch", "delete"]
    edit-routes.yaml
    yaml
    apiVersion: rbac.authorization.k8s.io/v1
    kind: ClusterRole
    metadata:
    name: edit-routes
    labels:
    rbac.authorization.k8s.io/aggregate-to-edit: "true"
    rules:
    - apiGroups: ["gateway.networking.k8s.io"]
    resources:
    - httproutes
    - grpcroutes
    - tcproutes
    verbs: ["get", "list", "watch", "create", "update", "patch", "delete"]

    The additional rules include permissions for the different types of Routes. You can adjust these according to the needs of your users.

    Tip

    You can use this pattern to define any additional, non-Gateway API permissions your developers may need. Create additional ClusterRoles that have the rbac.authorization.k8s.io/aggregate-to-edit: "true" label.

  2. Use kubectl to create the ClusterRole:

    nix
    kubectl apply -f edit-routes.yaml
    nix
    kubectl apply -f edit-routes.yaml
    output
    text
    clusterrole.rbac.authorization.k8s.io/edit-routes created
    output
    text
    clusterrole.rbac.authorization.k8s.io/edit-routes created
  3. You can use kubectl auth commands to check the permissions of your users:

    nix
    kubectl auth can-i create httproutes.gateway.networking.k8s.io -n dev-a --as alice@example.com
    nix
    kubectl auth can-i create httproutes.gateway.networking.k8s.io -n dev-a --as alice@example.com
    output
    text
    yes
    output
    text
    yes

    This user can now create HTTPRoutes, GRPCRoutes, and TCPRoutes.

Additional restrictions Jump to heading

Next, let’s review other ways to secure your Gateway API deployment.

Restrict communication through a Gateway Jump to heading

Gateways serve as entry points to the cluster. As such, it’s important to restrict who can communicate with the Gateway and to what pods the Gateway can communicate. This prevents a compromised Gateway from further compromising the cluster. You can implement NetworkPolicies for your Gateways that define these retrictions. RBAC protects your control plane while NetworkPolicies protect your data plane. You’ll need to develop a NetworkPolicy that fits the needs of your organization and your security and networking policies. Here is an example NetworkPolicy for a Gateway that includes some basic protections:

yaml
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: restrict-gateway-traffic
namespace: haproxy-unified-gateway
spec:
podSelector:
matchLabels:
run: haproxy-unified-gateway
policyTypes:
- Ingress
- Egress
ingress:
- from:
- ipBlock:
cidr: <CLOUD LB RANGE HERE>
ports:
- protocol: TCP
port: 8080
- protocol: TCP
port: 8443
egress:
- to:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: dev-a
ports:
- protocol: TCP
port: 80
- protocol: TCP
port: 443
- to:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: dev-b
ports:
- protocol: TCP
port: 80
- protocol: TCP
port: 443
- to:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: kube-system
ports:
- protocol: TCP
port: 53
- protocol: UDP
port: 53
yaml
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: restrict-gateway-traffic
namespace: haproxy-unified-gateway
spec:
podSelector:
matchLabels:
run: haproxy-unified-gateway
policyTypes:
- Ingress
- Egress
ingress:
- from:
- ipBlock:
cidr: <CLOUD LB RANGE HERE>
ports:
- protocol: TCP
port: 8080
- protocol: TCP
port: 8443
egress:
- to:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: dev-a
ports:
- protocol: TCP
port: 80
- protocol: TCP
port: 443
- to:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: dev-b
ports:
- protocol: TCP
port: 80
- protocol: TCP
port: 443
- to:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: kube-system
ports:
- protocol: TCP
port: 53
- protocol: UDP
port: 53

This NetworkPolicy applies to the controller pods (pods labeled with run: haproxy-unified-gateway). It implements the following:

  • Allows ingress traffic only from a specific IP address range, perhaps for a cloud load balancer, only on TCP ports 8080 and 8443.
  • Allows egress to only the following:
    • Pods in the namespace labeled kubernetes.io/metadata.name=dev-a or kubernetes.io/metadata.name=dev-b on TCP ports 80 and 443.
    • Pods in the namespace labeled kubernetes.io/metadata.name=kube-system on TCP and UDP port 53 to allow DNS resolution.
  • All other traffic is denied.

Restrict GatewayClasses per namespace Jump to heading

It’s possible to restrict which GatewayClasses can be used in a particular namespace, but this requires the use of a policy engine that can validate that any Gateway in a particular namespace references only the GatewayClass names that are allowed for that namespace. See the official Kubernetes Gateway API documentation for more information.

Restrict access to TLS secrets Jump to heading

Developer teams shouldn’t be allowed to read or create TLS secrets for shared Gateways. You can put Gateway TLS secrets in their own namespace and reference them via ReferenceGrants from the Gateway namespaces. For more information see the official Gateway API documentation for cross namespace certificate references.

See also

Do you have any suggestions on how we can improve the content of this page?