I use Hashicorp Vault as my primary secrets management system. Mostly because most of my on-premise OpenShift customers use it as well. Here’s how to install it on a RHEL 9.6 box using ansible.
One of the prerequisites is to get certbot installed on the host and configured to use DNS challenges. Why DNS challenges? Because I don’t like port 80 being open. I find the DNS challenge more palatable. Here’s the tasks for setting it up.
---
- name: Install required packages for certbot
ansible.builtin.package:
name: "{{ item }}"
state: present
loop:
- certbot
- python3-certbot-dns-cloudflare
- name: Create dns credentials file
ansible.builtin.copy:
dest: /etc/letsencrypt/cloudflare.ini
content: |
dns_cloudflare_api_token = {{ dns_cloudflare_api_token }}
mode: '0600'
when: dns_cloudflare_api_token is defined
Here’s the playbook for the vault install.
---
- name: Install and configure HashiCorp Vault with Let's Encrypt certs
hosts: vault
become: true
vars:
vault_domain: vault.lab.snimmo.com
certbot_credentials: /etc/letsencrypt/cloudflare.ini
vault_cert_dir: /etc/vault/certs
deploy_hook_script: /usr/local/bin/deploy-vault-certs.sh
tasks:
- name: Add HashiCorp repository
ansible.builtin.command: >
dnf config-manager --add-repo https://rpm.releases.hashicorp.com/RHEL/hashicorp.repo
args:
creates: /etc/yum.repos.d/hashicorp.repo
- name: Install Vault
ansible.builtin.package:
name: vault
state: present
- name: Request cert using certbot and Cloudflare DNS # noqa no-changed-when
ansible.builtin.command: >
certbot certonly --non-interactive --preferred-challenges dns-01
--dns-cloudflare
--dns-cloudflare-credentials {{ certbot_credentials }}
--register-unsafely-without-email --agree-tos
-d {{ vault_domain }}
- name: Create Vault certs directory
ansible.builtin.file:
path: "{{ vault_cert_dir }}"
state: directory
owner: vault
group: vault
mode: '0750'
- name: Create deploy hook script for Vault
ansible.builtin.copy:
dest: "{{ deploy_hook_script }}"
mode: '0750'
owner: root
group: root
content: |
#!/bin/bash
cp /etc/letsencrypt/live/{{ vault_domain }}/fullchain.pem {{ vault_cert_dir }}/
cp /etc/letsencrypt/live/{{ vault_domain }}/privkey.pem {{ vault_cert_dir }}/
chown vault:vault {{ vault_cert_dir }}/*
chmod 600 {{ vault_cert_dir }}/*
systemctl restart vault
- name: Ensure Vault service is started and enabled
ansible.builtin.service:
name: vault
state: started
enabled: true
- name: Create vault config
ansible.builtin.template:
src: vault.hcl.j2
dest: /etc/vault.d/vault.hcl
mode: '0644'
notify: restart_vault
- name: Allow Vault ports on firewall
ansible.posix.firewalld:
zone: public
port: "{{ item }}"
state: enabled
permanent: true
immediate: true
loop:
- 8200/tcp
- 8201/tcp
- name: Create systemd service for certbot renew
ansible.builtin.copy:
dest: /etc/systemd/system/vault-certbot-renew.service
owner: root
group: root
mode: '0644'
content: |
[Unit]
Description=Vault Certbot Renewal with Deploy Hook
Wants=network-online.target
After=network-online.target
[Service]
Type=oneshot
ExecStart=/usr/bin/certbot renew --cert-name {{ vault_domain }} --deploy-hook '{{ deploy_hook_script }}'
notify: reload_systemd_daemon
- name: Create systemd timer for certbot renew
ansible.builtin.copy:
dest: /etc/systemd/system/vault-certbot-renew.timer
owner: root
group: root
mode: '0644'
content: |
[Unit]
Description=Timer for Certbot Renewal for Vault
[Timer]
OnCalendar=daily
Persistent=true
[Install]
WantedBy=timers.target
notify: reload_systemd_daemon
- name: Enable and start certbot-renew.timer
ansible.builtin.systemd:
name: vault-certbot-renew.timer
enabled: true
state: started
handlers:
- name: reload_systemd_daemon
ansible.builtin.command: systemctl daemon-reexec
- name: restart_vault
ansible.builtin.service:
name: vault
state: restarted