How do I get a self-signed cert for a public domain name?

1. Output of caddy version:

v2.6.2 h1:wKoFIxpmOJLGl3QXoo6PNbYvGW4xLEgo32GPBEjWL8o=

2. How I run Caddy:

With caddy start

a. System environment:

macOS via homebrew

b. Command:

caddy start

d. My complete Caddy config:

foobar.dev {
	reverse_proxy localhost:1337
}

*.foobar.dev {
	reverse_proxy localhost:1337
}

3. The problem I’m having:

I need to do local development testing Google OAuth. This requires that:

  1. The local server is accessible via a a hostname that “must end with a public top-level domain (such as .com or .org)”. These restrictions imposed by Google OAuth “Authorized JavaScript origins”.
  2. The browser (Chrome) accepts a certificate for HTTPS.

I am using foobar.dev as that public hostname. I am using dnsmasq to point this at 127.0.0.1.

I now need a cert for foobar.dev, just like the self-signed certs that Caddy generates for foobar.localhost. However, when I run Caddy with this config, I get the error below.

Caddy by default seems to have the policy: “if a public domain name, request from LetsEncrypt; otherwise self-sign”. This policy doesn’t work for me - I need a self-signed cert for foobar.dev, since I don’t own it. So - what’s the magic config to tell Caddy to just self-sign instead of requesting from LE?

4. Error messages and/or full log output:

2022/11/20 01:08:18.810	INFO	tls.obtain	lock acquired	{"identifier": "foobar.dev"}
2022/11/20 01:08:18.810	INFO	tls.obtain	obtaining certificate	{"identifier": "foobar.dev"}
2022/11/20 01:08:18.812	INFO	http	waiting on internal rate limiter	{"identifiers": ["foobar.dev"], "ca": "https://acme-v02.api.letsencrypt.org/directory", "account": ""}
2022/11/20 01:08:18.812	INFO	http	done waiting on internal rate limiter	{"identifiers": ["foobar.dev"], "ca": "https://acme-v02.api.letsencrypt.org/directory", "account": ""}
2022/11/20 01:08:18.829	INFO	tls.obtain	lock acquired	{"identifier": "*.foobar.dev"}
2022/11/20 01:08:18.829	INFO	tls.obtain	obtaining certificate	{"identifier": "*.foobar.dev"}
2022/11/20 01:08:18.830	INFO	http	waiting on internal rate limiter	{"identifiers": ["*.foobar.dev"], "ca": "https://acme-v02.api.letsencrypt.org/directory", "account": ""}
2022/11/20 01:08:18.830	INFO	http	done waiting on internal rate limiter	{"identifiers": ["*.foobar.dev"], "ca": "https://acme-v02.api.letsencrypt.org/directory", "account": ""}
2022/11/20 01:08:19.993	INFO	http.acme_client	trying to solve challenge	{"identifier": "foobar.dev", "challenge_type": "http-01", "ca": "https://acme-v02.api.letsencrypt.org/directory"}
2022/11/20 01:08:20.087	ERROR	tls.obtain	could not get certificate from issuer	{"identifier": "*.foobar.dev", "issuer": "acme-v02.api.letsencrypt.org-directory", "error": "[*.foobar.dev] solving challenges: *.foobar.dev: no solvers available for remaining challenges (configured=[http-01 tls-alpn-01] offered=[dns-01] remaining=[dns-01]) (order=https://acme-v02.api.letsencrypt.org/acme/order/832393997/145685255337) (ca=https://acme-v02.api.letsencrypt.org/directory)"}
2022/11/20 01:08:20.088	INFO	http	waiting on internal rate limiter	{"identifiers": ["*.foobar.dev"], "ca": "https://acme.zerossl.com/v2/DV90", "account": "caddy@zerossl.com"}
2022/11/20 01:08:20.088	INFO	http	done waiting on internal rate limiter	{"identifiers": ["*.foobar.dev"], "ca": "https://acme.zerossl.com/v2/DV90", "account": "caddy@zerossl.com"}
2022/11/20 01:08:21.347	ERROR	http.acme_client	challenge failed	{"identifier": "foobar.dev", "challenge_type": "http-01", "problem": {"type": "urn:ietf:params:acme:error:unauthorized", "title": "", "detail": "2001:4860:4802:36::15: Invalid response from https://www.foobar.dev/.well-known/acme-challenge/N_yZQq65WOTYQ6C1_bIdmkibi2hc9OO-3AJPQok8PA0: 403", "instance": "", "subproblems": []}}
2022/11/20 01:08:21.347	ERROR	http.acme_client	validating authorization	{"identifier": "foobar.dev", "problem": {"type": "urn:ietf:params:acme:error:unauthorized", "title": "", "detail": "2001:4860:4802:36::15: Invalid response from https://www.foobar.dev/.well-known/acme-challenge/N_yZQq65WOTYQ6C1_bIdmkibi2hc9OO-3AJPQok8PA0: 403", "instance": "", "subproblems": []}, "order": "https://acme-v02.api.letsencrypt.org/acme/order/832393997/145685255617", "attempt": 1, "max_attempts": 3}
2022/11/20 01:08:22.712	INFO	http.acme_client	trying to solve challenge	{"identifier": "foobar.dev", "challenge_type": "tls-alpn-01", "ca": "https://acme-v02.api.letsencrypt.org/directory"}
2022/11/20 01:08:23.265	ERROR	tls.obtain	could not get certificate from issuer	{"identifier": "*.foobar.dev", "issuer": "acme.zerossl.com-v2-DV90", "error": "[*.foobar.dev] solving challenges: *.foobar.dev: no solvers available for remaining challenges (configured=[http-01 tls-alpn-01] offered=[dns-01] remaining=[dns-01]) (order=https://acme.zerossl.com/v2/DV90/order/xnoGq4DtNzKSvX_zZu15SA) (ca=https://acme.zerossl.com/v2/DV90)"}
2022/11/20 01:08:23.265	ERROR	tls.obtain	will retry	{"error": "[*.foobar.dev] Obtain: [*.foobar.dev] solving challenges: *.foobar.dev: no solvers available for remaining challenges (configured=[http-01 tls-alpn-01] offered=[dns-01] remaining=[dns-01]) (order=https://acme.zerossl.com/v2/DV90/order/xnoGq4DtNzKSvX_zZu15SA) (ca=https://acme.zerossl.com/v2/DV90)", "attempt": 1, "retrying_in": 60, "elapsed": 4.436450984, "max_duration": 2592000}
2022/11/20 01:08:23.266	ERROR	http.acme_client	challenge failed	{"identifier": "foobar.dev", "challenge_type": "tls-alpn-01", "problem": {"type": "urn:ietf:params:acme:error:unauthorized", "title": "", "detail": "Incorrect validation certificate for tls-alpn-01 challenge. Requested foobar.dev from [2001:4860:4802:38::15]:443. Received certificate which is not self-signed.", "instance": "", "subproblems": []}}
2022/11/20 01:08:23.266	ERROR	http.acme_client	validating authorization	{"identifier": "foobar.dev", "problem": {"type": "urn:ietf:params:acme:error:unauthorized", "title": "", "detail": "Incorrect validation certificate for tls-alpn-01 challenge. Requested foobar.dev from [2001:4860:4802:38::15]:443. Received certificate which is not self-signed.", "instance": "", "subproblems": []}, "order": "https://acme-v02.api.letsencrypt.org/acme/order/832393997/145685265237", "attempt": 2, "max_attempts": 3}
2022/11/20 01:08:23.266	ERROR	tls.obtain	could not get certificate from issuer	{"identifier": "foobar.dev", "issuer": "acme-v02.api.letsencrypt.org-directory", "error": "HTTP 403 urn:ietf:params:acme:error:unauthorized - Incorrect validation certificate for tls-alpn-01 challenge. Requested foobar.dev from [2001:4860:4802:38::15]:443. Received certificate which is not self-signed."}
2022/11/20 01:08:23.266	INFO	http	waiting on internal rate limiter	{"identifiers": ["foobar.dev"], "ca": "https://acme.zerossl.com/v2/DV90", "account": "caddy@zerossl.com"}
2022/11/20 01:08:23.266	INFO	http	done waiting on internal rate limiter	{"identifiers": ["foobar.dev"], "ca": "https://acme.zerossl.com/v2/DV90", "account": "caddy@zerossl.com"}
2022/11/20 01:08:26.144	INFO	http.acme_client	trying to solve challenge	{"identifier": "foobar.dev", "challenge_type": "http-01", "ca": "https://acme.zerossl.com/v2/DV90"}

5. What I already tried:

Google; trawling documentation

tls internal is what you want :slight_smile: Put that in your site block for foobar.dev.

Or you can do it globally for all sites by using internal_certs in the global options block.

@matt perfect, thank you so much! :slight_smile:

Btw I’m surprised/embarrassed I didn’t find this myself! I googled “caddy self signed certificate” which leads to Automatic HTTPS — Caddy Documentation but this page doesn’t mention it. I also found tls (Caddyfile directive) — Caddy Documentation, but I was looking for the words “self-signed”, which don’t appear here.

It seems like Caddy uses the word “internal” for two distinct things: publicly inaccessible domains and self-signed certificates. I think it would be less confusing if it separated these, e.g. tls self_signed.

1 Like

The reason we didn’t do that is that technically it’s not a self-signed cert, because it is signed by a CA. Yes, the CA is managed by “self”, but a self-signed cert is specifically a cert that is signed by the same private key as the one being used to encrypt connections.

TIL! I always assumed “self-signed cert” meant “signed by my own made-up CA”. Interesting!

(That said, perhaps Caddy docs could include “self-signed certificate” (in quotation marks) to point misguided users like me in the right direction)

Yeah that’s fair, we should probably explain the difference since it’s not a well-understood distinction colloquially.

It could probably be either… I don’t think there’s a dictionary definition of “self-signed” that’s really authoritative.

It is true that colloquially, “self-signed” traditionally refers to a root cert that is also the leaf cert. However it could also mean “a cert signed by one’s own authority” regardless of cert/key hierarchy.

I figured the term “internal” might be clearer in that regard: certificates for internal use.