Binding AWS IAM Roles to K3S Service accounts
In this short tutorial, you will learn how to configure the IAM roles for Service Account for a bare-metal cluster using k3s as an example.
As public OIDC enpoint will be used public S3 bucket.
Provision K3S cluster
1
| curl -sfL https://get.k3s.io | sh -
|
After cluster will start we need stop it,
1
2
| systemctl stop k3s
/usr/local/bin/k3s-killall.sh
|
and edit it’s configuration.
/etc/rancher/k3s/config.yaml
We need add next values to kube-apiserver-arg:
1
2
3
4
5
| kube-apiserver-arg:
- 'service-account-issuer=https://[bucket name].s3.amazonaws.com'
- 'service-account-jwks-uri=https://[bucket name].s3.amazonaws.com/openid/v1/jwks'
- 'service-account-lookup=true'
- 'api-audiences=k3s-cluster'
|
And now we should start k3s cluster
Use k3d for local testing
1
2
3
4
| k3d cluster create test --k3s-arg "--kube-apiserver-arg=--service-account-issuer=https://[bucket name].s3.amazonaws.com@server:0" \
--k3s-arg "--kube-apiserver-arg=--service-account-jwks-uri=https://[bucket name].s3.amazonaws.com/openid/v1/jwks@server:0" \
--k3s-arg "--kube-apiserver-arg=--service-account-lookup=true@server:0" \
--k3s-arg "--kube-apiserver-arg=--api-audiences=k3d-cluster@server:0"
|
Get OIDC configuration from k3s cluster
Now we need get configuration for OIDC from cluster:
1
2
| kubectl get --raw /.well-known/openid-configuration > openid-configuration
kubectl get --raw /openid/v1/jwks > jwks
|
The content of the openid-configuration file should be:
1
2
3
4
5
6
7
8
9
10
11
12
13
| {
"issuer": "https://[bucket name].s3.amazonaws.com",
"jwks_uri": "https://[bucket name].s3.amazonaws.com/openid/v1/jwks",
"response_types_supported": [
"id_token"
],
"subject_types_supported": [
"public"
],
"id_token_signing_alg_values_supported": [
"RS256"
]
}
|
And for the jwks:
1
2
3
4
5
6
7
8
9
10
11
12
| {
"keys": [
{
"use": "sig",
"kty": "RSA",
"kid": "ZO4TUgVjBzMWKVP8mmBwKLvsuyn8z-gfqUp27q9lO4w",
"alg": "RS256",
"n": "34a81xuM…",
"e": "AQAB"
}
]
}
|
Create s3 bucket and upload OIDC configurations
Furst of all we need create s3 bucket with Turned OFF Block Public Access option.
And add next bucket policy to make it public:
1
2
3
4
5
6
7
8
9
10
11
12
| {
"Version": "2012-10-17",
"Statement": [
{
"Sid": "PublicReadGetObject",
"Effect": "Allow",
"Principal": "*",
"Action": "s3:GetObject",
"Resource": "arn:aws:s3:::[bucket name]/*"
}
]
}
|
Upload the files on the S3 bucket with the following directory structure:
.well-known/openid-configuration for the configuration.
openid/v1/jwks for the configuration.
Test that the endpoints are publicly reachable:
1
2
| curl https://[bucket name].s3.amazonaws.com/.well-known/openid-configuration
curl https://[bucket name].s3.amazonaws.com/openid/v1/jwks
|
Create an Open ID connect provider on AWS
On AWS console navigate to IAM -> Identity providers and push ADD Provider button.
Fill in values that is required for creating OIDC provider
Create Role for SA
From the AWS console, navigate to IAM > Roles > click on the Create Role and choose Web identity role.
Select OIDC provider that you have just create and also select audience (NOTE: It shoul be equal you put in k3s configuration)
- Create a new role.
- Type “web identity”.
- Assign the policies.
- Edit the trust relationships.
The content for the trust relationship should be the following:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
| {
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Federated": "arn:aws:iam::[aws account id]:oidc-provider/[bucket name].s3.amazonaws.com"
},
"Action": "sts:AssumeRoleWithWebIdentity",
"Condition": {
"StringEquals": {
"[bucket name].s3.amazonaws.com:aud": "test", // <- this is the audience
"[bucket name].s3.amazonaws.com:sub": "system:serviceaccount:default:test" // <- optional
}
}
}
]
}
|
Make a note of the Role ARN.
Test Integration
Apply next kubernetes manifest to cluster:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
| ---
apiVersion: v1
kind: ServiceAccount
metadata:
name: test
namespace: default
---
apiVersion: v1
kind: ConfigMap
metadata:
name: "test-configmap"
data:
AWS_DEFAULT_REGION: us-east-1
AWS_ROLE_ARN: "arn:aws:iam::[aws account id]:role/[role name]" # matches `RoleName` in the CloudFormation template
AWS_WEB_IDENTITY_TOKEN_FILE: /var/run/secrets/kubernetes.io/serviceaccount/token
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: "aws-cli"
labels:
app: "aws-cli"
spec:
replicas: 1
selector:
matchLabels:
app: "aws-cli"
template:
metadata:
labels:
app: "aws-cli"
spec:
serviceAccountName: "test"
containers:
- name: "aws-cli"
image: amazon/aws-cli:2.15.8
command: ["sleep"]
args: ["86400"]
envFrom:
- configMapRef:
name: "test-configmap"
|
After deployment will be up and running it is time to check integration:
1kubectl exec -it [pod name] bash
2
3aws sts get-caller-identity
4{
5 "UserId": "AROAXXXXXXXXXXXXXXXXX:botocore-session-1608249397",
6 "Account": "000000000000",
7 "Arn": "arn:aws:iam::000000000000:role/blog-example-iam-role/botocore-session-1608249397"
8}