# Add Kubernetes users with Ansible Hi! In the [previous article](https://gist.github.com/allanger/84db2647578316f8e721f7219052788f), 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.