Functional Kubernetes Namespaces in Docker Enterprise

For Kubernetes in a Docker Enterprise Edition (EE) 2.1 cluster, namespaces can be used to segregate objects and, with Role Based Access Control (RBAC), designate which users or groups can do what within each of them. In this post, we are going to create three namespaces for development, test and production environments, four groups for the development, test, operations and management teams and access controls defining what each of these groups can do in each of these namespaces.

You can create the three namespaces using either imperative or declarative forms. We prefer to use the declarative form as we’re big believers in infrastructure as code so we’ll use it in the following examples. (For a good discussion on the differences between the two and how Kubernetes manages them, see Imperative/Declarative and a Few `kubectl` tricks.)

We’ll assume you’ve already created your users and groups through either the Docker EE UI or its UCP (Universal Control Plane) API. In a company, we would typically integrate our Docker EE cluster with our corporate LDAP, Active Directory, or OAuth systems to map our Docker users and groups to our company’s users and groups. In this example, we will use locally defined users and groups but the principles are the same in both cases.

Let’s take a step back and look at the access requirements for each of these groups in each of these namespaces.

The development team needs (almost) full control of the development namespace in order to create, read, delete and update (CRUD) object in it as they’re their solutions. However, in the test and production namespaces they don’t need to do anything but view the objects there. We’ll call this group dev-team.

The test team may need/want view access to the development and production namespaces to help with troubleshooting their own solutions. And, if they’re using a fully automated CI/CD pipeline, they may only need view access to the test namespace as well. However, in this example, we’ll assume they need (almost) full control of the test namespace. (We’ll discuss the pipeline automation user later in this post.) We’ll call this group test-team.

The operations team needs full control over all of the namespaces in order to handle problems in any of them. We’ll call this group ops-team.

Finally, the management only need view access to all of the namespaces to see what is going on in them. We’ll call this group mgmt-team.

Functional Namespaces

Now let’s codify these requirements in the following Kubernetes manifests. In these manifests, we’ll define the namespaces, the roles and the rolebindings. (For an in-depth discussion of these concepts, see Using RBAC Authorization in the Kubernetes reference documentation.

development-namespace.yml:

kind: Namespace
apiVersion: v1
metadata:
  name: development
---
kind: Role
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: development-full-access
  namespace: development
rules:
- apiGroups: ["", "extensions", "apps"]
  resources: ["*"]
  verbs: ["*"]
- apiGroups: ["batch"]
  resources:
  - jobs
  - cronjobs
  verbs: ["*"]
---
kind: Role
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: development-view-access
  namespace: development
rules:
- apiGroups: ["", "extensions", "apps"]
  resources: ["*"]
  verbs: ["get", "list", "watch"]
  - apiGroups: ["batch"]
  resources:
  - jobs
  - cronjobs
  verbs: ["get", "list", "watch"]
---
kind: RoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  labels:
    subjectName: dev-team
  name: dev-team:development-full-access
  namespace: development
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: Role
  name: development-full-access
subjects:
- apiGroup: rbac.authorization.k8s.io
  kind: Group
  name: dev-team
---
kind: RoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  labels:
    subjectName: test-team
  name: test-team:development-view-access
  namespace: development
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: Role
  name: development-view-access
subjects:
- apiGroup: rbac.authorization.k8s.io
  kind: Group
  name: test-team
---
kind: RoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  labels:
    subjectName: ops-team
  name: ops-team:development-full-access
  namespace: development
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: Role
  name: development-full-access
subjects:
- apiGroup: rbac.authorization.k8s.io
  kind: Group
  name: ops-team
---
kind: RoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  labels:
    subjectName: mgmt-team
  name: mgmt-team:development-view-access
  namespace: development
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: Role
  name: development-view-access
subjects:
- apiGroup: rbac.authorization.k8s.io
  kind: Group
  name: mgmt-team

test-namespace.yml:

kind: Namespace
apiVersion: v1
metadata:
  name: test
---
kind: Role
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: test-full-access
  namespace: test
rules:
- apiGroups: ["", "extensions", "apps"]
  resources: ["*"]
  verbs: ["*"]
- apiGroups: ["batch"]
  resources:
  - jobs
  - cronjobs
  verbs: ["*"]
---
kind: Role
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: test-view-access
  namespace: test
rules:
- apiGroups: ["", "extensions", "apps"]
  resources: ["*"]
  verbs: ["get", "list", "watch"]
- apiGroups: ["batch"]
  resources:
  - jobs
  - cronjobs
  verbs: ["get", "list", "watch"]
---
kind: RoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  labels:
    subjectName: dev-team
  name: dev-team:test-view-access
  namespace: test
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: Role
  name: test-view-access
subjects:
- apiGroup: rbac.authorization.k8s.io
  kind: Group
  name: dev-team
---
kind: RoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  labels:
    subjectName: test-team
  name: test-team:test-full-access
  namespace: test
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: Role
  name: test-full-access
subjects:
- apiGroup: rbac.authorization.k8s.io
  kind: Group
  name: test-team
---
kind: RoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  labels:
    subjectName: ops-team
  name: ops-team:test-full-access
  namespace: test
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: Role
  name: test-full-access
subjects:
- apiGroup: rbac.authorization.k8s.io
  kind: Group
  name: ops-team
---
kind: RoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  labels:
    subjectName: mgmt-team
  name: mgmt-team:test-view-access
  namespace: test
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: Role
  name: test-view-access
subjects:
- apiGroup: rbac.authorization.k8s.io
  kind: Group
  name: mgmt-team

production-namespace.yml:

kind: Namespace
apiVersion: v1
metadata:
  name: production
---
kind: Role
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: production-full-access
  namespace: production
rules:
- apiGroups: ["", "extensions", "apps"]
  resources: ["*"]
  verbs: ["*"]
- apiGroups: ["batch"]
  resources:
  - jobs
  - cronjobs
  verbs: ["*"]
---
kind: Role
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: production-view-access
  namespace: production
rules:
- apiGroups: ["", "extensions", "apps"]
  resources: ["*"]
  verbs: ["get", "list", "watch"]
- apiGroups: ["batch"]
  resources:
  - jobs
  - cronjobs
  verbs: ["get", "list", "watch"]
---
kind: RoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  labels:
    subjectName: dev-team
  name: dev-team:production-view-access
  namespace: production
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: Role
  name: production-view-access
subjects:
- apiGroup: rbac.authorization.k8s.io
  kind: Group
  name: dev-team
---
kind: RoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  labels:
    subjectName: test-team
  name: test-team:production-view-access
  namespace: production
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: Role
  name: production-view-access
subjects:
- apiGroup: rbac.authorization.k8s.io
  kind: Group
  name: test-team
---
kind: RoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  labels:
    subjectName: ops-team
  name: ops-team:production-full-access
  namespace: production
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: Role
  name: production-full-access
subjects:
- apiGroup: rbac.authorization.k8s.io
  kind: Group
  name: ops-team
---
kind: RoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  labels:
    subjectName: mgmt-team
  name: mgmt-team:production-view-access
  namespace: production
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: Role
  name: production-view-access
subjects:
- apiGroup: rbac.authorization.k8s.io
  kind: Group
  name: mgmt-team

Here’s what it looks like when we apply these manifests to Kubernetes in our Docker EE cluster using an administrator’s client bundle.

admin> kubectl apply -f .\development-namespace.yml
namespace "development" created
role.rbac.authorization.k8s.io "development-full-access" created
role.rbac.authorization.k8s.io "development-view-access" created
rolebinding.rbac.authorization.k8s.io "dev-team:development-full-access" created
rolebinding.rbac.authorization.k8s.io "test-team:development-view-access" created
rolebinding.rbac.authorization.k8s.io "ops-team:development-full-access" created
rolebinding.rbac.authorization.k8s.io "mgmt-team:development-view-access" created
admin> kubectl apply -f .\test-namespace.yml
namespace "test" created
role.rbac.authorization.k8s.io "test-full-access" created
role.rbac.authorization.k8s.io "test-view-access" created
rolebinding.rbac.authorization.k8s.io "dev-team:test-view-access" created
rolebinding.rbac.authorization.k8s.io "test-team:test-full-access" created
rolebinding.rbac.authorization.k8s.io "ops-team:test-full-access" created
rolebinding.rbac.authorization.k8s.io "mgmt-team:test-view-access" created
admin> kubectl apply -f .\production-namespace.yml
namespace "production" created
role.rbac.authorization.k8s.io "production-full-access" created
role.rbac.authorization.k8s.io "production-view-access" created
rolebinding.rbac.authorization.k8s.io "dev-team:production-view-access" created
rolebinding.rbac.authorization.k8s.io "test-team:production-view-access" created
rolebinding.rbac.authorization.k8s.io "ops-team:production-full-access" created
rolebinding.rbac.authorization.k8s.io "mgmt-team:production-view-access" created

For each of the environments we’ve created the corresponding namespace. Within each namespace, we’ve created roles for full access and view access. Finally, in each namespace, we’ve created the appropriate role binding for each group in that namespace.

Now, as long as each user is a member of the appropriate team, they will have the appropriate access to each namespace. This depends upon users specifying the namespace they intend to work in either through their current context, as a command line parameter or in their manifests.

As mentioned above, the pipeline automation user (or users) tends to be a special user. In general, its access requirements are very similar to, if not the same as, an operations user. In this case, we’ll treat this user, say pipeline-user, the same as a member of the operations team but specify its access separately. Since we’ve already defined the roles in each namespace, we only need to define the role bindings for the pipeline-user.

pipeline-user.yml:

kind: RoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  labels:
    subjectName: pipeline-user
  name: pipeline-user:development-full-access
  namespace: development
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: Role
  name: development-full-access
subjects:
- apiGroup: rbac.authorization.k8s.io
  kind: User
  name: pipeline-user
---
kind: RoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  labels:
    subjectName: pipeline-user
  name: pipeline-user:test-full-access
  namespace: test
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: Role
  name: test-full-access
subjects:
- apiGroup: rbac.authorization.k8s.io
  kind: User
  name: pipeline-user
---
kind: RoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  labels:
    subjectName: pipeline-user
  name: pipeline-user:production-full-access
  namespace: production
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: Role
  name: production-full-access
subjects:
- apiGroup: rbac.authorization.k8s.io
  kind: User
  name: pipeline-user

Finally, here’s what it looks like when we apply this manifest to our Docker EE Kubernetes cluster.

admin> kubectl apply -f .\pipeline-user.yml
rolebinding.rbac.authorization.k8s.io "pipeline-user:development-full-access" created
rolebinding.rbac.authorization.k8s.io "pipeline-user:test-full-access" created
rolebinding.rbac.authorization.k8s.io "pipeline-user:production-full-access" created

In this post, we’ve made the assumption you have one development team, one test team, one operations team and one management team. We’ve also made the assumption that the solutions you’re developing don’t need to be (or you don’t want them to be) separated from each. In any but the smallest of organizations, this will never be the case. However, the principles demonstrated here can be easily extended to these larger organizations. For example, instead of one development namespace, you might have multiple development namespaces for multiple development teams, i.e. app1-development and app2-development namespaces for app1-dev-team and app2-dev-team, respectively. Similarly, operations might have separate teams for networking and storage. You might create a network-ops-team and a storage-ops-team with corresponding roles giving them full access but only to network and storage resources, respectively.

Leave a Reply