Score:0

Generate SSL certificate with let's encrypt (dns-01 challenge)

mv flag

I'm trying to generate an SSL certificate with Ansible for *.rasp.example.com and rasp.example.com.

I already have a "working" solution (No errors when deploying), but when I try to compare it with certbot, I have some csr, crt, key whereas certbot only returns 2 pem files (key and cert).

When it comes to the browser, I have some issue, for example, https works for rasp.example.com but not for *.rasp.example.com even if I add an alt DNS name.

My role :

- name: Certificate - set facts
  ansible.builtin.set_fact:
      account_key_path: /etc/ssl/private/account.key
      key_path: /etc/ssl/private/rasp.example.com.key

      crt_path: /etc/ssl/certs/rasp.example.com.crt
      crt_fullchain_path: /etc/ssl/certs/rasp.example.com-fullchain.crt

      csr_path: /etc/ssl/certs/rasp.example.com.csr

      acme_directory: https://acme-v02.api.letsencrypt.org/directory
      acme_challenge_type: dns-01
      acme_version: 2
      acme_email: [email protected]

      zone: example.com
      subdomain: rasp

- name: Generate let's encrypt account key
  community.crypto.openssl_privatekey:
      path: "{{ account_key_path }}"

- name: Create private key (RSA, 4096 bits)
  community.crypto.openssl_privatekey:
      path: "{{ key_path }}"

- name: Generate an OpenSSL Certificate Signing Request
  community.crypto.openssl_csr:
      path: "{{ csr_path }}"
      privatekey_path: "{{ key_path }}"
      common_name: "*.{{ subdomain }}.{{ zone }}"
      subject_alt_name: "DNS:{{ subdomain + '.' + zone }}" # for rasp.example.com

- name: Make sure account exists and has given contacts. We agree to TOS.
  community.crypto.acme_account:
      account_key_src: "{{ account_key_path }}"
      acme_directory: "{{ acme_directory }}"
      acme_version: "{{ acme_version }}"
      state: present
      terms_agreed: true
      contact:
          - mailto:[email protected]

- name: Create a challenge using a account key file.
  community.crypto.acme_certificate:
      account_key_src: "{{ account_key_path }}"
      account_email: "{{ acme_email }}"
      src: "{{ csr_path }}"
      fullchain_dest: "{{ crt_fullchain_path }}"
      challenge: dns-01
      acme_directory: "{{ acme_directory }}"
      acme_version: 2
      terms_agreed: true
      remaining_days: 60
      force: true
  register: challenge

- name: Certificate does not exists or needs to be renewed
  when: challenge["challenge_data"] is defined and (challenge["challenge_data"] | length > 0)
  block:
      - name: Set challenge data
        ansible.builtin.set_fact:
            challenge: "{{ challenge }}"

      - name: Upload OVH credentials
        ansible.builtin.template:
            src: ovh.conf.j2
            dest: /root/.ovh.conf
            owner: root
            group: root
            mode: 0600

      - name: Create DNS challenge record on OVH
        ansible.builtin.script:
            cmd: "dns.py create {{ zone }} TXT -t {{ item.value['dns-01'].resource_value }} -s {{ item.value['dns-01'].resource }}.{{ subdomain }}"
        args:
            executable: python3
            chdir: /root
        with_dict: "{{ challenge['challenge_data'] }}"

      - name: Let the challenge be validated and retrieve the cert and intermediate certificate
        community.crypto.acme_certificate:
            account_key_src: "{{ account_key_path }}"
            account_email: "{{ acme_email }}"
            src: "{{ csr_path }}"
            dest: "{{ crt_path }}"
            fullchain_dest: "{{ crt_fullchain_path }}"
            challenge: dns-01
            acme_directory: "{{ acme_directory }}"
            acme_version: 2
            terms_agreed: true
            remaining_days: 60
            data: "{{ challenge }}"
        notify:
            - Delete DNS challenge record on OVH

I use OVH as DNS, I created a simple .py script that add/delete a TXT record.

Also, I use NGINX as web server :

listen 443 ssl;
    ssl_certificate     /etc/ssl/certs/rasp.example.com.crt;
    ssl_certificate_key /etc/ssl/private/rasp.example.com.key;
    ssl_protocols       TLSv1 TLSv1.1 TLSv1.2;
    ssl_ciphers         HIGH:!aNULL:!MD5;

Am I doing something wrong here ?

Score:0
by flag

You are using the wildcard certificate for *.rasp.example.com, but not for rasp.example.com You should include both the wildcard and the base domain in the subject alternative names.

Update openssl_csr task in Ansible like that:

- name: Generate an OpenSSL Certificate Signing Request
  community.crypto.openssl_csr:
      path: "{{ csr_path }}"
      privatekey_path: "{{ key_path }}"
      common_name: "*.{{ subdomain }}.{{ zone }}"
      subject_alt_name:
        - "DNS:*.{{ subdomain }}.{{ zone }}"
        - "DNS:{{ subdomain }}.{{ zone }}" # for rasp.example.com

then in your Nginx configuration it is better to use the fullchain certificate instead of just the certificate:

listen 443 ssl;
ssl_certificate     /etc/ssl/certs/rasp.example.com-fullchain.crt;
ssl_certificate_key /etc/ssl/private/rasp.example.com.key;
ssl_protocols       TLSv1 TLSv1.1 TLSv1.2;
ssl_ciphers         HIGH:!aNULL:!MD5;
tholeb avatar
mv flag
Thank you for your answer. I'll look into it (when i'm not rate limited anymore). Also, why do I have to specify it again in the `subject_alt_name` when it's already defined in the `common_name` ?
I sit in a Tesla and translated this thread with Ai:

mangohost

Post an answer

Most people don’t grasp that asking a lot of questions unlocks learning and improves interpersonal bonding. In Alison’s studies, for example, though people could accurately recall how many questions had been asked in their conversations, they didn’t intuit the link between questions and liking. Across four studies, in which participants were engaged in conversations themselves or read transcripts of others’ conversations, people tended not to realize that question asking would influence—or had influenced—the level of amity between the conversationalists.