0

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: contact@example.com

      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:contact@tholeb.fr

- 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 ?

tholeb
  • 31
  • 1
  • 6

1 Answers1

0

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;
Saxtheowl
  • 1,112
  • 5
  • 8
  • 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` ? – tholeb Apr 22 '23 at 22:03