Playbook #5

/home/zuul/src/opendev.org/opendev/system-config/playbooks/letsencrypt.yaml

Report Status CLI Date Duration Controller User Versions Hosts Plays Tasks Results Files Records
18 Jul 2025 20:47:32 +0000 00:00:24.78 bridge99.opendev.org root Ansible 2.15.13 ara 1.7.2 (client), 1.7.2 (server) Python 3.10.12 2 5 47 47 33 0

File: /home/zuul/src/opendev.org/opendev/system-config/playbooks/roles/letsencrypt-acme-sh-install/tasks/main.yaml

- name: Check status of acme.sh script
  stat:
    path: /opt/acme.sh/acme.sh
    get_checksum: true
    checksum_algorithm: sha256
  register: acme_sh_stat

- name: Install acme.sh if not already up to date
  when: not acme_sh_stat.stat.exists or acme_sh_stat.stat.checksum != "5c298a2bd5f90635aef8d013b02b25f34027ad0cb2cef2bdca68f3d13b931216"
  block:
    # We only want to update the clone and checkout if things are not already
    # in place or at the expected versions. This avoids unnecessary daily
    # git operations and makes us more resilient to full disks.
    - name: Install acme.sh client
      git:
        repo: https://github.com/acmesh-official/acme.sh
        dest: /opt/acme.sh
        # Pinned due to https://github.com/acmesh-official/acme.sh/issues/4416
        version: 3.0.5
        recursive: false
      register: clone_acmesh_result
      until: clone_acmesh_result is not failed
      retries: 3
      delay: 2

    # Temporary https://github.com/acmesh-official/acme.sh/issues/4659 fix
    # until we can upgrade to 3.0.6 or later
    - name: Patch for issue 4659
      shell: |
        git -C /opt/acme.sh cherry-pick 4c30250
        git -C /opt/acme.sh cherry-pick 327e2fb

- name: Install letsencrypt group
  group:
    name: letsencrypt
    state: present
    gid: "{{ letsencrypt_gid | default(omit) }}"

- name: Install driver script
  copy:
    # Because this is a fily copy and not git operations with multiple states
    # Ansible should successfully determine that the file doesn't need to be
    # copied after the initial copy unless the file changes.
    src: driver.sh
    dest: /opt/acme.sh/driver.sh
    mode: 0755

- name: Setup log directory
  file:
    path: /var/log/acme.sh
    state: directory
    mode: 0755

- name: Setup log rotation
  include_role:
    name: logrotate
  vars:
    logrotate_file_name: /var/log/acme.sh/acme.sh.log

- name: Setup top level cert directory
  file:
    path: /etc/letsencrypt-certs
    state: directory
    owner: root
    group: letsencrypt
    mode: u=rwx,g=rx,o=,g+s

- name: Create acme.sh config directory
  file:
    path: /root/.acme.sh
    state: directory
    owner: root
    group: root
    mode: u=rwx,g=rx,o=

# An implementation note on accounts: We could share an account key
# across all our hosts and this would be the logical place to deploy
# it.  However, really the only thing you can do with an account key
# is revoke a certificate if you lose the private key.  It makes more
# sense to have an account per host with key material that never
# leaves the host rather than keeping a global secret that, if leaked,
# could revoke all keys simultaneously.

- name: Check for account email
  assert:
    that: letsencrypt_account_email is defined

- name: Configure account email
  lineinfile:
    path: /root/.acme.sh/account.conf
    regexp: '^ACCOUNT_EMAIL='
    line: 'ACCOUNT_EMAIL={{ letsencrypt_account_email }}'
    create: true
  register: account_email

# If we updated the email and we have existing accounts, we should
# update the address.

# NOTE(ianw) 2020-03-04 : acme.sh dumps the 200 response json from the
# ACME api when creating an account into this file to keep track of
# the account-id.  However, it doesn't actually then update it in
# response to --updateaccount although the details in the account
# *are* correctly updated.  It doesn't make a difference to ongoing
# operation since all that cares about is the unchanging id, but can
# be confusing if you check this and don't see an updated email
# address.  I have filed:
# https://github.com/acmesh-official/acme.sh/pull/2769
- name: Check for existing account setup
  stat:
    path: '{{ item }}'
  loop:
    - /root/.acme.sh/ca/acme-v02.api.letsencrypt.org/account.json
    - /root/.acme.sh/ca/acme-staging-v02.api.letsencrypt.org/account.json
  register: existing_accounts

- name: Run account update
  shell: |
    /opt/acme.sh/acme.sh --debug --updateaccount
  when: account_email.changed and (existing_accounts.results | selectattr('stat.exists') | map(attribute='item') | list | length > 0)