Skip to content

Instantly share code, notes, and snippets.

@langburd
Last active May 21, 2024 18:43
Show Gist options
  • Save langburd/d7ff3a5f622e2c9f5cf52607de32fcc1 to your computer and use it in GitHub Desktop.
Save langburd/d7ff3a5f622e2c9f5cf52607de32fcc1 to your computer and use it in GitHub Desktop.
An Ansible playbook for manually updating certificates used by some K8S components installed with Kubespray. Valid for K8S version <1.16.
---
- name: Generate new kubelet certificates
hosts: k8s-cluster
gather_facts: true
serial: 1
vars:
valid_point_1: 10d
valid_point_2: 20d
tasks:
- name: Disable docker repository
yum_repository:
name: docker-engine
description: Docker-Engine Repository
baseurl: https://yum.dockerproject.org/repo/main/centos/7
enabled: no
gpgcheck: yes
keepcache: "0"
gpgkey: https://yum.dockerproject.org/gpg
file: docker
state: absent
when: ansible_distribution in ["CentOS","RedHat"]
- name: Check if the certificate pem file exists
stat:
path: /var/lib/kubelet/pki/kubelet-client-current.pem
register: kubelet_pem
- name: Insure the Python cryptography library is installed
pip:
name: cryptography
when: kubelet_pem.stat.exists
- name: Check validity of existing pem file
openssl_certificate_info:
path: /var/lib/kubelet/pki/kubelet-client-current.pem
valid_at:
point_1: "+{{ valid_point_1 }}"
point_2: "+{{ valid_point_2 }}"
register: cert_info
when: kubelet_pem.stat.exists
- debug:
msg: "Certificate is not valid before: {{ cert_info.not_before }}"
when: kubelet_pem.stat.exists
- debug:
msg: "Certificate is not valid after: {{ cert_info.not_after }}"
when: kubelet_pem.stat.exists
- debug:
msg: "Certificate is expired: {{ cert_info.expired }}"
when: kubelet_pem.stat.exists
- debug:
msg: "Cert is valid in {{ valid_point_1 }}: {{ cert_info.valid_at.point_1 }}"
when: kubelet_pem.stat.exists
- debug:
msg: "Cert is valid in {{ valid_point_2 }}: {{ cert_info.valid_at.point_2 }}"
when: kubelet_pem.stat.exists
- name: Check kubelet.conf for hardcoded certificate and key
lineinfile:
path: /etc/kubernetes/kubelet.conf
regexp: "^.*client-certificate.*$"
line: " client-certificate: /var/lib/kubelet/pki/kubelet-client-current.pem"
state: present
check_mode: yes
register: cert_is_hardcoded
no_log: true
- name: Replace hardcoded client certificate in kubelet.conf with path to client certificate file
replace:
path: /etc/kubernetes/kubelet.conf
regexp: "client-certificate-data.*"
replace: "client-certificate: /var/lib/kubelet/pki/kubelet-client-current.pem"
backup: yes
when: cert_is_hardcoded.changed
register: kubelet_conf
no_log: true
- name: Replace hardcoded client key in kubelet.conf with path to client key file
replace:
path: /etc/kubernetes/kubelet.conf
regexp: "client-key-data.*"
replace: "client-key: /var/lib/kubelet/pki/kubelet-client-current.pem"
backup: yes
when: cert_is_hardcoded.changed
register: kubelet_conf
no_log: true
# Block of regenerating certificates
- name: Generate new kubelet certificates
block:
- name: Check that the ca.key exists
stat:
path: /etc/kubernetes/ssl/ca.key
register: ca_key_on_node
- name: Slurp ca.key from first master node
slurp:
src: /etc/kubernetes/ssl/ca.key
register: ca_key
delegate_to: "{{ groups['kube-master'] | first }}"
when: not ca_key_on_node.stat.exists or 'kube-node' in group_names
no_log: true
- name: Copy ca.key to the node
lineinfile:
path: /etc/kubernetes/ssl/ca.key
create: yes
line: "{{ ca_key['content'] | b64decode }}"
when: ca_key is defined and ca_key | length > 0 and (not ca_key_on_node.stat.exists or 'kube-node' in group_names)
no_log: true
- name: Generate new kubelet configuration for the node
shell:
args:
cmd: /usr/local/bin/kubeadm alpha kubeconfig user --org system:nodes --client-name system:node:$(hostname)
register: new_kubelet_configuration
no_log: true
- name: Get client_certificate from generated kubelet configuration
set_fact:
client_certificate: "{{ new_kubelet_configuration.stdout_lines[17] | regex_replace('client-certificate-data: (.*$)', '\\1') | b64decode }}"
when: new_kubelet_configuration.stdout_lines is defined
- name: Get client_key from generated kubelet configuration
set_fact:
client_key: "{{ new_kubelet_configuration.stdout_lines[18] | regex_replace('client-key-data: (.*$)', '\\1') | b64decode }}"
when: new_kubelet_configuration.stdout_lines is defined
no_log: true
- name: Set desired pem file path
set_fact:
pem_file_path: /var/lib/kubelet/pki/kubelet-client-{{ ansible_date_time.date }}-{{ ansible_date_time.hour }}-{{ ansible_date_time.minute }}-{{ ansible_date_time.second }}.pem
- name: Create pem file
lineinfile:
path: "{{ pem_file_path }}"
create: yes
line: "{{ client_certificate }}{{ client_key }}"
when: pem_file_path is defined and client_certificate is defined and client_key is defined
no_log: true
- name: Link created pem file to 'kubelet-client-current.pem'
file:
src: "{{ pem_file_path }}"
dest: /var/lib/kubelet/pki/kubelet-client-current.pem
state: link
force: yes
register: kubelet_client_current_pem
when: pem_file_path is defined
- name: Find old PEM files
find:
paths:
- /var/lib/kubelet/pki/
file_type: file
contains: "-----BEGIN CERTIFICATE-----"
patterns:
- "kubelet-client-*.pem"
excludes:
- "{{ kubelet_client_current_pem.src | basename }}"
register: found_old_pems
when: kubelet_client_current_pem.src is defined
- name: Delete old PEM files
file:
path: "{{ item.path }}"
state: absent
with_items: "{{ found_old_pems.files }}"
when: kubelet_client_current_pem.src is defined and found_old_pems.files is defined
always:
- name: Delete ca.key from the worker nodes
file:
path: /etc/kubernetes/ssl/ca.key
state: absent
when: not ca_key_on_node.stat.exists
no_log: true
# Conditions for the block
when: not kubelet_pem.stat.exists or cert_is_hardcoded.changed or cert_info.expired or not cert_info.valid_at.point_1 or not cert_info.valid_at.point_2
- name: Restart Kubelet service
service:
name: kubelet
state: restarted
when: kubelet_conf.changed or kubelet_client_current_pem.changed
# Usage:
# ansible-playbook -vv -i inventory/CLUSTER_INVENTORY/hosts.cfg includes/cert_kubelet_renew.yml
# ansible-playbook -vv -i inventory/CLUSTER_INVENTORY/hosts.cfg includes/cert_kubelet_renew.yml --limit NODE_NAME
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment