As we all know, in the K8s permission management system, you can bind RoleBinding to ServiceAccount, User and Group to achieve permission assignment.
We often use ServiceAccount to restrict the permissions of a pod; for User and Group, there are no specific resources to correspond to them except for some special system groups, which is very unfriendly to the user management in traditional projects.
This article talks about how to unify user management in K8s clusters.
Preparation
First we need an Identity Provider to unify the management of users in K8s and provide OIDC protocol services, this article uses KeyCloak as the Identity Provider.
Configuration in KeyCloak
To implement user management, we need to use the concept of group in K8s to assign privileges to a group of users, which requires the use of the Claim concept in the OIDC protocol to implement the grouping of users in K8s.
Claim is the information carried in the ID Token, which refers to the range of information requested by the client, such as user name, email address, etc., and these can be extended to carry some information about the group to which the user belongs, etc.
So the first step is to extend the Claim in KeyCloak, as follows.
We added a “User Attribute” to the Client and added it to the ID Token; Multivalued must be set to ON to ensure that the value of the “groups” Claim is a String array, where each value represents a group to which the User A User can belong to multiple groups at the same time, and each value is separated by a comma.
The second step is to set the “groups” property for the user.
Once everything is set up, you can see the “groups” attribute in the ID Token of the user “admin”.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
{
"jti":"9259af9c-8a3d-45ff-94f6-6780f2a79580",
"exp":1564739637,
"nbf":0,
"iat":1564739337,
"iss":"https://172.16.105.1:8082/auth/realms/hdls",
"aud":"kubernetes",
"sub":"f846ddb1-4435-429f-9ce5-faba7a791d43",
"typ":"ID",
"azp":"kubernetes",
"auth_time":0,
"session_state":"37b1a2ca-1b3b-4c61-ae2c-f8c14818ca6e",
"acr":"1",
"email_verified":false,
"groups":[
"manager"
],
"preferred_username":"admin"
}
|
Configuration of ApiServer
There are several configurable environment variables left in ApiServer to support the OIDC plugin, official link.
oidc-issuer-url
: URL of OIDC Server, only accepts https protocol.
oidc-client-id
: The client_id configured in OIDC Server, which is unique.
oidc-username-claim
: Specify the claim used to identify the user name in the ID Token.
oidc-username-prefix
: Username prefix, “-” means no prefix.
oidc-groups-claim
: A claim in the ID Token that identifies the user’s group. The claim must be in the form of an array, so the user can belong to multiple groups.
oidc-groups-prefix
: group prefix.
oidc-ca-file
: the path where the CA certificate of Identity Provider is located.
The configuration parameters of this article are as follows.
1
2
3
4
5
6
|
- --oidc-issuer-url=https://172.16.105.1:8082/auth/realms/hdls
- --oidc-client-id=kubernetes
- --oidc-username-claim=preferred_username
- --oidc-username-prefix=-
- --oidc-groups-claim=groups
- --oidc-ca-file=/etc/kubernetes/pki/ca.crt
|
KubeConfig configuration
As a user, we need to access the API Server through the Client Application. kubectl is obviously the preferred Client, so that kubectl can access Kubernetes as the user we created, “admin”, and pass the authentication, and this requires a configuration of KubeConfig to complete the following processes.
- create a kubeconfig user: “admin”.
- Configure client-id, client credential, id-token, refresh-token, certficaite, etc. for “admin”.
- Create a user context for “admin”.
- Set it to the current context.
The following command generates the configuration with one click.
1
2
3
4
5
6
7
8
|
kubectl config set-credentials USER_NAME \
--auth-provider=oidc \
--auth-provider-arg=idp-issuer-url=( issuer url ) \
--auth-provider-arg=client-id=( your client id ) \
--auth-provider-arg=client-secret=( your client secret ) \
--auth-provider-arg=refresh-token=( your refresh token ) \
--auth-provider-arg=idp-certificate-authority=( path to your ca certificate ) \
--auth-provider-arg=id-token=( your id_token )
|
How to get Token
There are various ways to generate ID Token and Refresh Token, the easiest way is to use curl to authenticate with Password Grant to get the desired ID Token and Refresh Token.
1
2
3
4
5
6
7
8
9
10
11
12
|
$ curl -k 'https://172.16.105.1:8082/auth/realms/hdls/protocol/openid-connect/token' -d "client_id=kubernetes" -d "client_secret=40dc1fef...c3eeec6" -d "response_type=code token" -d "grant_type=password" -d "username=test" -d "password=dangerous" -d "scope=openid"
{
"access_token":"eyJhbGciOiJSU...0CMPw",
"expires_in":300,
"refresh_expires_in":1800,
"refresh_token":"eyJhbGciOiJ...W1VUA",
"token_type":"bearer",
"id_token":"eyJhbGc...z3TaGJGQ",
"not-before-policy":0,
"session_state":"2845e...92ff2",
"scope":"openid profile email"
}
|
However, this requires manually generating a Token and filling it into KubeConfig each time, which is a pain. The good news is that there are already many tools in the community that can automatically write Token into KubeConfig for you that work very well, such as
- kubelogin
- k8s-auth-client
- k8s-keycloak-oidc-helper
- kuberos
- k8s-oidc-helper
User Management
After getting everything configured successfully, let’s look at assigning permissions to users. This takes into account K8s’ RBAC system.
RBAC
For users with group manager, we assign them the “cluster-admin” role which comes with the system, i.e. the administrator privileges of the cluster.
1
2
3
4
5
6
7
8
9
10
11
12
|
kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: keycloak-admin-group
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: cluster-admin
subjects:
- kind: Group
name: manager
apiGroup: rbac.authorization.k8s.io
|
We “add” the admin user to the “manager” group in keyCloak.
Then use this user to access APIServer.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
[root@172-16-105-1 ~]# kubelogin --username=admin --password=dangerous
You got a valid token until 2019-08-03 15:32:42 +0800 CST
[root@172-16-105-1 ~]#
[root@172-16-105-1 ~]# kubectl get cs
NAME STATUS MESSAGE ERROR
controller-manager Healthy ok
scheduler Healthy ok
etcd-0 Healthy {"health":"true"}
[root@172-16-105-1 ~]# kubectl get no
NAME STATUS ROLES AGE VERSION
172-16-105-1 Ready master 54d v1.14.1
172-16-105-2 Ready <none> 54d v1.14.1
[root@172-16-105-1 ~]# kubectl get po
No resources found.
[root@172-16-105-1 ~]# kubectl get all
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 54d
|
You can see that the “admin” user has access to all resources.
Then, we create a new role called “hdls-role” for the user whose group is developer, and only give them access to the pod.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: hdls-role
rules:
- apiGroups: [""]
resources: ["pods"]
verbs: ["get", "list"]
---
kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: hdls-rolebinding
roleRef:
kind: ClusterRole
name: hdls-role
apiGroup: rbac.authorization.k8s.io
subjects:
- kind: Group
name: developer
apiGroup: rbac.authorization.k8s.io
|
“Add” the test user to the “developer” group in keyCloak.
Then use that user to access APIServer.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
[root@172-16-105-1 ~]# kubelogin --username=test --password=dangerous
You got a valid token until 2019-08-03 15:40:21 +0800 CST
[root@172-16-105-1 ~]#
[root@172-16-105-1 ~]# kubectl get po
No resources found.
[root@172-16-105-1 ~]# kubectl get no
Error from server (Forbidden): nodes is forbidden: User "test" cannot list resource "nodes" in API group "" at the cluster scope
[root@172-16-105-1 ~]#
[root@172-16-105-1 ~]# kubectl get cs
Error from server (Forbidden): componentstatuses is forbidden: User "test" cannot list resource "componentstatuses" in API group "" at the cluster scope
[root@172-16-105-1 ~]#
[root@172-16-105-1 ~]# kubectl get all
Error from server (Forbidden): replicationcontrollers is forbidden: User "test" cannot list resource "replicationcontrollers" in API group "" in the namespace "default"
Error from server (Forbidden): services is forbidden: User "test" cannot list resource "services" in API group "" in the namespace "default"
Error from server (Forbidden): daemonsets.apps is forbidden: User "test" cannot list resource "daemonsets" in API group "apps" in the namespace "default"
Error from server (Forbidden): deployments.apps is forbidden: User "test" cannot list resource "deployments" in API group "apps" in the namespace "default"
Error from server (Forbidden): replicasets.apps is forbidden: User "test" cannot list resource "replicasets" in API group "apps" in the namespace "default"
Error from server (Forbidden): statefulsets.apps is forbidden: User "test" cannot list resource "statefulsets" in API group "apps" in the namespace "default"
Error from server (Forbidden): horizontalpodautoscalers.autoscaling is forbidden: User "test" cannot list resource "horizontalpodautoscalers" in API group "autoscaling" in the namespace "default"
Error from server (Forbidden): jobs.batch is forbidden: User "test" cannot list resource "jobs" in API group "batch" in the namespace "default"
Error from server (Forbidden): cronjobs.batch is forbidden: User "test" cannot list resource "cronjobs" in API group "batch" in the namespace "default"
|
The test user is restricted from accessing all resources except for pod information.
Summary
This article only introduced how to manage users in K8s through KeyCloak and kubectl, accordingly, if your own user center implements the OIDC protocol, and the client accesses APIServer as “bearer token” through ID Token, you can really connect the permission system of K8s with the superstructure.