Ansible controlled Let's Encrypt Challenge Stuck at 'Pending', No Certificates Generated

My domain is: metoz.de

  • I ran this command: ansible playbook.
  • It produced this output: (Provide the relevant part of the Ansible output, particularly any error messages from the acme_certificate module)
  • My web server is: Apache/2.4.52
  • The operating system my web server runs on is: Ubuntu 22.04.3 LTS
  • My hosting provider: self-hosted, cloudflare (but even disabled, only dns)
  • I can login to a root shell on my machine: Yes
  • I'm using a control panel to manage my site: No
  • The version of my client is: Ansible 2.15.3

Problem Description:

I am attempting to use Ansible to automate the generation and installation of Let's Encrypt SSL certificates for my domain metoz.de. The Ansible playbook uses the acme_certificate module to request a certificate.

I have successfully generated an account key and CSR, and I have set up the required directory and file for the HTTP-01 challenge (/.well-known/acme-challenge/). The challenge token is correctly placed in this directory and accesible via GET. However, the certificate generation does not proceed beyond the "pending" status, as reported by the Ansible output.

Here's a snippet of the data from the Ansible debug output for le_challenge:

"authorizations": {
    "metoz.de": {
        "challenges": [
            {
                "status": "pending",
                "token": "...",
                "type": "http-01",
                "url": "..."
            },
            ...
        ],
        "expires": "2023-09-16T10:56:54Z",
        "identifier": {
            "type": "dns",
            "value": "metoz.de"
        },
        "status": "pending",
        ...
    }
}

I have waited for a significant amount of time, but the status remains "pending" and the certificate files are not generated. Could this issue be related to DNS settings or something else? I am using Cloudflare for DNS.

I appreciate any help or guidance you can provide.

These are the ansible tasks:

---
- name: Install OpenSSL package
  apt:
    name: openssl
    state: present

- name: Ensure the required directories exist
  file:
    path: "{{ item }}"
    state: directory
    mode: '0755'
  loop:
    - /etc/letsencrypt
    - /etc/letsencrypt/account
    - /etc/letsencrypt/csr
    - /etc/letsencrypt/cert

- name: Generate a private key for each domain
  community.crypto.openssl_privatekey:
    path: "/etc/letsencrypt/account/{{ item }}.key"
    type: RSA
  loop: "{{ domains }}"

- name: Generate a CSR for each domain
  openssl_csr:
    path: "/etc/letsencrypt/csr/{{ item }}.csr"
    privatekey_path: "/etc/letsencrypt/account/{{ item }}.key"
    common_name: "{{ item }}"
  loop: "{{ domains }}"

- name: Create a challenge for each domain
  acme_certificate:
    account_key_src: "/etc/letsencrypt/account/{{ item }}.key"
    account_email: "{{ email }}"
    csr: "/etc/letsencrypt/csr/{{ item }}.csr"
    dest: "/etc/letsencrypt/cert/{{ item }}.crt"
    acme_directory: "{{ letsencrypt_url }}"
    acme_version: "{{ acme_version }}"
    terms_agreed: yes
  loop: "{{ domains }}"
  register: le_challenge

- name: Ensure the challenge directory exists and has correct permissions
  file:
    path: "/var/www/{{ item }}/.well-known/acme-challenge/"
    state: directory
    owner: www-data
    group: www-data
    mode: '0755'
  loop: "{{ domains }}"

- name: Create challenge files for all domains
  copy:
    content: "{{ le_challenge['results'][0]['challenge_data'][item]['http-01']['resource_value'] }}"
    dest: "/var/www/{{ item }}/{{ le_challenge['results'][0]['challenge_data'][item]['http-01']['resource'] }}"
  loop: "{{ domains }}"

- name: Validate the challenge and retrieve the cert
  acme_certificate:
    account_key_src: "/etc/letsencrypt/account/{{ item }}.key"
    account_email: "{{ email }}"
    csr: "/etc/letsencrypt/csr/{{ item }}.csr"
    dest: "/etc/letsencrypt/cert/{{ item }}.crt"
    challenge: "{{ acme_challenge_type }}"
    acme_directory: "{{ letsencrypt_url }}"
    acme_version: "{{ acme_version }}"
    terms_agreed: yes
    data: "{{ le_challenge }}"
  loop: "{{ domains }}"
  register: le_result

Steps performed

  1. Installation of required packages: OpenSSL is installed on the server.
  2. Directory structure: All required directories for Let's Encrypt have been created.
  3. File Permissions: Directory and file permissions have been set in the correct way, including ownership of www-data.
  4. Private Keys and CSR: Private keys and CSR (Certificate Signing Request) were generated for each domain.
  5. ACME Challenge: ACME challenges were created and placed in the .well-known/acme-challenge/ directories. Permissions for these directories were set to www-data.
    6 Debugging: Various debugging steps were taken, including checking the output of le_challenge and le_result.
  6. ACME version and URL: The ACME version and URL used are set correctly.

I spent 5 hours on this and now I've given up :frowning: any idea anyone? any help is so much appreciated! :person_tipping_hand:

In a previous, rather recent, thread (Not able to validate my let's encrypt dns Challenge stay in pending) it was as simple as to make Ansible trigger the validation attempt. How that poster did that? Beats me.

1 Like

thanks @Osiris, but isnt that a DNS challenge? i'm doing http :confused:

1 Like

Sure, but a challenge is a challenge. If it's not triggered, it'll keep staying in the pending state. That's the same for all challenges.

What comes after you trigger the challenge is different indeed. Success? Failure? If failure, then what? That's obviously different. (And also what's before triggering it of course, that's different between challenges too.)

1 Like

yes sorry, of course it doesnt matter from where it's challenged.

I'm just wondering why this doesn't work, because it follows the official Ansible docs. The tasks first deal with creating keys etc, then save the token, then start the verification/challenge which should be the LE trigger step in the end. but it just says so in the code and somehow runs green - yet nothing seems to happen further. no errors, no warnings. how can I see if something reaches LE at all? i can't, i think....

I don't want to take any manual steps, and implementing a DNS challenge requires more automation steps, because I then have to write the entries to Cloudflare. The HTTP challenge is actually easier to set up - and above all, it has always worked that way. argh. anyways, I mean, if I can't do it with http, then I'll have to go the TXT record route.

I don't know Ansible at all really but I agree with Osiris that it does not look like you are triggering the challenge. I found these steps at digital ocean perhaps this helps you see what is missing.

4 Likes

It needs the "I'm ready to be challenged now" step.
[the prep work seems to have been done]

2 Likes

Luckily, the Ansible documentation for the acme_certificate module is very helpful! [/sarcasm]...

The way that module has to be used looking at the examples, it's just terrible? What determines the action undertaken with each piece of code? Apparently, when you have account_key_content, csr and dest as inputs it outputs a challenge? The f*ck? And, MAYBE, if you use data with the challenge as input, it's supposed to trigger the challenge? But I'm guessing here, but it's never literally mentioned in the docs.

How can the authors of that piece of .. live with themselves?

I'm guessing OP might have more luck on an Ansible specific support channel..

1 Like

Sarcasm never ends!
LOL

2 Likes

Huh...

It seems the second run is enough to trigger the validation? But it doesn't? :thinking: :roll_eyes:

Weird. I'm giving up.

1 Like