Workload identity for Kubernetes (cert-based authentication)

Security policy can be used to create identities for OpenShift/Kubernetes resources/objects at multiple levels of granularity.

In most cases, you create an identity for your workload based on its service account or based on its controller such as a Deployment or StatefulSet. Then you configure your application Pod to authenticate with Conjur using this identity.

Once configured, the identity can be used by any Pod deployed using the service account or the controller, providing a flexible way of sharing secrets among Pods that may have common access needs.

By assigning identities to Kubernetes resources/objects, you can use security policy to control the granularity and identity of the Kubernetes resources/objects that authenticate to Conjur and that can access secrets. The access usually occurs in the context of a workload-specific policy, where specific roles are granted access to specific secrets.

Define Kubernetes resources/objects as Conjur identities

In security policy, workloads are represented as hosts and their identities are represented as annotations on the host resource. These annotations determine the granularity at which authentication is performed and access is granted.

Kubernetes
Object/Resource

Description

Annotation format

Namespace

Authentication and secret access is by Kubernetes namespace. All resources in the namespace are controlled by the same grants and permissions.

Use this level of granularity: if all applications in the namespace share secrets, and if access to the secrets can be managed together.

authn-k8s/namespace

Namespace Label Selector

Authentication and secret access is by Kubernetes namespaces that match the configured label selector. The same grants and permissions control all of the resources in the namespaces that match the label selector.

The label selector must adhere to the format <key>=<value>,<key1>=<value2>,..., and supports = and ==.

For example, in a Kubernetes deployment managed by Rancher, namespaces that belong to a specific Rancher project are given a reserved label. To match with a project whose ID is equal to p-zvd84, set the value of this Conjur annotation to field.cattle.io/projectId=p-zvd84.

Use this level of granularity: if all applications in a group of namespaces share secrets, and if access to the secrets can be managed together.

authn-k8s/namespace-label-selector

Deployment

Authentication and secret access is by the Kubernetes Deployment name within a namespace.

Use this level of granularity: if a group of stateless applications share secrets and access management rules.

authn-k8s/deployment

DeploymentConfig

(OpenShift only)

Authentication and secret access is by the DeploymentConfig name within an OpenShift project.

Use this level of granularity: if a group of stateless applications share secrets and access management rules.

authn-k8s/deployment-config

ServiceAccount

Authentication is by Kubernetes service account name within a namespace.

Use this level of granularity (relevant for cert-based authentication only): if you are already using service accounts to control access to secrets.

You can build policy on top of the service account access control. In this case, you will be managing both sets of access control for the same secrets.

We recommend using this option as a transition, and move towards using the Kubernetes Deployment and StatefulSet resources as app IDs.

authn-k8s/service-account

StatefulSet

Authentication is by Kubernetes StatefulSet name within a namespace.

Use this level of granularity: if a group of stateful applications share secrets and access management rules.

authn-k8s/stateful-set

Pod

Authentication is by Kubernetes Pod name within a namespace.

Use this level of granularity: for testing and proof of concept (POC). Pods tend to stop and restart too often to depend on them for security.

authn-k8s/authentication-container-name

Authentication Container Name

The name of the container from which the host authenticates with Conjur.

This annotation is required whenever the sidecar or init container is given a name other than authenticator.

authn-k8s/authentication-container-name

Workload identity in policy

Once you have decided on the granularity of authentication and secret access that you want to enforce, create the relevant host in your policy.

For example, to control authentication and secret access by the test-app-namespace namespace, you'd create the following policy:

 
- !policy
  id: apps
  body:
    - !group
      
    - !host
      id: test-app
      annotations:
        authn-k8s/namespace: test-app-namespaceauthn-k8s/authentication-container-name:  test-container
								
    - !grant
      role: !group
      member: !host test-app

where authn-k8s/authentication-container-name is the container name from which the host authenticates with Conjur.

When you define the Deployment manifest to deploy your workload, you create a connection to the workload ID (host) by adding the full host id path an environment variable in the Deployment manifest.

Examples of host annotations

 
- !host
  id: test-app
  annotations:
    authn-k8s/namespace: <namespace> # required and mutually exclusive with authn-k8s/namespace-label-selector
    authn-k8s/namespace-label-selector: <label_name>=<label_value>,... # required and mutually exclusive with authn-k8s/namespace
    authn-k8s/service-account: <service-account>
    authn-k8s/deployment: <deployment>
    authn-k8s/deployment-config: <deployment-config>
    authn-k8s/stateful-set: <stateful-set>

Namespace label security

When you use namespace labels to grant authentication and secret access, you must secure the labels to ensure that only Kubernetes admins can create and label namespaces. Use ClusterRoles and ClusterRoleBindings to grant and restrict namespace access.

ClusterRole and ClusterRoleBinding for read-only access

The following ClusterRole and ClusterRoleBinding pair applies read-only permissions on namespace resources for all authenticated users.

---
apiVersion: rbac.authorization.k8s.io
kind: ClusterRole
metadata:
  name: namespace-reader
rules:
- apiGroups: [""]
  resources: ["namespaces"]
  verbs: ["get", "list", "watch"]
---
apiVersion: rbac.authorization.k8s.io
kind: ClusterRoleBinding
metadata:
  name: namespace-reader
subjects:
- kind: Group
  name: system:authenticated
  apiGroup: rbac.authorization.k8s.io
roleRef:
  kind: ClusterRole
  name: namespace-reader
  apiGroup: rbac.authorization.k8s.io

When a non-admin user tries to label a namespace with kubectl label, the command returns the following:

---Error from server (Forbidden): namespaces "<namespace>" is forbidden: User "<authd-user>" cannot patch resource "namespaces" in API group "" in the namespace "<namespace>"

ClusterRole and ClusterRoleBinding for write access

To give a specific group of admin users write access to namespace resources, use the following ClusterRole and ClusterRoleBinding for those admins.

---
apiVersion: rbac.authorization.k8s.io
kind: ClusterRole
metadata:
  name: namespace-writer
rules:
- apiGroups: [""]
  resources: ["namespaces"]
  verbs: ["create", "update", "patch"]
---
apiVersion: rbac.authorization.k8s.io
kind: ClusterRoleBinding
metadata:
  name: namespace-writer
subjects:
- kind: Group
  name: <group-that-should-have-edit-permission>
  apiGroup: rbac.authorization.k8s.io
roleRef:
  kind: ClusterRole
  name: namespace-editor
  apiGroup: rbac.authorization.k8s.io