AWS Elastic Kubernetes Service: RBAC Authorization via AWS IAM and RBAC Groups
Tags: devops, aws, security, kubernetes
Published: 2023-12-13 13:29:02

---
title: AWS Elastic Kubernetes Service: RBAC Authorization via AWS IAM and RBAC Groups
published: true
date: 2023-11-27 07:55:40 UTC
tags: devops,aws,security,kubernetes
canonical_url: https://rtfm.co.ua/en/aws-elastic-kubernetes-service-rbac-authorization-via-aws-iam-and-rbac-groups/
---

![](https://cdn-images-1.medium.com/max/318/1*X3phdxmrG9gpQFETH74efQ.png)

We have two new projects in the Elastic Kubernetes Service (см. [AWS Elastic Kubernetes Service: a cluster creation automation, part 1 — CloudFormation](https://rtfm.co.ua/en/aws-elastic-kubernetes-service-a-cluster-creation-automation-part-1-cloudformation/)), each project lives in its own separate Namespace.

In addition, there are two users, developers, who need to be given access to these two Namespaces, but only to Pods in them and only for certain read-only operations.

In order to implement common access control for these users, we need some kind of group to which we can add them and then use it to check access rules.

To better understand the idea described below — see posts [Kubernetes: part 4 — AWS EKS authentication, aws-iam-authenticator, and AWS IAM](https://rtfm.co.ua/en/kubernetes-part-4-aws-eks-authentification-aws-iam-authenticator-and-aws-iam/) and [Kubernetes: part 5 — RBAC authorization with a Role and RoleBinding example](https://rtfm.co.ua/en/kubernetes-part-5-rbac-authorization-with-a-role-and-rolebinding-example/).

**_Note_** _: the_ [_original post_](https://rtfm.co.ua/ru/aws-elastic-kubernetes-service-rbac-avtorizaciya-cherez-aws-iam-i-rbac-gruppy/) _was written on 12/04/2020, so some parts might be slightly outdated. But the general idea and some technical details still worth knowing._

**What have we have now?**

We have the AWS IAM — its users, groups, roles and policies.

We have Kubernetes RBAC with its `ClusterRole`, `RoleBinding`, and the _aws-auth_ ConfigMap.

This means that our task is divided into two:

- think about a mechanism for authentication and authorization of two users in AWS IAM that can create a single object for identification (group, role)
- think through the mechanism of authorization of two users in Kubernetes RBAC using this object

**The Problem**

What’s the non-obvious difficulty?

In how we link these two tasks, since unlike IAM Users and IAM Roles, an AWS IAM Group cannot be the subject of authentication, see the list in [AWS JSON Policy Elements: Principal](https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_policies_elements_principal.html), and we will not be able to use the ARN of such a group of the form _arn:aws:iam::111:_ **_group_** _/group-name_ in our _aws-auth_ ConfigMap to give users of that group access to the Kubernetes cluster.

But we can use [IAM Roles](https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles.html), and via [IAM Groups](https://docs.aws.amazon.com/IAM/latest/UserGuide/id_groups.html) we can connect common IAM policies with access to the desired roles.

What can we build using it?

- create an IAM Role with an IAM read-only policy on the API calls [eks:*](https://docs.aws.amazon.com/IAM/latest/UserGuide/list_amazonelasticcontainerserviceforkubernetes.html), and add a _trust policy_ to it with users of our account - then we will use this role in the _aws-auth_ ConfigMap
- create an IAM Group with an IAM policy that allows the [`sts::AssumeRole`](https://docs.aws.amazon.com/STS/latest/APIReference/API_AssumeRole.html) call to the IAM Role created above to be executed
- create an IAM User, add it to this Group so that it “inherits” the AssumeRole permission through the group Policy.

Let’s look ahead a bit — what’s next for Kubernetes?

There our _aws-auth_ ConfigMap will "map" our IAM Role to an RBAC group in the cluster, and our users will configure their AWS CLI to execute the AssumeRole of the actual Role _arn:aws:iam::534***385:role/iam-bttrm-web-ro-role_ - then Kubernetes will receive an identifier common to both of there users, and they will be able to perform operations defined in their common RBAC Role and only in the allowed namespaces.

**About names**

So we have two users - let's combine them into a conditional group "_web_" which will be used in IAM role and group names.

Plus we have two projects in Kubernetes with separate namespaces - _project1_ and _project2_, which we will also combine into the conditional group "_web_", but we will use this group only in the names of RBAC resources in Kubernetes to make them more visible.

In addition, we will add the prefix _iam-_ to AWS IAM resource names, and _rbac-_ to RBAC resource names.

Hence, we will have names of the species:

- **_iam-bttrm-web_** _-ro-role_: here **_iam_** says this is an object from AWS IAM, **_bttrm_** is the common name of my project, **_web_** is the “domain” for our two new web projects
- **_rbac-bttrm-web_** _-ro-role-binding_: **_rbac_** is the object linked to Kubernetes RBAC, **_bttrm_** is the common name of my project, **_web_** is the “domain” for our two web projects

#### AWS IAM

To visualize what exactly we are doing and how it all will work with each other, we can sketch a scheme like this:

![](https://cdn-images-1.medium.com/max/1024/0*v8FSyhM68VxeIqg1.png)

Here:

1. A user needs to access a Role by executing an AssumeRole request in AWS IAM:
2. AWS IAM performs **authentication** of the user
3. if authentication is passed — AWS IAM starts **authorization** of the user: checks the access policies connected to it
4. finds a policy that allows AssumeRole for the desired role and allows the user to access that role
5. the user accesses the IAM Role:
6. AWS IAM validates the Trust relations of this Role
7. makes sure that the user came from the same account as our Role and allows access
8. the user accesses the AWS Elastic Kubernetes Service, but already using the IAM Role identifier (token)

_This and the following diagram were made in the_ [_cloudcraft.co_](https://cloudcraft.co)_, but in the workflow such ones are simply drawn by hand in a large notepad. I don’t know why no one in any of the guides I’ve seen has shown all these connections in the form of such simple and visual diagrams — without them, it’s much more difficult to understand all this, especially in the part Kubernetes RBAC._

We’ll leave it here for now, we can come back as we go.

#### Creating An IAM Role

Create a new role — the key, in fact, element of the entire design. Use ЕС2:

![](https://cdn-images-1.medium.com/max/1008/0*yg5tqpIW0VuGcaSo.png)

On the next page click _Create policy_, in the new tab in the policy creation window switch to JSON, enter the policy:

```
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "eks:DescribeCluster",
                "eks:ListClusters"
            ],
            "Resource": "*"
        }
    ]
}
```

![](https://cdn-images-1.medium.com/max/748/0*DQRq1pI0GWRTY_2E.png)

In it, we allow the `eks:DescribeCluster` and `eks:ListClusters` API calls to be executed across all regions of our entire account - these calls are used by the AWS CLI during the execution of [`aws eks update-kubeconfig`](https://docs.aws.amazon.com/cli/latest/reference/eks/update-kubeconfig.html), which will be executed by our new users to configure their local `kubectl`.

Save it with the name _iam-bttrm-eks-ro-policy_.

Go back to the previous tab with the role creation, click on "Update" on the right, and add the created policy to the role:

![](https://cdn-images-1.medium.com/max/1007/0*NLrMRkVWW7YvzIhl.png)

Skip the _Tags_, save the Role with the name _iam-bttrm-web-ro-ro-role_:

![](https://cdn-images-1.medium.com/max/822/0*tT4UwHJKH16bu92Z.png)

Remember its **Role ARN** :

![](https://cdn-images-1.medium.com/max/949/0*BOgyCKUJUftc0Ysh.png)

IAM Role Trust relationships

_Although_ [_here>>>_](https://serverfault.com/questions/944254/aws-iam-assumerole-within-same-account) _the answers claim that Trust relations within a single account don’t need to be created — but it didn’t work for me without it, plus it’s mentioned in the documentation here _[_t>>>>_](https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-role.html#cli-role-prepare)_._

Switch to the _Trust relationships_ tab, click _Edit trust relationship_, in _Principal_ specify _“AWS”: “arn:aws:iam::534***385:root”_ — here **_root_** includes all users (or rather, all authentication objects) of our account:

![](https://cdn-images-1.medium.com/max/671/0*OZaO5XRfpYT9ownP.png)

```
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "AWS": "arn:aws:iam::534***385:root"
      },
      "Action": "sts:AssumeRole"
    }
  ]
}
```

**_Note_** _: actually, from the security point of view it’s a bad idea to give such a wide access, via the_ _root entity_

But it doesn’t mean that any subject authenticated in our account will be able to use (_assume_) this role, because this subject must still have the corresponding rights to call this particular Role — and for our future users we are implementing this through IAM Group.

#### Creating an IAM Policy

Before we create an IAM Group — let’s add the actual policy that will allow the execution of the API call `sts:AssumeRole` to the Role we created, and then this Policy will be connected to the created Group, through which it will be "inherited" by future users of the group.

Later, to take away the IAM-user's access to an Elastic Kubernetes Service cluster, it will be enough to disconnect him from the IAM Group.

Create a policy:

![](https://cdn-images-1.medium.com/max/783/0*KS5niYMoMe01YYW1.png)

In the `Resource`, specify the ARN of the Policy we created above:

```
{
    "Version": "2012-10-17",
    "Statement": {
        "Effect": "Allow",
        "Action": "sts:AssumeRole",
        "Resource": "arn:aws:iam::534***385:role/iam-bttrm-web-ro-role"
    }
}
```

Save it, for example with the name _iam-bttrm-allow-assume-web-ro-role-policy_:

![](https://cdn-images-1.medium.com/max/968/0*XSHx6yaNfCEOrp5H.png)

#### Creating an IAM Group

Go to Groups, create a new one, name it _iam-bttrm-web-ro-group_, connect the previously created Policy to it:

![](https://cdn-images-1.medium.com/max/715/0*qTRZOe-EFYYnV023.png)

#### Creating an IAM User

Create a user with _Programmatic access_, add it to the _iam-bttrm-web-ro-group,_ were we already have the _am-bttrm-allow-assume-web-ro-role-policy_ connected:

![](https://cdn-images-1.medium.com/max/801/0*PODd9B-tsQoyo_kp.png)

Configure the local AWS CLI profile to a new user _iam-bttrm-web-user-1_:

```
$ aws configure --profile iam-bttrm-web-user-1
AWS Access Key ID [None]: AKI***O4Z
AWS Secret Access Key [None]: FoS***kft
Default region name [None]: us-east-2
Default output format [None]: json
```

Check it:

```
$ aws --profile iam-bttrm-web-user-1 sts get-caller-identity
{
    "UserId": "AID***DUH",
    "Account": "534***385",
    "Arn": "arn:aws:iam::534***385:user/iam-bttrm-web-user-1"
}
```

Trying to access EC2 — should get a rejection:

```
$ aws --profile iam-bttrm-web-user-1 ec2 describe-instances

An error occurred (UnauthorizedOperation) when calling the DescribeInstances operation: You are not authorized to perform this operation.
```

Okay — no one gave us permission to make an API request `DescribeInstances`, and we were not authorized (although we were authenticated as user _iam-bttrm-web-user-1_). Try EKS operations:

```
$ aws --profile iam-bttrm-web-user-1 ec2 describe-instances

An error occurred (UnauthorizedOperation) when calling the DescribeInstances operation: You are not authorized to perform this operation.
```

Great — we authenticated again as _iam-bttrm-web-user-1_ — but again failed authorization, this time to execute the `eks:ListClusters` API call.

And now, let's do this by using AssumeRole:

1. authenticate as _iam-bttrm-web-user-1_
2. check `sts:AssumeRole` - IAM will check what policies are attached to us and find the _iam-bttrm-allow-assume-web-ro-role-policy_ policy
3. we will temporarily gain rights to perform operations allowed for the _iam-bttrm-web-ro-role_ IAM Role with the `eks:ListClusters` permissions

Let’s try it.

Update local `~/.aws/config`, and add a second profile, _iam-bttrm-web-user-1-eks_, to the _iam-bttrm-web-user-1-eks_ profile:

```
...
[profile iam-bttrm-web-user-1]
region = us-east-2
output = json

[profile iam-bttrm-web-user-1-eks]
role_arn = arn:aws:iam::534***385:role/iam-bttrm-web-ro-role
source_profile = iam-bttrm-web-user-1
region = us-east-2
```

In which we use the _arn:aws:iam::534***385:role/iam-bttrm-web-ro-role_ role, but authenticate as _iam-bttrm-web-user-1_ via the `source_profile`. (see [Using an IAM Role in the AWS CLI](https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-role.html)).

Try it - repeat under a common profile:

```
$ aws --profile iam-bttrm-web-user-1 eks list-clusters --output text

An error occurred (AccessDeniedException) [...]
```

And under the second, with the AssumeRole:

```
$ aws --profile iam-bttrm-web-user-1-eks eks list-clusters --output text
CLUSTERS bttrm-eks-prod-0
CLUSTERS eksctl-bttrm-eks-production-1
CLUSTERS bttrm-eks-dev-0
```

Everything works, we’re done with that — we have a group using which users get to execute `eks:ListClusters`.

Using the same role (but their ACCESS and SECRET keys for authentication) users will configure their `kubectl` to access the Pods. But we will restrict access to Namespaces and Pods via Kubernetes RBAC.

#### Kubernetes RBAC

So, we need to have some group through permissions to which we can grant access to pods in two different namespaces, and this group must be associated with an AWS IAM Group.

Since we can’t use AWS IAM Group ARN — we do a “dirty hack” in the form of using a common role for different users of the group, by which we will identify them for authorization in Kubernetes RBAC.

Next, we need to create:

- a `ClusterRole` - role that will authorize operations with Pods in all Namespaces
- two `RoleBinding` in two Namespaces, one in each, which will bind the RBAC group and the ClusterRole in this Namespace, thus limiting the impact of this ClusterRole for the RBAC group to the boundaries of the Namespace in which this RoleBinding is created
- and update the _aws-auth_ ConfigMap to link the IAM Role and the RBAC group

As a result, we should have the following scheme:

![](https://cdn-images-1.medium.com/max/1024/0*26ivZJYpZOCT03Pp.png)

#### Creating an RBAC ClusterRole

Describe a `ClusterRole` named _rbac-bttrm-pods-ro-cluster-role_:

```
kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1beta1
metadata:
  name: rbac-bttrm-pods-ro-cluster-role
rules:
- apiGroups: [""]
  resources: ["pods"]
  verbs: ["get", "watch", "list"]
```

Create it:

```
$ kubectl apply -f rbac-bttrm-pods-ro-cluster-role.yml 
clusterrole.rbac.authorization.k8s.io/rbac-bttrm-pods-ro-cluster-role created
```

#### Creating an RBAC RoleBinding

Create two `RoleBinding` - identical but **in different namespace** - _bttrm-web-proj-_ **_1_** _-ns_ and _bttrm-web-proj-_ **_2_** _-ns_:

```
--
kind: RoleBinding
apiVersion: rbac.authorization.k8s.io/v1beta1
metadata:
  name: rbac-bttrm-web-proj-1-ro-role-binding
  namespace: bttrm-web-proj-1-ns
subjects:
- kind: Group
  name: rbac-bttrm-web-ro-group
  apiGroup: rbac.authorization.k8s.io
roleRef:
  kind: ClusterRole
  name: rbac-bttrm-pods-ro-cluster-role
  apiGroup: rbac.authorization.k8s.io
---
kind: RoleBinding
apiVersion: rbac.authorization.k8s.io/v1beta1
metadata:
  name: rbac-bttrm-web-proj-2-ro-role-binding
  namespace: bttrm-web-proj-2-ns
subjects:
- kind: Group
  name: rbac-bttrm-web-ro-group
  apiGroup: rbac.authorization.k8s.io
roleRef:
  kind: ClusterRole
  name: rbac-bttrm-pods-ro-cluster-role
  apiGroup: rbac.authorization.k8s.io
```

Create the namespaces themselves:

```
$ kubectl create ns bttrm-web-proj-1-ns
namespace/bttrm-web-proj-1-ns created
$ kubectl create ns bttrm-web-proj-2-ns
namespace/bttrm-web-proj-2-ns created
```

Create NGINX Pods in them:

```
$ kubectl -n bttrm-web-proj-1-ns run nginx --image=nginx 
$ kubectl -n bttrm-web-proj-2-ns run nginx --image=nginx
```

And create the Bindings:

```
$ kubectl apply -f rbac-bttrm-web-ro-role-binding.yml 
rolebinding.rbac.authorization.k8s.io/rbac-bttrm-web-proj-1-ro-role-binding created
rolebinding.rbac.authorization.k8s.io/rbac-bttrm-web-proj-2-ro-role-binding created
```

#### Updating aws-auth ConfigMap

Now we need to update the _aws-auth_ ConfigMap, which would link our IAM Group in AWS (and essentially a shared IAM Role) to the _rbac-bttrm-web-ro-group_ "virtual" group we "created" in bindings.

Edit it:

```
$ kubectl -n kube-system edit cm aws-auth
```

Add the Role ARN to the mapGroups:

```
...
    - groups:
      - rbac-bttrm-web-ro-group
      rolearn: arn:aws:iam::534***385:role/iam-bttrm-web-ro-role
      username: iam-bttrm-web-ro-role 
...
```

Save, exit.

#### `kubecl` config and verification

Now for our `kubectl`, let's set up an access context to our Dev cluster using the AWS CLI profile _iam-bttrm-web-user-1-_ **_eks_** , which runs AssumeRole _arn:aws:iam::534***385:role/iam-bttrm-web-ro-role_, with the IAM user _iam-bttrm-web-user-1_ used for authentication:

```
$ aws --profile iam-bttrm-web-user-1-eks eks update-kubeconfig --name bttrm-eks-dev-0
Updated context arn:aws:eks:us-east-2:534***385:cluster/bttrm-eks-dev-0 in /home/setevoy/.kube/config
```

Try just `get pod` in the _default_ namespace:

```
$ kubectl get pod
Error from server (Forbidden): pods is forbidden: User "iam-bttrm-web-ro-role" cannot list resource "pods" in API group "" in the namespace "default"
```

And repeat in the _bttrm-web-proj-1-ns_:

```
$ kubectl get pod -n bttrm-web-proj-1-ns
NAME READY STATUS RESTARTS AGE
nginx-7bb7cd8db5-l9jtm 1/1 Running 0 2m24s
```

Here’s our NGINX.

Repeat for the namespace of the second project — _bttrm-web-proj-_ **_2_** _-ns_:

```
$ kubectl get pod -n bttrm-web-proj-2-ns
NAME READY STATUS RESTARTS AGE
nginx-7bb7cd8db5-9s6zj 1/1 Running 0 2m31s
```

You can try accessing other resources that we didn’t give access to, such as WorkerNodes:

```
$ kubectl get node
Error from server (Forbidden): nodes is forbidden: User "iam-bttrm-web-ro-role" cannot list resource "nodes" in API group "" at the cluster scope
```

Or use `kubectl auth can-i`:

```
$ kubectl auth can-i get node
Warning: resource 'nodes' is not namespace scoped
no

$ kubectl auth can-i get pod
no

$ kubectl auth can-i get pod -n bttrm-web-proj-1-ns
yes

$ kubectl auth can-i create pod -n bttrm-web-proj-1-ns
no
```

Done.

#### Useful links:

- [Let’s do DevOps: Assuming an IAM role from an EC2 instance](https://medium.com/swlh/aws-iam-assuming-an-iam-role-from-an-ec2-instance-882081386c49)
- [3 Realistic Approaches to Kubernetes RBAC](https://thenewstack.io/three-realistic-approaches-to-kubernetes-rbac/)

_Originally published at_ [_RTFM: Linux, DevOps, and system administration_](https://rtfm.co.ua/en/aws-elastic-kubernetes-service-rbac-authorization-via-aws-iam-and-rbac-groups/)_._

* * *