notes/articles/2. Add Kubernetes roles/Add Kubernetes Roles.md
Allen Languor 71f4cd0462 articles
2021-06-04 06:03:46 +03:00

6.8 KiB

Add Kubernetes users with Ansible

Hi! In the previous article, I've told how to deploy k8s cluster with ansible. Now I'm going to tell, how to add users to your cluster to be able to control your k8s remotely.

My Github: https://github.com/allanger/kubernetes-rbac-ansible-role

Let's imagine you've deployed a bare-metal cluster and you ssh to the master node every time you wanna do something with it. It's not cool, right? So you need to add a user to your cluster.

You can do it manually but, I think, after the first time you perform it, you'd like to do it automatically. That's why I've created this Ansible role.

Clone the repo and go to /vars/main.yaml file. If you know what you wanna do, set all variables yourself, but if you're not sure, you should update only the username var. In this case, we're adding a cluster-admin user, because I guess if you're able to run this role against your master node, you're a cluster admin.

---
# --------------------------------------
# -- K8s username
# --------------------------------------
username: "admin"
# --------------------------------------
# -- How many days certificate 
# -- will be valid
# --------------------------------------
certificate_expires_in: 500
# --------------------------------------
# -- K8s cluster name
# --------------------------------------
cluster: "kubernetes"
# --------------------------------------
# -- RoleBinding parameters
# --------------------------------------
# -- Binding type: 
# ---- ClusterRoleBinding
# ---- RoleBinding
# --------------------------------------
binding_type: ClusterRoleBinding
# --------------------------------------
# -- Role type
# -- ClusterRole
# -- Role
# --------------------------------------
role_type: ClusterRole
# --------------------------------------
# -- Cluster role name
# -- https://kubernetes.io/docs/reference/access-authn-authz/rbac/
# --------------------------------------
role: cluster-admin

When you're done, let's go to /tasks/main.yaml.

In the first block, we are creating a working directory. In this directory, Ansible will store certificates and configs. (It will be removed after the play, so it's a temporary dir)

- name: Prepare working directory
 block:
 - name: Set workdir as fact
 set_fact:
 working_dir: "{{ ansible_env.HOME }}/.certs/{{ username }}"

 - name: Create a directory if it does not exist
 ansible.builtin.file:
 path: "{{ working_dir }}"
 state: directory
 mode: "0775"

In the second block, we're installing packages that will be used while running the role.

- name: Ensure required packages are installed
 block:
 # --------------------------------------
 # -- yq is a lightweight and portable
 # -- command-line YAML processor
 # --------------------------------------
 - name: Ensure yq is installed
 become: yes
 get_url:
 url: "https://github.com/mikefarah/yq/releases/download/{{ yq.version }}/{{ yq.binary }}"
 dest: /usr/bin/yq
 mode: "0777"

 - name: Ensure openssl is installed
 package:
 name: openssl
 state: present
 tags: packages

Then we will generate a certificate:

- name: Generate openssl certificate
 block:
 - name: Generate an OpenSSL private key
 community.crypto.openssl_privatekey:
 path: "{{ working_dir }}/{{ username }}.key"
 size: 2048

 - name: Generate an OpenSSL Certificate Signing Request
 community.crypto.openssl_csr:
 path: "{{ working_dir }}/{{ username }}.csr"
 privatekey_path: "{{ working_dir }}/{{ username }}.key"
 common_name: "{{ username }}"

 - name: Generate an OpenSSL certificate signed with your own CA certificate
 become: yes
 community.crypto.x509_certificate:
 path: "{{ working_dir }}/{{ username }}.crt"
 csr_path: "{{ working_dir }}/{{ username }}.csr"
 ownca_path: /etc/kubernetes/pki/ca.crt
 ownca_privatekey_path: /etc/kubernetes/pki/ca.key
 provider: ownca
 entrust_not_after: "+{{ certificate_expires_in }}d"

 tags: openssl

When the certificate is ready we need to add a user to our cluster

- name: Add user to cluster
 block:
 # --------------------------------------
 # -- Get k8s server from admin.conf
 # --------------------------------------
 - name: Get k8s server
 shell: yq e '.clusters[0] | select(.name == "{{ cluster }}").cluster.server' "{{ k8s_config_path }}"
 register: kubernetes_server_output
 # --------------------------------------
 # -- Get k8s certificate authority data
 # -- from admin-conf
 # --------------------------------------
 - name: Get k8s certificate authority data
 shell: yq e '.clusters[0] | select(.name == "{{ cluster }}").cluster.certificate-authority-data' "{{ k8s_config_path }}"
 register: kubernetes_cad_output

 - name: Get user cert data
 shell: cat "{{ working_dir }}/{{ username }}.crt" | base64 -w 0
 register: user_cert_data_output

 - name: Get user key data
 shell: cat "{{ working_dir }}/{{ username }}.key" | base64 -w 0
 register: user_key_data_output

 - name: Set variables for template
 set_fact:
 kubernetes_server: "{{ kubernetes_server_output.stdout }}"
 kubernetes_cad: "{{ kubernetes_cad_output.stdout }}"
 user_cert_data: " {{ user_cert_data_output.stdout }}"
 user_key_data: " {{ user_key_data_output.stdout }}"

 - name: Create k8s user
 ansible.builtin.shell: |
 kubectl config set-credentials "{{ username }}"\
 --client-certificate="{{ working_dir }}/{{ username }}.crt" \
 --client-key="{{ working_dir }}/{{ username }}.key"
 notify: remove certificates

 - name: Set user context
 ansible.builtin.shell: |
 kubectl config set-context "{{ username }}@{{ cluster }}" \
 --cluster={{ cluster }} --user="{{ username }}"

 - name: Create config file from template
 template:
 src: config.j2
 dest: "{{ working_dir }}/config"

 - name: Storing config on the local machine
 ansible.builtin.fetch:
 src: "{{ working_dir }}/config"
 dest: ./
 flat: yes
 tags: config

As you can see, in the step "Create k8s user" I'm notifying the handler that's gonna remove certs and configs after the run. If you wanna save them, just comment the string notify: remove certificates

Now we've left with the last block:

- name: Bind user to role
 block:
 - name: Generate role binding yaml
 template:
 src: role-binding.j2
 dest: "{{ working_dir }}/{{ username }}.yaml"

 - name: Apply role binding manifest
 shell: kubectl apply -f "{{ working_dir }}/{{ username }}.yaml"
 tags: add_user

It's gonna gerenate k8s manifest for adding a RoleBinding or ClusterRoleBinding nad apply it.

To run the playbook, simply do:

$ ansible-playbook ./kubernetes-create-user.yaml -i ${PATH_TO_INVENTORY}
# -- then copy config file
$ cp config ~/.kube/config
# chown $USER ~/.kube/config
# -- to check that everything is great
# -- run the following and ensure 
# -- you get all resources from you cluster
$ kubectl get all --all-namespaces 

This role doesn't support adding user groups, so I would be happy if anybody will contribute. Or I will do it myself one day.