Workload identity for Kubernetes (JWT-based authentication)

An workload identity is an identity used by workloads to authenticate to Conjur and access secrets. This topic describes how to define workload identities in Conjur for Kubernetes workloads when using JWT-based authentication.

For more general information about workloads in Conjur, see Workloads in Conjur.

Service account token (JWT)

When using JWT-based authentication, you define workload identities for Kubernetes workloads based on the service account token (JWT).

The subject (sub) of a JWT is the fully qualified service account identity which combines the namespace and serviceaccount names.

"sub": "system:serviceaccount:test-app-namespace:test-app-sa"
  • The Namespace name in a Kubernetes cluster is unique.

  • The service account in a Namespace is unique.

Let's examine the claims in the following sample JWT:

{
  "aud": [
    "https://conjur.host.name/"
  ],
  "exp": 1637781121,
  "iat": 1637773921,
  "iss": "https://kubernetes.default.svc.cluster.local",
  "kubernetes.io": {
    "namespace": "test-app-namespace",
    "pod": {
      "name": "test-app-pod-554b4c6496-m849s",
      "uid": "e71d7a37-0725-4cf7-a685-c5a43d94f6ea"
    },
    "serviceaccount": {
      "name": "test-app-sa",
      "uid": "ee0952eb-72df-4d98-a3a6-e1550e9e63cc"
    }
  },
  "nbf": 1637773921,
  "sub": "system:serviceaccount:test-app-namespace:test-app-sa"
}

The following claims from the JWT can be used to define a Kubernetes workload identity in Conjur:

Claim path

Description

aud

Identifies possible token recipients.

We recommend narrowing down the aud list as much as possible to prevent different recipients of the same token from approaching each other. The token should ideally have one aud value only.

In parallel, when defining the JWT Authenticator policy, we also recommend utilizing this claim in combination with the audience variable in the policy. The audience variable value in the JWT Authenticator should be the value of the aud claim.

For more details about defining the JWT Authenticator policy, see JWT-based Kubernetes authentication.

kubernetes.io/namespace

The Namespace name (unique at the Kubernetes cluster level)

This can be used as the workload identity name.

In parallel, when defining the JWT Authenticator policy, use the token-app-property variable. Alternatively, when the service account identity is not important (dev or test environments), define one host annotation.

kubernetes.io/pod/name

Pod name (unique at the namespace level)

Usually not constant for Pods created from Deployments, StatefulSets or for Pods that are part of an auto-scaling group.

In cases where the Pod name is a constant, this can be used together with the sub claim to narrow down the workload identity to the Pod level.

kubernetes.io/serviceaccount/name

Service account name (unique at the namespace level)

If your organization allows creating a service account with the same name in another namespace, we recommend that you do not use the value from this claim on its own.

sub

Combination of namespace and service account names (unique at the Kubernetes cluster level)

Provides the highest identity granularity.

We strongly recommend using the value of this claim as the name for the workload identity, together with one host annotation. In parallel, use the token-app-property variable in the JWT Authenticator.

Workload identity for Kubernetes resources

In Conjur, a workload is represented as a host and its identifying attributes are is defined in policy using host annotations. These annotations determine the granularity at which authentication of the workload is performed and access is granted to the workload.

This section demonstrates how you would define a workload identity in Conjur for a Kubernetes resource based on claims in the service account token (JWT).

Once you have decided on the granularity of authentication and secret access that you want to enforce, you set up and load the relevant JWT Authenticator and host policies.

For example, to control authentication and secret access by the test-app-namespace namespace and test-app-sa service account, you'd first define the JWT Authenticator in a policy as follows:

  • You'd include the token-app-property variable, and set its value to sub

  • You'd include the identity-path variable, and set its value to app-path

Next, you'd define a policy for the workload identity (host) as follows::

  • You'd name the host id for the expected sub claim value. For example, using the sample claim earlier in this topic, the host id would be system:serviceaccount:test-app-namespace:test-app-sa

  • You'd include one host annotation, authn-jwt/<serviceId>/sub, whose value would be equivalent to the value of the sub claim

- !policy
  id: app-path
  body:     
    - !host
      id: system:serviceaccount:test-app-namespace:test-app-sa
      annotations:
        authn-jwt/<serviceId>/sub: system:serviceaccount:test-app-namespace:test-app-sa

Relationship between the JWT Authenticator and the workload identity

The following table lists the possible token-app-property values you could use in your JWT Authenticator policy, and the respective annotation that you'd need to define in the host policy:

token-app-property format

Annotation format

Description

sub

authn-jwt/<serviceId>/sub

Expected sub claim value

kubernetes.io/serviceaccount/name

authn-jwt/<serviceId>/kubernetes.io/serviceaccount/name

Expected kubernetes.io/serviceaccount/name claim value

kubernetes.io/pod/name

authn-jwt/<serviceId>/kubernetes.io/pod/name

Expected kubernetes.io/pod/name claim value

kubernetes.io/namespace

authn-jwt/<serviceId>/kubernetes.io/namespace

Expected kubernetes.io/namespace claim value

When multiple applications are running in the same namespace and each application needs access to different secrets, you'd need to create a dedicated service account for each application and a respective workload identity in Conjur.

Each service account should be mapped into the relevant workload Pod and the service account should be projected to the relevant container.