dvtirol / serles-acme

Pluggable ACME: a tiny ACME-CA implementation to enhance existing CA infrastructure
https://serles-acme.readthedocs.io/
GNU General Public License v3.0
46 stars 10 forks source link

Implement DNS01 Challenge #8

Open Toxma opened 1 month ago

Toxma commented 1 month ago

Hi,

First, thank you for your amazing work, I am actually working with the serles-acme HTTP01 challenge, but for some technical reason I would like to swap for DNS01 challenge. Do you think it's possible to implement this ?

Have a good day !

uedvt359 commented 1 month ago

This should be fairly easy; I would like to do it, but can't get the resources for it right now.

firstly, implement dns_challenge() in serles/challenge.py, with the same signature as the other two supported challenges, and call it from verify_challenge(). then, in a seperate commit, add it to the list of supported challenges.

for designing and implementing the challenge function, the rfc lays out nicely what needs to be done (boils down to making a TXT lookup and comparing it to the challenge).

if you want to take a crack at it, i'm happy to mentor the PR.

uedvt359 commented 1 month ago

some more notes:

syberalexis commented 2 weeks ago

Hi, Thank's for this awesome project !

I make a PR for this issue. I follow the RFC for implementation, it manages classic identifier and wildcard.

But I think it's better to leave the system DNS configuration rather than having a configuration file for that ;)

uedvt359 commented 2 weeks ago

I'll keep this ticket open until the next release, which is probably going to be 1.2.0.

dmizin commented 2 weeks ago

@syberalexis Thank you fir your work on DNS-01. Timing for me was impeccable :) Started similar yesterday, and then checked the repo this morning to find you already done it. Matrix ;) And as to wildcard - just tested and with EJBCA and BIND it works perfectly. Thanks again both of you!

uedvt359 commented 2 weeks ago

And as to wildcard - just tested and with EJBCA and BIND it works perfectly.

erm, this is strange: we don't yet implement wildcard handling. can you please elaborate on what you have done and what certificate was issued in the end?

dmizin commented 2 weeks ago

you have done and what certificate was issued in the end?

Not sure where to start, but basically running your code inside a container per EJBCA integration instructions. Injected the latest merged code. Running certbot like this:

 certbot certonly \
  --manual \
  --preferred-challenges dns \
  --server https://XXXXX:8080/directory \
  --manual-auth-hook "./dns_nsupdate.sh deploy_challenge" \
  --manual-cleanup-hook "./dns_nsupdate.sh clean_challenge" \
  -d "toolbox.okd-stage.XXX.XXX" -d "*.toolbox.okd-stage.XXX.XXX" \
  --register-unsafely-without-email --agree-tos -v

Inside the hook against the BIND

  #!/bin/bash

DOMAIN="${CERTBOT_DOMAIN}"
CHALLENGE="${CERTBOT_VALIDATION}"
ZONE="okd-stage.XXX.XXX."  # (end with a dot)
TSIG_KEY_FILE="XXX.key"  # TSIG key path
DNS_SERVER="xxx.xxx.xxx.xxx"

# Function to add a DNS-01 challenge record
add_record() {
    echo "Adding DNS record for _acme-challenge.${DOMAIN}"
    nsupdate -k "${TSIG_KEY_FILE}" <<-EOF
    server ${DNS_SERVER}
    zone ${ZONE}
    update add _acme-challenge.${DOMAIN}. 300 IN TXT "${CHALLENGE}"
    send
EOF
}

# Function to remove a DNS-01 challenge record
remove_record() {
    echo "Removing DNS record for _acme-challenge.${DOMAIN}"
    nsupdate -k "${TSIG_KEY_FILE}" <<-EOF
    server ${DNS_SERVER}
    zone ${ZONE}
    update delete _acme-challenge.${DOMAIN}. TXT
    send
EOF
}

# Certbot hook cases
case $1 in
    "deploy_challenge")
        add_record
        # Wait for DNS propagation (adjust time as needed)
        sleep 5
        ;;
    "clean_challenge")
        remove_record
        ;;
    "deploy_cert")
        echo "Certificate for ${DOMAIN} has been deployed."
        ;;
    "unchanged_cert")
        echo "Certificate for ${DOMAIN} is unchanged."
        ;;
    "exit_hook")
        echo "Exit hook for ${DOMAIN}"
        ;;
    *)
        echo "Unknown hook: $1"
        ;;
esac

And comes out to this:

Certificate:
    Data:
        Version: 3 (0x2)
        Serial Number:
            <redacted>
        Signature Algorithm: ecdsa-with-SHA256
        Issuer: C = US, O = <redacted>, CN = <redacted>
        Validity
            Not Before: Nov  7 20:28:53 2024 GMT
            Not After : Feb  5 20:28:52 2025 GMT
        Subject: CN = toolbox.okd-stage.<redacted>
        Subject Public Key Info:
            Public Key Algorithm: rsaEncryption
                Public-Key: (2048 bit)
                Modulus:
                    <redacted>
                 Exponent: 65537 (0x10001)
        X509v3 extensions:
            X509v3 Basic Constraints: critical
                CA:FALSE
            X509v3 Authority Key Identifier:
                <redacted>
            X509v3 Subject Alternative Name:
                DNS:toolbox.okd-stage.<redacted>, DNS:*.toolbox.okd-stage.<redacted>
            X509v3 Extended Key Usage:
                TLS Web Server Authentication
            X509v3 Subject Key Identifier:
                <redacted>
            X509v3 Key Usage: critical
                Digital Signature, Key Encipherment
    Signature Algorithm: ecdsa-with-SHA256
    Signature Value:
        <redacted>

Tried with only the wildmark and same result. End result:

image

Now, if anyone could tell me why this works with certbot and same is rejected with cert-manager on EJBCA side with "userdata did not fulfill end entity profile" - would really appreciate it :) Figured this part after long search - maybe a note in documents if someone wants to implement this with cert-manager. In End Entity Profile used for ACME - ensure you have enabled "Send Notification" in "Other Data" section. It only took me a one night and one day to figure this :)) Without this cert request failed with order is in "errored" state: Failed to finalize Order: 400 urn:ietf:params:acme:error:badCSR: Email notification cannot be used.