diff --git a/hosts.yml b/hosts.yml index 4d7552d..28350f7 100644 --- a/hosts.yml +++ b/hosts.yml @@ -34,4 +34,5 @@ haproxy: haproxy_shared_ip: "10.10.2.60" haproxy_shared_ip_subnet: "/24" haproxy_shared_virtual_router_id: 25 - \ No newline at end of file + haproxy_certbot_user: ansible + haproxy_private_key: "/home/ansible/.ssh/HaProxyPrivateKey" diff --git a/roles/haproxy/defaults/main.yml b/roles/haproxy/defaults/main.yml index 3eb307b..ad33d36 100644 --- a/roles/haproxy/defaults/main.yml +++ b/roles/haproxy/defaults/main.yml @@ -29,3 +29,14 @@ haproxy_bind_adapter: "eth0" haproxy_keepalived_adapter: "eth0" haproxy_keepalived_adapter_vlan: "" haproxy_keepalived_ip: 172.16.10.1/24 + +# Cerbot configuration +haproxy_certbot_enable: true +haproxy_certbot_user: "{{ ansible_user_id }}" + +# Private key file used for cluster member communication +haproxy_private_key: "/root/.ssh/HaProxyPrivateKey" + +# Certbot renewal timer +haproxy_certbot_renewal_timer_calendar: "*-*-* 01:00" +haproxy_certbot_renewal_random_delay: "" diff --git a/roles/haproxy/handlers/main.yml b/roles/haproxy/handlers/main.yml index 775d4e5..1f3beb2 100644 --- a/roles/haproxy/handlers/main.yml +++ b/roles/haproxy/handlers/main.yml @@ -1,26 +1,26 @@ --- # file: roles/haproxy/handlers/main.yml +- name: Daemon Reload + systemd: + daemon_reload: yes + - name: reload haproxy service: name: haproxy state: reloaded - become: yes - name: reload keepalived service: name: keepalived state: reloaded - become: yes - name: reload firewalld service: name: firewalld state: reloaded - become: yes - name: Restart SSH service: name: sshd state: restarted - become: yes diff --git a/roles/haproxy/tasks/certbot.yml b/roles/haproxy/tasks/certbot.yml new file mode 100644 index 0000000..cf7df14 --- /dev/null +++ b/roles/haproxy/tasks/certbot.yml @@ -0,0 +1,61 @@ +--- +# file: roles/haproxy/tasks/main.yml + +- name: Install certbot + pip: + name: certbot + executable: pip3 + state: latest + +- name: Place certbot script + template: + src: "certbot-renewal-cluster.j2" + dest: "/usr/local/sbin/certbot-renewal-cluster" + owner: root + mode: 755 + +- name: Deploy certbot renewal service + template: + src: certbot-renewal.service.j2 + dest: /etc/systemd/system/certbot-renewal.service + notify: Daemon Reload + +- name: Deploy borgbackups timer + template: + src: certbot-renewal.timer.j2 + dest: /etc/systemd/system/certbot-renewal.timer + notify: Daemon Reload + +- name: Generate ssh keypair + user: + name: "{{ haproxy_certbot_user }}" + generate_ssh_key: yes + ssh_key_type: ed25519 + ssh_key_bits: 4096 + ssh_key_file: "{{ haproxy_private_key }}" + ssh_key_passphrase: "" + force: no + +- name: Get the public key + slurp: + src: "{{ haproxy_private_key }}.pub" + register: slurped_pub_key + +- name: Decode the pub key and store as fact + set_fact: + haproxy_pub_key: "{{ slurped_pub_key.content | b64decode }}" + +- name: "Setup authorized key for the user with limited access" + authorized_key: + user: "{{ haproxy_certbot_user }}" + state: present + key: "{{ hostvars[item]['haproxy_pub_key'] }}" + loop: "{{ groups['haproxy']|difference([inventory_hostname]) }}" + +- name: Ensure ssh host key known + lineinfile: + dest: "/home/{{ haproxy_certbot_user }}/.ssh/known_hosts" + create: yes + state: present + line: "{{ lookup('pipe', 'ssh-keyscan -t ecdsa -p22 ' + hostvars[item]['ansible_host'] + ' ' + '2>/dev/null', errors='warn') }}" + loop: "{{ groups['haproxy']|difference([inventory_hostname]) }}" diff --git a/roles/haproxy/tasks/main.yml b/roles/haproxy/tasks/main.yml index c795df2..ef3ae0e 100644 --- a/roles/haproxy/tasks/main.yml +++ b/roles/haproxy/tasks/main.yml @@ -8,7 +8,7 @@ - NetworkManager state: latest -- name: Install packages +- name: Install haproxy cluster packages package: name: - keepalived @@ -16,6 +16,11 @@ - firewalld state: latest +- name: "### Certbot configuration ###" + include_tasks: + file: certbot.yml + when: haproxy_certbot_enable == true + - name: Add VLAN nmcli: conn_name: "{{ haproxy_keepalived_adapter }}.{{ haproxy_keepalived_adapter_vlan }}" diff --git a/roles/haproxy/templates/certbot-renewal-cluster.j2 b/roles/haproxy/templates/certbot-renewal-cluster.j2 new file mode 100644 index 0000000..8072660 --- /dev/null +++ b/roles/haproxy/templates/certbot-renewal-cluster.j2 @@ -0,0 +1,45 @@ +#!/bin/bash + +# Prereqs +# Setup private key between members + +SERVERS=( +{% for host in groups['haproxy'] %} +{{ hostvars[host].ansible_host }} +{% endfor %} +) +VIRTUAL_IP=( {{ haproxy_shared_ip }} ) +USER={{ haproxy_certbot_user }} +PRIVATE_KEY={{ haproxy_private_key }} + +TARFILE=letsencrypt.tar +TARFILE_COMPRESS=$TARFILE'.gz' + +if [[ $(hostname -I)[*] =~ $VIRTUAL_IP ]]; then + echo "Current master - Processing renewals" + certbot renew + + # tar the letsencrypt directory for transferring to other members with symlinks + tar cfP $TARFILE /etc/letsencrypt/ + + # Add each letsencrypt cert to the tarball + for cert in /etc/letsencrypt/live/*/ ; + do + tar ufP $TARFILE /etc/haproxy/ssl/$(basename $cert).pem + done + + # Compress the file for transfer + gzip -f9 $TARFILE + + # Update the other members of the cluster + for SERVER in "${SERVERS[@]}" + do + if [[ ! $(hostname -I)[*] =~ $SERVER ]]; then + # Transfer the files to the backup server + sudo -u $USER scp -i $PRIVATE_KEY $TARFILE_COMPRESS $USER@$SERVER:~ + + # Deploy the current letsencrypt config/certs + sudo -u $USER ssh -i $PRIVATE_KEY $USER@$SERVER "sudo rm -rf /etc/letsencrypt; sudo tar xzfP $TARFILE_COMPRESS && sudo rm $TARFILE_COMPRESS;sudo systemctl reload haproxy" + fi + done +fi diff --git a/roles/haproxy/templates/certbot-renewal.service.j2 b/roles/haproxy/templates/certbot-renewal.service.j2 new file mode 100644 index 0000000..fdf5647 --- /dev/null +++ b/roles/haproxy/templates/certbot-renewal.service.j2 @@ -0,0 +1,5 @@ +[Unit] +Description=Certbot Renewal Service +[Service] +Type=oneshot +ExecStart="/usr/local/sbin/certbot-renewal-cluster" diff --git a/roles/haproxy/templates/certbot-renewal.timer.j2 b/roles/haproxy/templates/certbot-renewal.timer.j2 new file mode 100644 index 0000000..f894a07 --- /dev/null +++ b/roles/haproxy/templates/certbot-renewal.timer.j2 @@ -0,0 +1,10 @@ +[Unit] +Description=Certbot Renewal Timer +[Timer] +WakeSystem=false +OnCalendar={{ haproxy_certbot_renewal_timer_calendar }} +{% if haproxy_certbot_renewal_random_delay != '' %} +RandomizedDelaySec={{ haproxy_certbot_renewal_random_delay }} +{% endif %} +[Install] +WantedBy=timers.target