acme_certificate with *.domain.name

470 views
Skip to first unread message

Patric Buskas

unread,
Apr 5, 2020, 3:30:26 AM4/5/20
to ansible...@googlegroups.com
I'm having trouble creating a wildcard certificate for a domain with acme_certificate.
I've followed this guide and successfully produced certificate (I changed the "shell"-part with appropriate openssl-modules)

I want to create a certificate with *.domain.name and domain.name but it's not working with that playbook.
I get stuck in the task "Implement http-01 challenge files"
Obviously... You can't name a file with a '*' in it's name.
How do I get around this problem within ansible and being idempotent or do I have to do a certbot shell-command to the host listening on port 80 to create the certificate *.domain.name?

Felix Fontein

unread,
Apr 5, 2020, 3:54:01 AM4/5/20
to ansible...@googlegroups.com
Hi,

you cannot use http-01 challenges for wildcard certificates. You need
to use the dns-01 challenge type for that. (This is a policy decision
by Let's Encrypt.)

How that works depends on how your DNS setup. Ansible needs to be able
to create/update the DNS TXT record _acme_challenge.domain.name for
this. (If you feel uncomfortable to give Let's Encrypt access to it,
you can also use a CNAME record and use
https://github.com/joohoi/acme-dns. Haven't tried that with Ansible
though.)

> <https://www.digitalocean.com/community/tutorials/how-to-acquire-a-let-s-encrypt-certificate-using-ansible-on-ubuntu-18-04>

Interesting, I haven't seen that. One remark: you should change the
step which creates the http-01 challenge files slightly by adding

when: item in acme_challenge_your_domain['challenge_data']

to it. (Also see the examples in the module docs:
https://docs.ansible.com/ansible/latest/modules/acme_certificate_module.html#examples)

> Obviously... You can't name a file with a '*' in it's name.

Actually, you can; you can even have a newline in a filename. But
that's never a good idea ;-) And won't help you here, since you can't
get wildcard certificates with the http-01 challenge.

Cheers,
Felix

Patric Buskas

unread,
Apr 8, 2020, 8:21:08 AM4/8/20
to ansible...@googlegroups.com
Thank you for the answer!
Unfortunately I still can't make acme_certificate to create the certificate.
I've changed the challenge to dns-01 and
 taken care of creating the _acme-challenge.domain.name vi REST api
 but when the last task is run I get:
"msg": "Authorization for dns:*.domain.name returned invalid:  CHALLENGE: dns-01 DETAILS: DNS problem: NXDOMAIN looking up TXT for _acme-challenge.domain.name - check that a DNS record exists for this domain;"
or
"msg": "Authorization for dns:domain.name returned invalid:  CHALLENGE: dns-01 DETAILS: DNS problem: NXDOMAIN looking up TXT for _acme-challenge.domain.name - check that a DNS record exists for this domain;"
alternately.

The DNS record exists * 2 before last task is run and as of the ansible output (-vvv) it's the same token.

Thank You!
Patric


--
You received this message because you are subscribed to the Google Groups "Ansible Project" group.
To unsubscribe from this group and stop receiving emails from it, send an email to ansible-proje...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/ansible-project/20200405095342.2da6eced%40rovaniemi.

Felix Fontein

unread,
Apr 8, 2020, 8:32:37 AM4/8/20
to ansible...@googlegroups.com
Hi,

> Thank you for the answer!
> Unfortunately I still can't make acme_certificate to create the
> certificate. I've changed the challenge to dns-01 and
> taken care of creating the _acme-challenge.domain.name vi REST api
> but when the last task is run I get:
> "msg": "Authorization for dns:*.domain.name returned invalid:
> CHALLENGE: dns-01 DETAILS: DNS problem: NXDOMAIN looking up TXT for _
> acme-challenge.domain.name - check that a DNS record exists for this
> domain;"
> or
> "msg": "Authorization for dns:domain.name returned invalid:
> CHALLENGE: dns-01 DETAILS: DNS problem: NXDOMAIN looking up TXT for _
> acme-challenge.domain.name - check that a DNS record exists for this
> domain;"
> alternately.
>
> The DNS record exists * 2 before last task is run and as of the
> ansible output (-vvv) it's the same token.

it looks like the DNS record wasn't there when Let's Encrypt tried to
validate it from (some of) the authoritative name servers for your
domain. You might need to add some waiting to make sure that the DNS
entries propagated among all authoritative name servers for your domain.

Depending on how you set the DNS records, you can ask the API to tell
you when the values propagated (route53 can do that for example), or
you can simply let your playbook/role sleep for some time (I personally
use 30 seconds for my DNS provider; probably less will work as well,
but I never had problems with 30 so I'm sticking to it).

Best,
Felix

Edoardo Tenani

unread,
Apr 8, 2020, 8:54:09 AM4/8/20
to ansible...@googlegroups.com
I created this snippet to wait for DNS resolution before proceeding with
the ACME verification.

    - name: '{{ certificate.common_name }} | "Wax on, wax off"'
      debug:
        msg: "{{ dns_txt_record }} <=> {{ item.1 | first }}"
      when: acme_challenge is changed
      loop: "{{ acme_challenge.challenge_data_dns | dictsort }}"
      until: dns_txt_record == item.1[0]
      # If the until parameter isn’t defined, the value for the retries
parameter is forced to 1.
      #
https://docs.ansible.com/ansible/latest/user_guide/playbooks_loops.html?highlight=delay#id9
      retries: 20
      delay: 60
      vars:
        dns_txt_record: "{{  lookup('dig', item.0, 'qtype=TXT')  }}"

This is tailored for AWS Route53 but be easy to adapt. It performs a DNS
TXT request once every minute checking for the ACME challenge text until
it's found or reaches 20 minutes.

20 minutes is a very long time but please note than this is run locally
(on operator machine or server) and as such I preferred to keep a longer
buffer (as DNS propagation may take time). As until is used it stops
waiting once the entry is found, the wait time depends on how fast the
DNS entry is found so there is no penalty if it's found sooner.

As the check is then performed from the ACME provider servers, this does
not ensure 100% that when the record is available to you is available
also to them, but in almost all cases this will be true as propagation
to their system will be faster or equal than propagation to your system.
This check has proven very effective for the last 2 years, but YMMV :)

You can obviously tweak retries and delay to suit your network conditions.

Hope it helps!

Best,
Edoardo


Felix Fontein

unread,
Apr 8, 2020, 9:00:16 AM4/8/20
to ansible...@googlegroups.com
Hi Edoardo,

for route53, you can simply set `wait: yes` (see
https://docs.ansible.com/ansible/latest/modules/route53_module.html#parameter-wait)
and the module will only return once all DNS servers have been updated.
Also takes some time (feels like forever ;) ), but a lot less than 20
minutes.

For general waits, there's also the pause module
(https://docs.ansible.com/ansible/latest/modules/pause_module.html).
For example:

- name: Wait for DNS entries to propagate
pause:
seconds: 10

For 20 minutes your solution is better though, since you can see that
something is still happening once per minute ;)

Cheers,
Felix

Patric Buskas

unread,
Apr 8, 2020, 11:30:13 AM4/8/20
to ansible...@googlegroups.com
Thank you all!

I found a typo in a variable. It's working as expected now.
:-)

--
You received this message because you are subscribed to the Google Groups "Ansible Project" group.
To unsubscribe from this group and stop receiving emails from it, send an email to ansible-proje...@googlegroups.com.
Reply all
Reply to author
Forward
0 new messages