HTTP 400 urn:ietf:params:acme:error:malformed - JWS verification error

1. The problem I’m having:

I am trying to use caddy as a reverse proxy as I have done many times, but when visiting the URL I get ERR_SSL_PROTOCOL_ERROR. When looking into the logs it looks like caddy is failing to get the certificate form Let’s Encrypt.

I thing the relevant error is HTTP 400 urn:ietf:params:acme:error:malformed - JWS verification error

I also tried it with the stating server and got an unsafe but working certificate.

2. Error messages and/or full log output:

{"level":"info","ts":1729978385.566533,"logger":"admin","msg":"admin endpoint started","address":"localhost:2019","enforce_origin":false,"origins":["//localhost:2019","//[::1]:2019","//127.0.0.1:2019"]}
{"level":"info","ts":1729978385.5668411,"logger":"http.auto_https","msg":"server is listening only on the HTTPS port but has no TLS connection policies; adding one to enable TLS","server_name":"srv0","https_port":443}
{"level":"info","ts":1729978385.5668623,"logger":"http.auto_https","msg":"enabling automatic HTTP->HTTPS redirects","server_name":"srv0"}
{"level":"info","ts":1729978385.5678353,"logger":"http","msg":"enabling HTTP/3 listener","addr":":443"}
{"level":"info","ts":1729978385.5680788,"logger":"http.log","msg":"server running","name":"srv0","protocols":["h1","h2","h3"]}
{"level":"info","ts":1729978385.568627,"logger":"http.log","msg":"server running","name":"remaining_auto_https_redirects","protocols":["h1","h2","h3"]}
{"level":"info","ts":1729978385.568643,"logger":"http","msg":"enabling automatic TLS certificate management","domains":["test.paulmaier.online","tars.paulmaier.online","sync.paulmaier.online","docker.tars.paulmaier.online","photos.paulmaier.online"]}
{"level":"info","ts":1729978385.5689948,"logger":"tls.cache.maintenance","msg":"started background certificate maintenance","cache":"0xc0002e5b00"}
{"level":"info","ts":1729978385.569079,"msg":"autosaved config (load with --resume flag)","file":"/root/.config/caddy/autosave.json"}
{"level":"info","ts":1729978385.5690916,"msg":"serving initial configuration"}
{"level":"info","ts":1729978385.570076,"logger":"tls.obtain","msg":"acquiring lock","identifier":"test.paulmaier.online"}
{"level":"info","ts":1729978385.5761354,"logger":"tls.obtain","msg":"lock acquired","identifier":"test.paulmaier.online"}
{"level":"info","ts":1729978385.5762367,"logger":"tls.obtain","msg":"obtaining certificate","identifier":"test.paulmaier.online"}
{"level":"info","ts":1729978385.578072,"logger":"http","msg":"waiting on internal rate limiter","identifiers":["test.paulmaier.online"],"ca":"https://acme-v02.api.letsencrypt.org/directory","account":""}
{"level":"info","ts":1729978385.5781412,"logger":"http","msg":"done waiting on internal rate limiter","identifiers":["test.paulmaier.online"],"ca":"https://acme-v02.api.letsencrypt.org/directory","account":""}
{"level":"info","ts":1729978385.5785215,"logger":"http","msg":"using ACME account","account_id":"https://acme-v02.api.letsencrypt.org/acme/acct/2020432947","account_contact":[]}
{"level":"info","ts":1729978385.5797386,"logger":"tls.obtain","msg":"acquiring lock","identifier":"docker.tars.paulmaier.online"}
{"level":"info","ts":1729978385.5799298,"logger":"tls.obtain","msg":"acquiring lock","identifier":"sync.paulmaier.online"}
{"level":"info","ts":1729978385.5812254,"logger":"tls","msg":"storage cleaning happened too recently; skipping for now","storage":"FileStorage:/root/.local/share/caddy","instance":"3aa521ea-37f5-486a-97f0-481c43dbef5a","try_again":1730064785.5812204,"try_again_in":86399.999999248}
{"level":"info","ts":1729978385.581342,"logger":"tls","msg":"finished cleaning storage units"}
{"level":"info","ts":1729978385.581961,"logger":"tls.obtain","msg":"acquiring lock","identifier":"photos.paulmaier.online"}
{"level":"info","ts":1729978385.5894427,"logger":"tls.obtain","msg":"acquiring lock","identifier":"tars.paulmaier.online"}
{"level":"info","ts":1729978385.6013052,"logger":"tls.obtain","msg":"lock acquired","identifier":"docker.tars.paulmaier.online"}
{"level":"info","ts":1729978385.601529,"logger":"tls.obtain","msg":"obtaining certificate","identifier":"docker.tars.paulmaier.online"}
{"level":"info","ts":1729978385.6034245,"logger":"http","msg":"waiting on internal rate limiter","identifiers":["docker.tars.paulmaier.online"],"ca":"https://acme-v02.api.letsencrypt.org/directory","account":""}
{"level":"info","ts":1729978385.6034808,"logger":"http","msg":"done waiting on internal rate limiter","identifiers":["docker.tars.paulmaier.online"],"ca":"https://acme-v02.api.letsencrypt.org/directory","account":""}
{"level":"info","ts":1729978385.6034968,"logger":"http","msg":"using ACME account","account_id":"https://acme-v02.api.letsencrypt.org/acme/acct/2020432947","account_contact":[]}
{"level":"info","ts":1729978385.6036146,"logger":"tls.obtain","msg":"lock acquired","identifier":"sync.paulmaier.online"}
{"level":"info","ts":1729978385.6037016,"logger":"tls.obtain","msg":"obtaining certificate","identifier":"sync.paulmaier.online"}
{"level":"info","ts":1729978385.6120772,"logger":"http","msg":"waiting on internal rate limiter","identifiers":["sync.paulmaier.online"],"ca":"https://acme-v02.api.letsencrypt.org/directory","account":""}
{"level":"info","ts":1729978385.6121445,"logger":"http","msg":"done waiting on internal rate limiter","identifiers":["sync.paulmaier.online"],"ca":"https://acme-v02.api.letsencrypt.org/directory","account":""}
{"level":"info","ts":1729978385.6121666,"logger":"http","msg":"using ACME account","account_id":"https://acme-v02.api.letsencrypt.org/acme/acct/2020432947","account_contact":[]}
{"level":"info","ts":1729978385.6141548,"logger":"tls.obtain","msg":"lock acquired","identifier":"photos.paulmaier.online"}
{"level":"info","ts":1729978385.614274,"logger":"tls.obtain","msg":"obtaining certificate","identifier":"photos.paulmaier.online"}
{"level":"info","ts":1729978385.6148582,"logger":"http","msg":"waiting on internal rate limiter","identifiers":["photos.paulmaier.online"],"ca":"https://acme-v02.api.letsencrypt.org/directory","account":""}
{"level":"info","ts":1729978385.6148717,"logger":"http","msg":"done waiting on internal rate limiter","identifiers":["photos.paulmaier.online"],"ca":"https://acme-v02.api.letsencrypt.org/directory","account":""}
{"level":"info","ts":1729978385.6148822,"logger":"http","msg":"using ACME account","account_id":"https://acme-v02.api.letsencrypt.org/acme/acct/2020432947","account_contact":[]}
{"level":"info","ts":1729978385.6183841,"logger":"tls.obtain","msg":"lock acquired","identifier":"tars.paulmaier.online"}
{"level":"info","ts":1729978385.6185741,"logger":"tls.obtain","msg":"obtaining certificate","identifier":"tars.paulmaier.online"}
{"level":"info","ts":1729978385.6193018,"logger":"http","msg":"waiting on internal rate limiter","identifiers":["tars.paulmaier.online"],"ca":"https://acme-v02.api.letsencrypt.org/directory","account":""}
{"level":"info","ts":1729978385.6193411,"logger":"http","msg":"done waiting on internal rate limiter","identifiers":["tars.paulmaier.online"],"ca":"https://acme-v02.api.letsencrypt.org/directory","account":""}
{"level":"info","ts":1729978385.6193557,"logger":"http","msg":"using ACME account","account_id":"https://acme-v02.api.letsencrypt.org/acme/acct/2020432947","account_contact":[]}
{"level":"error","ts":1729978386.2865307,"logger":"tls.obtain","msg":"could not get certificate from issuer","identifier":"test.paulmaier.online","issuer":"acme-v02.api.letsencrypt.org-directory","error":"HTTP 400 urn:ietf:params:acme:error:malformed - JWS verification error"}
{"level":"error","ts":1729978386.2866676,"logger":"tls.obtain","msg":"will retry","error":"[test.paulmaier.online] Obtain: [test.paulmaier.online] creating new order: attempt 1: https://acme-v02.api.letsencrypt.org/acme/new-order: HTTP 400 urn:ietf:params:acme:error:malformed - JWS verification error (ca=https://acme-v02.api.letsencrypt.org/directory)","attempt":1,"retrying_in":60,"elapsed":0.710500468,"max_duration":2592000}
{"level":"error","ts":1729978386.2888002,"logger":"tls.obtain","msg":"could not get certificate from issuer","identifier":"sync.paulmaier.online","issuer":"acme-v02.api.letsencrypt.org-directory","error":"HTTP 400 urn:ietf:params:acme:error:malformed - JWS verification error"}
{"level":"error","ts":1729978386.2889001,"logger":"tls.obtain","msg":"will retry","error":"[sync.paulmaier.online] Obtain: [sync.paulmaier.online] creating new order: attempt 1: https://acme-v02.api.letsencrypt.org/acme/new-order: HTTP 400 urn:ietf:params:acme:error:malformed - JWS verification error (ca=https://acme-v02.api.letsencrypt.org/directory)","attempt":1,"retrying_in":60,"elapsed":0.685273002,"max_duration":2592000}
{"level":"error","ts":1729978386.2900517,"logger":"tls.obtain","msg":"could not get certificate from issuer","identifier":"photos.paulmaier.online","issuer":"acme-v02.api.letsencrypt.org-directory","error":"HTTP 400 urn:ietf:params:acme:error:malformed - JWS verification error"}
{"level":"error","ts":1729978386.2901862,"logger":"tls.obtain","msg":"will retry","error":"[photos.paulmaier.online] Obtain: [photos.paulmaier.online] creating new order: attempt 1: https://acme-v02.api.letsencrypt.org/acme/new-order: HTTP 400 urn:ietf:params:acme:error:malformed - JWS verification error (ca=https://acme-v02.api.letsencrypt.org/directory)","attempt":1,"retrying_in":60,"elapsed":0.676000225,"max_duration":2592000}
{"level":"error","ts":1729978386.2939076,"logger":"tls.obtain","msg":"could not get certificate from issuer","identifier":"tars.paulmaier.online","issuer":"acme-v02.api.letsencrypt.org-directory","error":"HTTP 400 urn:ietf:params:acme:error:malformed - JWS verification error"}
{"level":"error","ts":1729978386.2940345,"logger":"tls.obtain","msg":"will retry","error":"[tars.paulmaier.online] Obtain: [tars.paulmaier.online] creating new order: attempt 1: https://acme-v02.api.letsencrypt.org/acme/new-order: HTTP 400 urn:ietf:params:acme:error:malformed - JWS verification error (ca=https://acme-v02.api.letsencrypt.org/directory)","attempt":1,"retrying_in":60,"elapsed":0.675598048,"max_duration":2592000}
{"level":"error","ts":1729978386.29948,"logger":"tls.obtain","msg":"could not get certificate from issuer","identifier":"docker.tars.paulmaier.online","issuer":"acme-v02.api.letsencrypt.org-directory","error":"HTTP 400 urn:ietf:params:acme:error:malformed - JWS verification error"}
{"level":"error","ts":1729978386.2996392,"logger":"tls.obtain","msg":"will retry","error":"[docker.tars.paulmaier.online] Obtain: [docker.tars.paulmaier.online] creating new order: attempt 1: https://acme-v02.api.letsencrypt.org/acme/new-order: HTTP 400 urn:ietf:params:acme:error:malformed - JWS verification error (ca=https://acme-v02.api.letsencrypt.org/directory)","attempt":1,"retrying_in":60,"elapsed":0.698269711,"max_duration":2592000}

3. Caddy version:

$ caddy version
v2.8.4 h1:q3pe0wpBj1OcHFZ3n/1nl4V4bxBrYoSoab7rL9BMYNk=

4. How I installed and ran Caddy:

a. System environment:

$ lsb_release -a
No LSB modules are available.
Distributor ID:	Ubuntu
Description:	Ubuntu 22.04.5 LTS
Release:	22.04
Codename:	jammy

Caddy install (like in the docs)

sudo apt install -y debian-keyring debian-archive-keyring apt-transport-https curl
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/gpg.key' | sudo gpg --dearmor -o /usr/share/keyrings/caddy-stable-archive-keyring.gpg
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/debian.deb.txt' | sudo tee /etc/apt/sources.list.d/caddy-stable.list
sudo apt update
sudo apt install caddy

b. Command:

$ sudo caddy start --config Caddyfile 
2024/10/26 21:38:58.528	INFO	using config from file	{"file": "Caddyfile"}
2024/10/26 21:38:58.530	INFO	adapted config to JSON	{"adapter": "caddyfile"}
2024/10/26 21:38:58.530	INFO	redirected default logger	{"from": "stderr", "to": "/var/log/caddy/access.log"}
Successfully started Caddy (pid=97138) - Caddy is running in the background

c. My complete Caddy config:

{
	log {
		output file /var/log/caddy/access.log
		format json
	}
}

test.paulmaier.online {
	respond "Hello world"
}

tars.paulmaier.online {
	root * /home/tars/caddy
	file_server
	try_files tars.txt
}

sync.paulmaier.online {
	reverse_proxy http://127.0.0.1:8384
}

photos.paulmaier.online {
	reverse_proxy http://localhost:2342
}

docker.tars.paulmaier.online {
	reverse_proxy https://127.0.0.1:9443 {
		transport http {
			tls
			tls_insecure_skip_verify
		}
	}
}

5. What I already tried

a. Checking DNS

$ ip a
[...]
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000
    link/ether 00:50:56:55:f5:00 brd ff:ff:ff:ff:ff:ff
    altname enp0s18
    altname ens18
    inet 84.247.131.178/20 brd 84.247.143.255 scope global eth0
       valid_lft forever preferred_lft forever
    inet6 fe80::250:56ff:fe55:f500/64 scope link 
       valid_lft forever preferred_lft forever
[...]

tars@vmd152576:~$ dig test.paulmaier.online

; <<>> DiG 9.18.28-0ubuntu0.22.04.1-Ubuntu <<>> test.paulmaier.online
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 10654
;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 65494
;; QUESTION SECTION:
;test.paulmaier.online.		IN	A

;; ANSWER SECTION:
test.paulmaier.online.	3600	IN	A	84.247.131.178

;; Query time: 16 msec
;; SERVER: 127.0.0.53#53(127.0.0.53) (UDP)
;; WHEN: Sat Oct 26 23:44:17 CEST 2024
;; MSG SIZE  rcvd: 66

I also don’t have the domain pointing to two ips which I read was the issue in another case.

b. Generating a certificate with certbot

$ sudo certbot certonly -d test.paulmaier.online
Saving debug log to /var/log/letsencrypt/letsencrypt.log

How would you like to authenticate with the ACME CA?
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1: Spin up a temporary webserver (standalone)
2: Place files in webroot directory (webroot)
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Select the appropriate number [1-2] then [enter] (press 'c' to cancel): 1
Requesting a certificate for test.paulmaier.online

Successfully received certificate.
Certificate is saved at: /etc/letsencrypt/live/test.paulmaier.online/fullchain.pem
Key is saved at:         /etc/letsencrypt/live/test.paulmaier.online/privkey.pem
This certificate expires on 2025-01-24.
These files will be updated when the certificate renews.
Certbot has set up a scheduled task to automatically renew this certificate in the background.

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
If you like Certbot, please consider supporting our work by:
 * Donating to ISRG / Let's Encrypt:   https://letsencrypt.org/donate
 * Donating to EFF:                    https://eff.org/donate-le
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

Worked.

c. Checking Firewall

sudo ufw status
Status: active

To                         Action      From
--                         ------      ----
80                         ALLOW       Anywhere                  
443                        ALLOW       Anywhere                  
[...]             
80 (v6)                    ALLOW       Anywhere (v6)             
443 (v6)                   ALLOW       Anywhere (v6)             
[...] 

Thanks for you help :slight_smile:

PS: right now I am using the stating certs to be able to access my services at all.

Typically when this happens, Caddy’s storage got corrupted for some reason. We’ve put in mitigations for this in the upcoming release, but for now as a workaround you should wipe out Caddy’s storage and restart it to have it try again with fresh state.

Caddy’s storage is at /var/lib/caddy/.local/share/caddy

Never use caddy start when you have Caddy running as a systemd service. It will not run using the correct user. Always use systemctl commands. See Keep Caddy Running — Caddy Documentation

1 Like

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.