RFC 8738 support? #510

Open
opened 2025-12-29 01:26:28 +01:00 by adam · 18 comments
Owner

Originally created by @strophy on GitHub (Nov 30, 2020).

Does/will dehydrated support RFC 8738 to issue certificates to IP addresses over ACME? ZeroSSL currently supports this over their REST API, but I am unable to test it with ACME because I am unable to find a client that supports both RFC 8738 and the EAB now requried by ZeroSSL. Looks dehydrated might be a possible choice, is this feature planned? Thanks!

Originally created by @strophy on GitHub (Nov 30, 2020). Does/will dehydrated support [RFC 8738](https://tools.ietf.org/html/rfc8738) to issue certificates to IP addresses over ACME? ZeroSSL currently supports this over their REST API, but I am unable to test it with ACME because I am unable to find a client that supports both RFC 8738 and the EAB now requried by ZeroSSL. Looks dehydrated might be a possible choice, is this feature planned? Thanks!
adam added the testing label 2025-12-29 01:26:28 +01:00
Author
Owner

@lukas2511 commented on GitHub (Dec 1, 2020):

This will be integrated for the upcoming release 👍
But I'm not sure if ZeroSSL supports it on their ACME API.

@lukas2511 commented on GitHub (Dec 1, 2020): This will be integrated for the upcoming release :+1: But I'm not sure if ZeroSSL supports it on their ACME API.
Author
Owner

@strophy commented on GitHub (Dec 2, 2020):

Thanks! I have confirmed ZeroSSL only supports it over their REST API but not ACME yet. Either way, it's great to see more client support for this feature 👍

@strophy commented on GitHub (Dec 2, 2020): Thanks! I have confirmed ZeroSSL only supports it over their REST API but not ACME yet. Either way, it's great to see more client support for this feature :+1:
Author
Owner

@lukas2511 commented on GitHub (Dec 10, 2020):

I have an experimental implementation locally but since I want to release the new version today and didn't yet have an opportunity to test the code I'll postpone it for the next release (which will follow much quicker than the current one).

@lukas2511 commented on GitHub (Dec 10, 2020): I have an experimental implementation locally but since I want to release the new version today and didn't yet have an opportunity to test the code I'll postpone it for the next release (which will follow much quicker than the current one).
Author
Owner

@kousu commented on GitHub (Apr 20, 2021):

So that I understand, that would mean this would work?

# echo 'CA="https://acme.zerossl.com/v2/DV90"' >> /etc/dehydrated/config
# dehydrated --register --accept-terms
# INFO: Using main config file /etc/dehydrated/config
# INFO: Using additional config file /etc/dehydrated/conf.d/hooks.sh
# INFO: Using additional config file /etc/dehydrated/conf.d/neuropoly.sh
+ Generating account key...
+ Registering account key with ACME server...
  + ERROR: An error occurred while sending post-request to https://acme.zerossl.com/v2/DV90/newAccount (Status 400)

Details:
HTTP/2 400 
server: nginx
date: Tue, 20 Apr 2021 07:53:13 GMT
content-type: application/problem+json
content-length: 159
replay-nonce: zbqMLe3MhuyqQXP2ZUZ2MEZ5XZJAVw_EZOJxUr6cO_g
cache-control: max-age=0, no-cache, no-store
access-control-allow-origin: *
link: <https://acme.zerossl.com/v2/DV90>;rel="index"
cache-control: max-age=-1

{"type":"urn:ietf:params:acme:error:externalAccountRequired","status":400,"detail":"The request must include a value for the \"externalAccountBinding\" field"}



@kousu commented on GitHub (Apr 20, 2021): So that I understand, that would mean this would work? ``` # echo 'CA="https://acme.zerossl.com/v2/DV90"' >> /etc/dehydrated/config # dehydrated --register --accept-terms # INFO: Using main config file /etc/dehydrated/config # INFO: Using additional config file /etc/dehydrated/conf.d/hooks.sh # INFO: Using additional config file /etc/dehydrated/conf.d/neuropoly.sh + Generating account key... + Registering account key with ACME server... + ERROR: An error occurred while sending post-request to https://acme.zerossl.com/v2/DV90/newAccount (Status 400) Details: HTTP/2 400 server: nginx date: Tue, 20 Apr 2021 07:53:13 GMT content-type: application/problem+json content-length: 159 replay-nonce: zbqMLe3MhuyqQXP2ZUZ2MEZ5XZJAVw_EZOJxUr6cO_g cache-control: max-age=0, no-cache, no-store access-control-allow-origin: * link: <https://acme.zerossl.com/v2/DV90>;rel="index" cache-control: max-age=-1 {"type":"urn:ietf:params:acme:error:externalAccountRequired","status":400,"detail":"The request must include a value for the \"externalAccountBinding\" field"} ```
Author
Owner

@lukas2511 commented on GitHub (Apr 21, 2021):

So that I understand, that would mean this would work?

# echo 'CA="https://acme.zerossl.com/v2/DV90"' >> /etc/dehydrated/config
# dehydrated --register --accept-terms
# INFO: Using main config file /etc/dehydrated/config
# INFO: Using additional config file /etc/dehydrated/conf.d/hooks.sh
# INFO: Using additional config file /etc/dehydrated/conf.d/neuropoly.sh
+ Generating account key...
+ Registering account key with ACME server...
  + ERROR: An error occurred while sending post-request to https://acme.zerossl.com/v2/DV90/newAccount (Status 400)

Details:
HTTP/2 400 
server: nginx
date: Tue, 20 Apr 2021 07:53:13 GMT
content-type: application/problem+json
content-length: 159
replay-nonce: zbqMLe3MhuyqQXP2ZUZ2MEZ5XZJAVw_EZOJxUr6cO_g
cache-control: max-age=0, no-cache, no-store
access-control-allow-origin: *
link: <https://acme.zerossl.com/v2/DV90>;rel="index"
cache-control: max-age=-1

{"type":"urn:ietf:params:acme:error:externalAccountRequired","status":400,"detail":"The request must include a value for the \"externalAccountBinding\" field"}

Wrong thread. RFC 8738 is about getting certificates signed directly for IP addresses instead of domains. If you're having issues with zerossl please make sure to have the newest dehydrated version and open a new issue if the problem persists.

@lukas2511 commented on GitHub (Apr 21, 2021): > So that I understand, that would mean this would work? > > ``` > # echo 'CA="https://acme.zerossl.com/v2/DV90"' >> /etc/dehydrated/config > # dehydrated --register --accept-terms > # INFO: Using main config file /etc/dehydrated/config > # INFO: Using additional config file /etc/dehydrated/conf.d/hooks.sh > # INFO: Using additional config file /etc/dehydrated/conf.d/neuropoly.sh > + Generating account key... > + Registering account key with ACME server... > + ERROR: An error occurred while sending post-request to https://acme.zerossl.com/v2/DV90/newAccount (Status 400) > > Details: > HTTP/2 400 > server: nginx > date: Tue, 20 Apr 2021 07:53:13 GMT > content-type: application/problem+json > content-length: 159 > replay-nonce: zbqMLe3MhuyqQXP2ZUZ2MEZ5XZJAVw_EZOJxUr6cO_g > cache-control: max-age=0, no-cache, no-store > access-control-allow-origin: * > link: <https://acme.zerossl.com/v2/DV90>;rel="index" > cache-control: max-age=-1 > > {"type":"urn:ietf:params:acme:error:externalAccountRequired","status":400,"detail":"The request must include a value for the \"externalAccountBinding\" field"} > ``` Wrong thread. RFC 8738 is about getting certificates signed directly for IP addresses instead of domains. If you're having issues with zerossl please make sure to have the newest dehydrated version and open a new issue if the problem persists.
Author
Owner

@kousu commented on GitHub (Apr 23, 2021):

Thank you @lukas2511, installing 0.7.0 made ZeroSSL work. Debian stable has 0.6.5 packaged; testing has 0.7.0 so in like a year when the next Debian stable comes around this will work more easily.

For reference for anyone who stumbles across this thread, my install script is now:

ssh root@server apt-get install dehydrated
scp ./usr/bin/dehydrated root@server:/usr/bin/ # where ./usr/bin/dehydrated is from https://github.com/dehydrated-io/dehydrated/releases/tag/v0.7.0
ssh root@server "echo 'CA=\"https://acme.zerossl.com/v2/DV90\"' >> /etc/dehydrated/conf.d/site.sh"

And this seems to work fine for getting ZeroSSL working on Debian stable.

@kousu commented on GitHub (Apr 23, 2021): Thank you @lukas2511, installing 0.7.0 made ZeroSSL work. Debian stable has 0.6.5 packaged; [testing has 0.7.0](https://packages.debian.org/sid/dehydrated) so in like a year when the next Debian stable comes around this will work more easily. For reference for anyone who stumbles across this thread, my install script is now: ``` ssh root@server apt-get install dehydrated scp ./usr/bin/dehydrated root@server:/usr/bin/ # where ./usr/bin/dehydrated is from https://github.com/dehydrated-io/dehydrated/releases/tag/v0.7.0 ssh root@server "echo 'CA=\"https://acme.zerossl.com/v2/DV90\"' >> /etc/dehydrated/conf.d/site.sh" ``` And this seems to work fine for getting ZeroSSL working on Debian stable.
Author
Owner

@lukas2511 commented on GitHub (Apr 7, 2022):

RFC 8738 support is now implemented on the rfc8738 branch. There are still a few issues, e.g. comparison on already signed certificates is missing normalization for now so certificates especially for IPv6 addresses might be refreshed on every run. Also signing of external CSRs hasn't been tested yet.

I don't think any public CA supports this feature yet, but e.g. step-ca can be used for testing or whatever internal use-cases one might have (e.g. etcd or ssh).

@lukas2511 commented on GitHub (Apr 7, 2022): RFC 8738 support is now implemented on the [rfc8738 branch](https://github.com/dehydrated-io/dehydrated/tree/rfc8738). There are still a few issues, e.g. comparison on already signed certificates is missing normalization for now so certificates especially for IPv6 addresses might be refreshed on every run. Also signing of external CSRs hasn't been tested yet. I don't think any public CA supports this feature yet, but e.g. step-ca can be used for testing or whatever internal use-cases one might have (e.g. etcd or ssh).
Author
Owner

@fragolinux commented on GitHub (Apr 14, 2022):

RFC 8738 support is now implemented on the rfc8738 branch. There are still a few issues, e.g. comparison on already signed certificates is missing normalization for now so certificates especially for IPv6 addresses might be refreshed on every run. Also signing of external CSRs hasn't been tested yet.

I don't think any public CA supports this feature yet, but e.g. step-ca can be used for testing or whatever internal use-cases one might have (e.g. etcd or ssh).

zerossl allows to have ip based certificates, but probably only in rest api...
anyway, can you please share a command line example? Thanks in advance

@fragolinux commented on GitHub (Apr 14, 2022): > RFC 8738 support is now implemented on the [rfc8738 branch](https://github.com/dehydrated-io/dehydrated/tree/rfc8738). There are still a few issues, e.g. comparison on already signed certificates is missing normalization for now so certificates especially for IPv6 addresses might be refreshed on every run. Also signing of external CSRs hasn't been tested yet. > > I don't think any public CA supports this feature yet, but e.g. step-ca can be used for testing or whatever internal use-cases one might have (e.g. etcd or ssh). zerossl allows to have ip based certificates, but probably only in rest api... anyway, can you please share a command line example? Thanks in advance
Author
Owner

@lukas2511 commented on GitHub (Apr 15, 2022):

RFC 8738 support is now implemented on the rfc8738 branch. There are still a few issues, e.g. comparison on already signed certificates is missing normalization for now so certificates especially for IPv6 addresses might be refreshed on every run. Also signing of external CSRs hasn't been tested yet.
I don't think any public CA supports this feature yet, but e.g. step-ca can be used for testing or whatever internal use-cases one might have (e.g. etcd or ssh).

zerossl allows to have ip based certificates, but probably only in rest api... anyway, can you please share a command line example? Thanks in advance

Unfortunately they are not supporting ip certs via acme.

Example usage (for now) would be dehydrated -c -d IP:192.0.2.42 --ca zerossl (but it will fail). The IP: prefix may be dropped, not entirely sure about that yet.

@lukas2511 commented on GitHub (Apr 15, 2022): > > RFC 8738 support is now implemented on the [rfc8738 branch](https://github.com/dehydrated-io/dehydrated/tree/rfc8738). There are still a few issues, e.g. comparison on already signed certificates is missing normalization for now so certificates especially for IPv6 addresses might be refreshed on every run. Also signing of external CSRs hasn't been tested yet. > > I don't think any public CA supports this feature yet, but e.g. step-ca can be used for testing or whatever internal use-cases one might have (e.g. etcd or ssh). > > zerossl allows to have ip based certificates, but probably only in rest api... anyway, can you please share a command line example? Thanks in advance Unfortunately they are not supporting ip certs via acme. Example usage (for now) would be `dehydrated -c -d IP:192.0.2.42 --ca zerossl` (but it will fail). The `IP:` prefix may be dropped, not entirely sure about that yet.
Author
Owner

@suhastm19 commented on GitHub (Feb 22, 2023):

@lukas2511 Is this done or its still in implementation stage? I have a similar requirement where both ip and fqdn are being added as SANS in a certificate.

@suhastm19 commented on GitHub (Feb 22, 2023): @lukas2511 Is this done or its still in implementation stage? I have a similar requirement where both ip and fqdn are being added as SANS in a certificate.
Author
Owner

@lukas2511 commented on GitHub (Feb 22, 2023):

@lukas2511 Is this done or its still in implementation stage? I have a similar requirement where both ip and fqdn are being added as SANS in a certificate.

It's implemented, but basically untested as afaik there are no public CAs supporting this (yet). It should probably work fine with private CAs like smallstep. Also the normalization issue with IPv6 addresses still exists but that can be worked around.

@lukas2511 commented on GitHub (Feb 22, 2023): > @lukas2511 Is this done or its still in implementation stage? I have a similar requirement where both ip and fqdn are being added as SANS in a certificate. It's implemented, but basically untested as afaik there are no public CAs supporting this (yet). It should probably work fine with private CAs like smallstep. Also the normalization issue with IPv6 addresses still exists but that can be worked around.
Author
Owner

@suhastm19 commented on GitHub (Mar 2, 2023):

Is this code available in v0.7.1 release?

@suhastm19 commented on GitHub (Mar 2, 2023): Is this code available in v0.7.1 release?
Author
Owner

@lukas2511 commented on GitHub (Mar 2, 2023):

Is this code available in v0.7.1 release?

Yes.

@lukas2511 commented on GitHub (Mar 2, 2023): > Is this code available in v0.7.1 release? Yes.
Author
Owner

@quite commented on GitHub (Jun 15, 2023):

@lukas2511 I think there is a typo here in the code:

https://github.com/dehydrated-io/dehydrated/blob/master/dehydrated#L1812

Should probably be ( Address)*. This showed up in shellcheck, which you really should be running.

@quite commented on GitHub (Jun 15, 2023): @lukas2511 I think there is a typo here in the code: https://github.com/dehydrated-io/dehydrated/blob/master/dehydrated#L1812 Should probably be `( Address)*`. This showed up in `shellcheck`, which you really should be running.
Author
Owner

@candlerb commented on GitHub (Jul 3, 2025):

This is almost working out-of-the-box with Letsencrypt Staging, which now offers IP SANs.

The challenge validation is successful, but the final stage fails:

HTTP/2 400
server: nginx
date: Thu, 03 Jul 2025 10:37:47 GMT
content-type: application/problem+json
content-length: 146
boulder-requester: 210222443
cache-control: public, max-age=0, no-cache
link: <https://acme-staging-v02.api.letsencrypt.org/directory>;rel="index"
replay-nonce: 8GeDqaBia2BthCHmE7GQHe5T-z1uLiHZ5QS5T4hwOxQXKoIukMo

{
  "type": "urn:ietf:params:acme:error:badCSR",
  "detail": "Error finalizing order :: CSR contains IP address in Common Name",
  "status": 400
}

This is with dehydrated 0.7.2. Relevant config:

CA="letsencrypt-test"
ACME_PROFILE="shortlived"
WELLKNOWN=/var/www/dehydrated/.well-known/acme-challenge

domains.txt (with addresses masked):

ip:xxx.xxx.xxx.193 ip:xxx:xxx:xxx:xxx::62 >ipsan

The solution is to request an empty subject (note that empty string or // are rejected by openssl req, but / is OK)

--- dehydrated.orig	2025-07-03 10:04:07.747841033 +0000
+++ dehydrated	2025-07-03 10:56:08.896896518 +0000
@@ -1554,7 +1554,7 @@
       fi
     done
     if [[ "${domain}" =~ ^ip: ]]; then
-      SUBJ="/CN=${domain:3}/"
+      SUBJ="/"
     else
       SUBJ="/CN=${domain}/"
     fi

Any garbage subject like /DC=example/ also works; in either case the issued certificate has an empty subject.

The fact that LetsEncrypt accepts and ignores almost any subject except one which contains a commonName IP address seems weird. I have raised it with LetsEncrypt here.

@candlerb commented on GitHub (Jul 3, 2025): This is *almost* working out-of-the-box with Letsencrypt Staging, which now offers IP SANs. The challenge validation is successful, but the final stage fails: ``` HTTP/2 400 server: nginx date: Thu, 03 Jul 2025 10:37:47 GMT content-type: application/problem+json content-length: 146 boulder-requester: 210222443 cache-control: public, max-age=0, no-cache link: <https://acme-staging-v02.api.letsencrypt.org/directory>;rel="index" replay-nonce: 8GeDqaBia2BthCHmE7GQHe5T-z1uLiHZ5QS5T4hwOxQXKoIukMo { "type": "urn:ietf:params:acme:error:badCSR", "detail": "Error finalizing order :: CSR contains IP address in Common Name", "status": 400 } ``` This is with dehydrated 0.7.2. Relevant config: ``` CA="letsencrypt-test" ACME_PROFILE="shortlived" WELLKNOWN=/var/www/dehydrated/.well-known/acme-challenge ``` domains.txt (with addresses masked): ``` ip:xxx.xxx.xxx.193 ip:xxx:xxx:xxx:xxx::62 >ipsan ``` The solution is to request an [empty subject](https://superuser.com/questions/512673/openssl-how-to-create-a-certificate-with-an-empty-subject-dn) (note that empty string or `//` are rejected by `openssl req`, but `/` is OK) ``` --- dehydrated.orig 2025-07-03 10:04:07.747841033 +0000 +++ dehydrated 2025-07-03 10:56:08.896896518 +0000 @@ -1554,7 +1554,7 @@ fi done if [[ "${domain}" =~ ^ip: ]]; then - SUBJ="/CN=${domain:3}/" + SUBJ="/" else SUBJ="/CN=${domain}/" fi ``` Any garbage subject like `/DC=example/` also works; in either case the issued certificate has an empty subject. The fact that LetsEncrypt accepts and ignores almost any subject *except* one which contains a commonName IP address seems weird. I have raised it with LetsEncrypt [here](https://community.letsencrypt.org/t/ip-san-error-csr-contains-ip-address-in-common-name/239012).
Author
Owner

@candlerb commented on GitHub (Jul 3, 2025):

I think that one-line fix I gave is correct: for LetsEncrypt, if you provide a subject commonName, it is only allowed to match a DNS SAN in the same CSR.

(Although Subject is obsolete anyway, and it would probably be fine to set an empty subject for all certificates)

@candlerb commented on GitHub (Jul 3, 2025): I think that one-line fix I gave is correct: for LetsEncrypt, if you provide a subject commonName, it is only allowed to match a *DNS* SAN in the same CSR. (Although Subject is obsolete anyway, and it would probably be fine to set an empty subject for *all* certificates)
Author
Owner

@jobe1986 commented on GitHub (Jul 3, 2025):

Of additional note, the initial IP cert Let'sEncrypt issued uses an empty Subject too as can be seen at https://crt.sh/?id=19376952215

@jobe1986 commented on GitHub (Jul 3, 2025): Of additional note, the initial IP cert Let'sEncrypt issued uses an empty Subject too as can be seen at https://crt.sh/?id=19376952215
Author
Owner

@lukas2511 commented on GitHub (Jul 5, 2025):

I think that one-line fix I gave is correct: for LetsEncrypt, if you provide a subject commonName, it is only allowed to match a DNS SAN in the same CSR.

(Although Subject is obsolete anyway, and it would probably be fine to set an empty subject for all certificates)

Thanks for the suggestion, I've added the change to the script. And yes, normally subjects are obsolete and an empty subject should be fine, but since some CAs are really weird I think it's better for now to just keep the old behaviour for non-ip names.

@lukas2511 commented on GitHub (Jul 5, 2025): > I think that one-line fix I gave is correct: for LetsEncrypt, if you provide a subject commonName, it is only allowed to match a _DNS_ SAN in the same CSR. > > (Although Subject is obsolete anyway, and it would probably be fine to set an empty subject for _all_ certificates) Thanks for the suggestion, I've added the change to the script. And yes, normally subjects are obsolete and an empty subject should be fine, but since some CAs are really weird I think it's better for now to just keep the old behaviour for non-ip names.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: starred/dehydrated#510