Reverse_proxy between valid external domain and a made up internal domain

1. The problem I’m having:

For domain say liveliteandwell.com I wrote a react.js + node.js frontend and backend. Backend starts a HTTPS server using a domain rp-tailscale.esco.ghaar on 8443 - which is not a valid domain on normal internet. Backend handles following paths: rp-tailscale.esco.ghaar/api/. I’m able to secure it (ala start HTTPS) using an internal root ca maintained via step-ca running on a VM on local network. Frontend runs securely (using same certificate) on port 3000. Direct access to rp-tailscale.esco.ghaar:3000 or rp-tailscale.esco.ghaar:8443 to verify functionality works like a charm.

Now comes caddy. On the same machine which runs backend+frontend, I installed Caddy. The idea was to have caddy host liveliteandwell.com (valid domain) for the rest of the world while fronting both the the front- and back-end to serve appropriate content.

Caddyfile has a very basic configuration comprising of one domain, followed by a single reverse_proxy directive (shown below) to test reverse proxying up to backend. But, it does not work.

When employing normal browser, postman or curl:

  1. URL: https://liveliteandwell.com: 404
  2. URL: https://liveliteandwell.com/api: 404

1 behaves as expected (because Caddyfile does not yet specify frontend configs). 2 should present a different msg (Route is valid). This tells me that backend is not being contacted by caddy. Backend logs confirm it. Since all of this happening in the same VM, there should be no need for traffic to leave the VM; even if it did, there is no routing or fw issue (tested extensively).

I’m sure its something basic, and being new to caddy its an RTFM problem. I’ve read local HTTPS TLS and other related wiki articles and posts. Trying suggested combinations of transport headers etc have not helped.

2. Error messages and/or full log output:

curl output for scenario#1 above

$ curl -vL https://liveliteandwell.com
*   Trying 192.168.100.17:443...
* Connected to liveliteandwell.com (192.168.100.17) port 443
* ALPN: curl offers h2,http/1.1
* (304) (OUT), TLS handshake, Client hello (1):
*  CAfile: /etc/ssl/cert.pem
*  CApath: none
* (304) (IN), TLS handshake, Server hello (2):
* (304) (IN), TLS handshake, Unknown (8):
* (304) (IN), TLS handshake, Certificate (11):
* (304) (IN), TLS handshake, CERT verify (15):
* (304) (IN), TLS handshake, Finished (20):
* (304) (OUT), TLS handshake, Finished (20):
* SSL connection using TLSv1.3 / AEAD-AES128-GCM-SHA256
* ALPN: server accepted h2
* Server certificate:
*  subject: CN=liveliteandwell.com
*  start date: Jun 17 00:50:29 2024 GMT
*  expire date: Sep 15 00:50:28 2024 GMT
*  subjectAltName: host "liveliteandwell.com" matched cert's "liveliteandwell.com"
*  issuer: C=US; O=Let's Encrypt; CN=E6
*  SSL certificate verify ok.
* using HTTP/2
* [HTTP/2] [1] OPENED stream for https://liveliteandwell.com/
* [HTTP/2] [1] [:method: GET]
* [HTTP/2] [1] [:scheme: https]
* [HTTP/2] [1] [:authority: liveliteandwell.com]
* [HTTP/2] [1] [:path: /]
* [HTTP/2] [1] [user-agent: curl/8.4.0]
* [HTTP/2] [1] [accept: */*]
> GET / HTTP/2
> Host: liveliteandwell.com
> User-Agent: curl/8.4.0
> Accept: */*
> 
< HTTP/2 404 
< alt-svc: h3=":443"; ma=2592000
< server: Caddy
< content-length: 0
< date: Mon, 17 Jun 2024 03:55:47 GMT
< 
* Connection #0 to host liveliteandwell.com left intact

caddy log for scenario#1

Jun 16 20:55:47 rp-tailscale caddy[14721]: {"level":"debug","ts":1718596547.7198305,"logger":"events","msg":"event","name":"tls_get_certificate","id":"a6deb7c6-673f-4d38-8527-bf1bc3784ac7","origin":"tls","data":{"client_hello":{"CipherSuites":[4866,4867,4865,49200,49196,49192,49188,49172,49162,159,107,57,52393,52392,52394,65413,196,136,129,157,61,53,192,132,49199,49195,49191,49187,49171,49161,158,103,51,190,69,156,60,47,186,65,49169,49159,5,4,49170,49160,22,10,255],"ServerName":"liveliteandwell.com","SupportedCurves":[29,23,24,25],"SupportedPoints":"AA==","SignatureSchemes":[2054,1537,1539,2053,1281,1283,2052,1025,1027,513,515],"SupportedProtos":["h2","http/1.1"],"SupportedVersions":[772,771,770,769],"RemoteAddr":{"IP":"192.168.0.28","Port":54489,"Zone":""},"LocalAddr":{"IP":"192.168.100.17","Port":443,"Zone":""}}}}
Jun 16 20:55:47 rp-tailscale caddy[14721]: {"level":"debug","ts":1718596547.7199287,"logger":"tls.handshake","msg":"choosing certificate","identifier":"liveliteandwell.com","num_choices":1}
Jun 16 20:55:47 rp-tailscale caddy[14721]: {"level":"debug","ts":1718596547.7199898,"logger":"tls.handshake","msg":"default certificate selection results","identifier":"liveliteandwell.com","subjects":["liveliteandwell.com"],"managed":true,"issuer_key":"acme-v02.api.letsencrypt.org-directory","hash":"8e02fc2b7fd7fdf007881e7004fe00a66666501929f409bdc8344a0ab77e6baf"}
Jun 16 20:55:47 rp-tailscale caddy[14721]: {"level":"debug","ts":1718596547.720008,"logger":"tls.handshake","msg":"matched certificate in cache","remote_ip":"192.168.0.28","remote_port":"54489","subjects":["liveliteandwell.com"],"managed":true,"expiration":1726361429,"hash":"8e02fc2b7fd7fdf007881e7004fe00a66666501929f409bdc8344a0ab77e6baf"}
Jun 16 20:55:47 rp-tailscale caddy[14721]: {"level":"info","ts":1718596547.7375457,"logger":"http.log.access","msg":"handled request","request":{"remote_ip":"192.168.0.28","remote_port":"54489","client_ip":"192.168.0.28","proto":"HTTP/2.0","method":"GET","host":"liveliteandwell.com","uri":"/","headers":{"User-Agent":["curl/8.4.0"],"Accept":["*/*"]},"tls":{"resumed":false,"version":772,"cipher_suite":4865,"proto":"h2","server_name":"liveliteandwell.com"}},"bytes_read":0,"user_id":"","duration":0.000031043,"size":0,"status":404,"resp_headers":{"Server":["Caddy"],"Alt-Svc":["h3=\":443\"; ma=2592000"],"Content-Type":[]}}

curl log for scenario#2

$ curl -vL https://liveliteandwell.com/api
*   Trying 192.168.100.17:443...
* Connected to liveliteandwell.com (192.168.100.17) port 443
* ALPN: curl offers h2,http/1.1
* (304) (OUT), TLS handshake, Client hello (1):
*  CAfile: /etc/ssl/cert.pem
*  CApath: none
* (304) (IN), TLS handshake, Server hello (2):
* (304) (IN), TLS handshake, Unknown (8):
* (304) (IN), TLS handshake, Certificate (11):
* (304) (IN), TLS handshake, CERT verify (15):
* (304) (IN), TLS handshake, Finished (20):
* (304) (OUT), TLS handshake, Finished (20):
* SSL connection using TLSv1.3 / AEAD-AES128-GCM-SHA256
* ALPN: server accepted h2
* Server certificate:
*  subject: CN=liveliteandwell.com
*  start date: Jun 17 00:50:29 2024 GMT
*  expire date: Sep 15 00:50:28 2024 GMT
*  subjectAltName: host "liveliteandwell.com" matched cert's "liveliteandwell.com"
*  issuer: C=US; O=Let's Encrypt; CN=E6
*  SSL certificate verify ok.
* using HTTP/2
* [HTTP/2] [1] OPENED stream for https://liveliteandwell.com/api
* [HTTP/2] [1] [:method: GET]
* [HTTP/2] [1] [:scheme: https]
* [HTTP/2] [1] [:authority: liveliteandwell.com]
* [HTTP/2] [1] [:path: /api]
* [HTTP/2] [1] [user-agent: curl/8.4.0]
* [HTTP/2] [1] [accept: */*]
> GET /api HTTP/2
> Host: liveliteandwell.com
> User-Agent: curl/8.4.0
> Accept: */*
> 
< HTTP/2 404 
< alt-svc: h3=":443"; ma=2592000
< server: Caddy
< content-length: 0
< date: Mon, 17 Jun 2024 03:57:35 GMT
< 
* Connection #0 to host liveliteandwell.com left intact

caddy log for scenario#2

Jun 16 20:57:35 rp-tailscale caddy[14721]: {"level":"debug","ts":1718596655.7855647,"logger":"events","msg":"event","name":"tls_get_certificate","id":"73f1da52-a9d2-43e7-9d82-8fb909437a36","origin":"tls","data":{"client_hello":{"CipherSuites":[4866,4867,4865,49200,49196,49192,49188,49172,49162,159,107,57,52393,52392,52394,65413,196,136,129,157,61,53,192,132,49199,49195,49191,49187,49171,49161,158,103,51,190,69,156,60,47,186,65,49169,49159,5,4,49170,49160,22,10,255],"ServerName":"liveliteandwell.com","SupportedCurves":[29,23,24,25],"SupportedPoints":"AA==","SignatureSchemes":[2054,1537,1539,2053,1281,1283,2052,1025,1027,513,515],"SupportedProtos":["h2","http/1.1"],"SupportedVersions":[772,771,770,769],"RemoteAddr":{"IP":"192.168.0.28","Port":54523,"Zone":""},"LocalAddr":{"IP":"192.168.100.17","Port":443,"Zone":""}}}}
Jun 16 20:57:35 rp-tailscale caddy[14721]: {"level":"debug","ts":1718596655.785646,"logger":"tls.handshake","msg":"choosing certificate","identifier":"liveliteandwell.com","num_choices":1}
Jun 16 20:57:35 rp-tailscale caddy[14721]: {"level":"debug","ts":1718596655.785678,"logger":"tls.handshake","msg":"default certificate selection results","identifier":"liveliteandwell.com","subjects":["liveliteandwell.com"],"managed":true,"issuer_key":"acme-v02.api.letsencrypt.org-directory","hash":"8e02fc2b7fd7fdf007881e7004fe00a66666501929f409bdc8344a0ab77e6baf"}
Jun 16 20:57:35 rp-tailscale caddy[14721]: {"level":"debug","ts":1718596655.7856965,"logger":"tls.handshake","msg":"matched certificate in cache","remote_ip":"192.168.0.28","remote_port":"54523","subjects":["liveliteandwell.com"],"managed":true,"expiration":1726361429,"hash":"8e02fc2b7fd7fdf007881e7004fe00a66666501929f409bdc8344a0ab77e6baf"}
Jun 16 20:57:35 rp-tailscale caddy[14721]: {"level":"info","ts":1718596655.8055096,"logger":"http.log.access","msg":"handled request","request":{"remote_ip":"192.168.0.28","remote_port":"54523","client_ip":"192.168.0.28","proto":"HTTP/2.0","method":"GET","host":"liveliteandwell.com","uri":"/api","headers":{"User-Agent":["curl/8.4.0"],"Accept":["*/*"]},"tls":{"resumed":false,"version":772,"cipher_suite":4865,"proto":"h2","server_name":"liveliteandwell.com"}},"bytes_read":0,"user_id":"","duration":0.000025911,"size":0,"status":404,"resp_headers":{"Server":["Caddy"],"Alt-Svc":["h3=\":443\"; ma=2592000"],"Content-Type":[]}}

internal domain certificate validity confirmation

$ sudo certbot certificates
Saving debug log to /var/log/letsencrypt/letsencrypt.log

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Found the following certs:
  Certificate Name: rp-tailscale.esco.ghaar
    Serial Number: <redacted.. but its valid>
    Key Type: RSA
    Domains: rp-tailscale.esco.ghaar
    Expiry Date: <redacted.. but its valid>
    Certificate Path: /etc/letsencrypt/live/rp-tailscale.esco.ghaar/fullchain.pem
    Private Key Path: /etc/letsencrypt/live/rp-tailscale.esco.ghaar/privkey.pem
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

curl log for backend access directly via internal domain (rp-tailscale.esco.ghaar) for scenario#1

$ curl -vkL https://rp-tailscale.esco.ghaar:8443
*   Trying 192.168.100.17:8443...
* Connected to rp-tailscale.esco.ghaar (192.168.100.17) port 8443
* ALPN: curl offers h2,http/1.1
* (304) (OUT), TLS handshake, Client hello (1):
* (304) (IN), TLS handshake, Server hello (2):
* (304) (IN), TLS handshake, Unknown (8):
* (304) (IN), TLS handshake, Certificate (11):
* (304) (IN), TLS handshake, CERT verify (15):
* (304) (IN), TLS handshake, Finished (20):
* (304) (OUT), TLS handshake, Finished (20):
* SSL connection using TLSv1.3 / AEAD-AES256-GCM-SHA384
* ALPN: server accepted http/1.1
* Server certificate:
*  subject: [NONE]
*  start date: Jan 29 16:44:18 2024 GMT
*  expire date: Jan 26 16:45:18 2034 GMT
*  issuer: O=Escoghaar; CN=Escoghaar Intermediate CA
*  SSL certificate verify result: unable to get local issuer certificate (20), continuing anyway.
* using HTTP/1.1
> GET / HTTP/1.1
> Host: rp-tailscale.esco.ghaar:8443
> User-Agent: curl/8.4.0
> Accept: */*
> 
< HTTP/1.1 404 Not Found
< Access-Control-Allow-Origin: *
< Content-Security-Policy: default-src 'self';base-uri 'self';font-src 'self' https: data:;form-action 'self';frame-ancestors 'self';img-src 'self' data:;object-src 'none';script-src 'self';script-src-attr 'none';style-src 'self' https: 'unsafe-inline';upgrade-insecure-requests
< Cross-Origin-Opener-Policy: same-origin
< Cross-Origin-Resource-Policy: same-origin
< Origin-Agent-Cluster: ?1
< Referrer-Policy: no-referrer
< Strict-Transport-Security: max-age=15552000; includeSubDomains
< X-Content-Type-Options: nosniff
< X-DNS-Prefetch-Control: off
< X-Download-Options: noopen
< X-Frame-Options: SAMEORIGIN
< X-Permitted-Cross-Domain-Policies: none
< X-XSS-Protection: 0
< Content-Type: application/json; charset=utf-8
< Content-Length: 26
< ETag: W/"1a-8zibdJXT1RIHjViPh/9pjQz5GqI"
< Date: Mon, 17 Jun 2024 04:00:56 GMT
< Connection: keep-alive
< Keep-Alive: timeout=5
< 
* Connection #0 to host rp-tailscale.esco.ghaar left intact
{"msg":"Route not found."}

curl log for backend access directly via internal domain (rp-tailscale.esco.ghaar) for scenario#2

$ curl -vkL https://rp-tailscale.esco.ghaar:8443/api
*   Trying 192.168.100.17:8443...
* Connected to rp-tailscale.esco.ghaar (192.168.100.17) port 8443
* ALPN: curl offers h2,http/1.1
* (304) (OUT), TLS handshake, Client hello (1):
* (304) (IN), TLS handshake, Server hello (2):
* (304) (IN), TLS handshake, Unknown (8):
* (304) (IN), TLS handshake, Certificate (11):
* (304) (IN), TLS handshake, CERT verify (15):
* (304) (IN), TLS handshake, Finished (20):
* (304) (OUT), TLS handshake, Finished (20):
* SSL connection using TLSv1.3 / AEAD-AES256-GCM-SHA384
* ALPN: server accepted http/1.1
* Server certificate:
*  subject: [NONE]
*  start date: Jan 29 16:44:18 2024 GMT
*  expire date: Jan 26 16:45:18 2034 GMT
*  issuer: O=Escoghaar; CN=Escoghaar Intermediate CA
*  SSL certificate verify result: unable to get local issuer certificate (20), continuing anyway.
* using HTTP/1.1
> GET /api HTTP/1.1
> Host: rp-tailscale.esco.ghaar:8443
> User-Agent: curl/8.4.0
> Accept: */*
> 
< HTTP/1.1 200 OK
< Access-Control-Allow-Origin: *
< Content-Security-Policy: default-src 'self';base-uri 'self';font-src 'self' https: data:;form-action 'self';frame-ancestors 'self';img-src 'self' data:;object-src 'none';script-src 'self';script-src-attr 'none';style-src 'self' https: 'unsafe-inline';upgrade-insecure-requests
< Cross-Origin-Opener-Policy: same-origin
< Cross-Origin-Resource-Policy: same-origin
< Origin-Agent-Cluster: ?1
< Referrer-Policy: no-referrer
< Strict-Transport-Security: max-age=15552000; includeSubDomains
< X-Content-Type-Options: nosniff
< X-DNS-Prefetch-Control: off
< X-Download-Options: noopen
< X-Frame-Options: SAMEORIGIN
< X-Permitted-Cross-Domain-Policies: none
< X-XSS-Protection: 0
< Content-Type: application/json; charset=utf-8
< Content-Length: 24
< ETag: W/"18-rnYwPv+5WQ64AtZltSy3r5DUfsk"
< Date: Mon, 17 Jun 2024 04:05:19 GMT
< Connection: keep-alive
< Keep-Alive: timeout=5
< 
* Connection #0 to host rp-tailscale.esco.ghaar left intact
{"msg":"A valid route."}

3. Caddy version:

v2.8.4 h1:q3pe0wpBj1OcHFZ3n/1nl4V4bxBrYoSoab7rL9BMYNk=

4. How I installed and ran Caddy:

Using official instructions for ubuntu

a. System environment:

OS: Linux rp-tailscale 5.15.0-97-generic #107-Ubuntu SMP Wed Feb 7 13:26:48 UTC 2024 x86_64 x86_64 x86_64 GNU/Linux
systemd: yes, caddy daemon runs fine
docker: installed, but caddy is not running on it

b. Command:

Standard systemd caddy service as setup during installation

$ cat /etc/systemd/system/multi-user.target.wants/caddy.service
# caddy.service
[Unit]
Description=Caddy
Documentation=https://caddyserver.com/docs/
After=network.target network-online.target
Requires=network-online.target

[Service]
Type=notify
User=caddy
Group=caddy
ExecStart=/usr/bin/caddy run --environ --config /etc/caddy/Caddyfile
ExecReload=/usr/bin/caddy reload --config /etc/caddy/Caddyfile --force
TimeoutStopSec=5s
LimitNOFILE=1048576
PrivateTmp=true
ProtectSystem=full
AmbientCapabilities=CAP_NET_ADMIN CAP_NET_BIND_SERVICE

[Install]
WantedBy=multi-user.target

c. Service/unit/compose file:

yes, please see above

d. My complete Caddy config:

{
	debug
	storage file_system {
		root /opt/caddy
	}
}

liveliteandwell.com {
	log
	route {
		reverse_proxy /api/* rp-tailscale.esco.ghaar:8443
		respond 404
	}

	handle_errors {
		respond "{err.status_code} {err.status_text}"
	}
}

5. Links to relevant resources:

This matches only /api/foo but not /api. If you want to also match /api then match /api* instead.

If you mean to proxy over HTTPS, then you’ll need to tell Caddy that you want to do that – the default for reverse_proxy is HTTP. See reverse_proxy (Caddyfile directive) — Caddy Documentation

1 Like

Thank you so much for reading through my issues and helping out!
I should have mentioned that there is nothing at root path /api which matters. It is simply to offer a quick testing check.
3. logs (below) w/ adjusted example of /api/foo yield similar result
4. /api/test yields expected 404 Route not found
5. /api/login also yields expected 404 Route not found when using a) GET, and b) username required when using POST.
But, desired behavior only works with tls_insecure_skip_verify. I’d like to make it work w/ TLS certs (issued by my local step-ca). Any pointers would be appreciated.

adjusted Caddyfile (for scenario#1-5)

liveliteandwell.com {
	log
	route {
		reverse_proxy /api/* rp-tailscale.esco.ghaar:8443 {
			transport http {
				tls_insecure_skip_verify
			}
		}
		respond 404
	}

	handle_errors {
		respond "{err.status_code} {err.status_text}"
	}
}

Adjusted Caddyfile w/ CEL

# The Caddyfile is an easy way to configure your Caddy web server.
#
# Unless the file starts with a global options block, the first
# uncommented line is always the address of your site.
#
# To use your own domain name (with automatic HTTPS), first make
# sure your domain's A/AAAA DNS records are properly pointed to
# this machine's public IP, then replace ":80" below with your
# domain name.

{
  debug
  storage file_system {
    root /opt/caddy
  }
}
liveliteandwell.com {
  log
  @cel_backend_node <<CEL
    ({method} == "GET" || {method} == "POST")
    && {path}.startsWith("/api/")
    CEL

  route {
    reverse_proxy @cel_backend_node rp-tailscale.esco.ghaar:8443 {
      transport http {
        tls_insecure_skip_verify
      }
    }
    respond 404
  }

  handle_errors {
    respond "{err.status_code} {err.status_text}"
  }
}

curl log for scenario#3

$ curl -vL https://liveliteandwell.com/api/test
*   Trying 192.168.100.17:443...
* Connected to liveliteandwell.com (192.168.100.17) port 443
* ALPN: curl offers h2,http/1.1
* (304) (OUT), TLS handshake, Client hello (1):
*  CAfile: /etc/ssl/cert.pem
*  CApath: none
* (304) (IN), TLS handshake, Server hello (2):
* (304) (IN), TLS handshake, Unknown (8):
* (304) (IN), TLS handshake, Certificate (11):
* (304) (IN), TLS handshake, CERT verify (15):
* (304) (IN), TLS handshake, Finished (20):
* (304) (OUT), TLS handshake, Finished (20):
* SSL connection using TLSv1.3 / AEAD-AES128-GCM-SHA256
* ALPN: server accepted h2
* Server certificate:
*  subject: CN=liveliteandwell.com
*  start date: Jun 17 00:50:29 2024 GMT
*  expire date: Sep 15 00:50:28 2024 GMT
*  subjectAltName: host "liveliteandwell.com" matched cert's "liveliteandwell.com"
*  issuer: C=US; O=Let's Encrypt; CN=E6
*  SSL certificate verify ok.
* using HTTP/2
* [HTTP/2] [1] OPENED stream for https://liveliteandwell.com/api/test
* [HTTP/2] [1] [:method: GET]
* [HTTP/2] [1] [:scheme: https]
* [HTTP/2] [1] [:authority: liveliteandwell.com]
* [HTTP/2] [1] [:path: /api/test]
* [HTTP/2] [1] [user-agent: curl/8.4.0]
* [HTTP/2] [1] [accept: */*]
> GET /api/test HTTP/2
> Host: liveliteandwell.com
> User-Agent: curl/8.4.0
> Accept: */*
> 
< HTTP/2 502 
< alt-svc: h3=":443"; ma=2592000
< content-type: text/plain; charset=utf-8
< server: Caddy
< content-length: 15
< date: Mon, 17 Jun 2024 15:03:02 GMT
< 
* Connection #0 to host liveliteandwell.com left intact
502 Bad Gateway(base)

caddy log for scenario#3

Jun 17 08:03:02 rp-tailscale caddy[14721]: {"level":"debug","ts":1718636582.3576386,"logger":"events","msg":"event","name":"tls_get_certificate","id":"06aa9d05-029d-4ca1-9046-7ecb05a96f3c","origin":"tls","data":{"client_hello":{"CipherSuites":[4866,4867,4865,49200,49196,49192,49188,49172,49162,159,107,57,52393,52392,52394,65413,196,136,129,157,61,53,192,132,49199,49195,49191,49187,49171,49161,158,103,51,190,69,156,60,47,186,65,49169,49159,5,4,49170,49160,22,10,255],"ServerName":"liveliteandwell.com","SupportedCurves":[29,23,24,25],"SupportedPoints":"AA==","SignatureSchemes":[2054,1537,1539,2053,1281,1283,2052,1025,1027,513,515],"SupportedProtos":["h2","http/1.1"],"SupportedVersions":[772,771,770,769],"RemoteAddr":{"IP":"192.168.0.28","Port":59188,"Zone":""},"LocalAddr":{"IP":"192.168.100.17","Port":443,"Zone":""}}}}
Jun 17 08:03:02 rp-tailscale caddy[14721]: {"level":"debug","ts":1718636582.3577335,"logger":"tls.handshake","msg":"choosing certificate","identifier":"liveliteandwell.com","num_choices":1}
Jun 17 08:03:02 rp-tailscale caddy[14721]: {"level":"debug","ts":1718636582.357763,"logger":"tls.handshake","msg":"default certificate selection results","identifier":"liveliteandwell.com","subjects":["liveliteandwell.com"],"managed":true,"issuer_key":"acme-v02.api.letsencrypt.org-directory","hash":"8e02fc2b7fd7fdf007881e7004fe00a66666501929f409bdc8344a0ab77e6baf"}
Jun 17 08:03:02 rp-tailscale caddy[14721]: {"level":"debug","ts":1718636582.3578079,"logger":"tls.handshake","msg":"matched certificate in cache","remote_ip":"192.168.0.28","remote_port":"59188","subjects":["liveliteandwell.com"],"managed":true,"expiration":1726361429,"hash":"8e02fc2b7fd7fdf007881e7004fe00a66666501929f409bdc8344a0ab77e6baf"}
Jun 17 08:03:02 rp-tailscale caddy[14721]: {"level":"debug","ts":1718636582.3829753,"logger":"http.handlers.reverse_proxy","msg":"selected upstream","dial":"rp-tailscale.esco.ghaar:8443","total_upstreams":1}
Jun 17 08:03:02 rp-tailscale caddy[14721]: {"level":"debug","ts":1718636582.3889506,"logger":"http.handlers.reverse_proxy","msg":"upstream roundtrip","upstream":"rp-tailscale.esco.ghaar:8443","duration":0.005857445,"request":{"remote_ip":"192.168.0.28","remote_port":"59188","client_ip":"192.168.0.28","proto":"HTTP/2.0","method":"GET","host":"liveliteandwell.com","uri":"/api/test","headers":{"User-Agent":["curl/8.4.0"],"Accept":["*/*"],"X-Forwarded-For":["192.168.0.28"],"X-Forwarded-Proto":["https"],"X-Forwarded-Host":["liveliteandwell.com"]},"tls":{"resumed":false,"version":772,"cipher_suite":4865,"proto":"h2","server_name":"liveliteandwell.com"}},"error":"EOF"}
Jun 17 08:03:02 rp-tailscale caddy[14721]: {"level":"debug","ts":1718636582.389124,"logger":"http.log.error","msg":"EOF","request":{"remote_ip":"192.168.0.28","remote_port":"59188","client_ip":"192.168.0.28","proto":"HTTP/2.0","method":"GET","host":"liveliteandwell.com","uri":"/api/test","headers":{"User-Agent":["curl/8.4.0"],"Accept":["*/*"]},"tls":{"resumed":false,"version":772,"cipher_suite":4865,"proto":"h2","server_name":"liveliteandwell.com"}},"duration":0.006193108,"status":502,"err_id":"y388px189","err_trace":"reverseproxy.statusError (reverseproxy.go:1269)"}
Jun 17 08:03:02 rp-tailscale caddy[14721]: {"level":"error","ts":1718636582.3891509,"logger":"http.log.access","msg":"handled request","request":{"remote_ip":"192.168.0.28","remote_port":"59188","client_ip":"192.168.0.28","proto":"HTTP/2.0","method":"GET","host":"liveliteandwell.com","uri":"/api/test","headers":{"User-Agent":["curl/8.4.0"],"Accept":["*/*"]},"tls":{"resumed":false,"version":772,"cipher_suite":4865,"proto":"h2","server_name":"liveliteandwell.com"}},"bytes_read":0,"user_id":"","duration":0.006193108,"size":15,"status":502,"resp_headers":{"Alt-Svc":["h3=\":443\"; ma=2592000"],"Content-Type":["text/plain; charset=utf-8"],"Server":["Caddy"]}}

With modification of Caddyfile to add http transport options, it now works as expected but only w/ tls_insecure_skip_verify. To make it work without this I assume would require a cascading caddy solution as per this post? Or could i employ the local step-ca certs here?

curl log for scenario#4

curl -vL https://liveliteandwell.com/api/test
*   Trying 192.168.100.17:443...
* Connected to liveliteandwell.com (192.168.100.17) port 443
* ALPN: curl offers h2,http/1.1
* (304) (OUT), TLS handshake, Client hello (1):
*  CAfile: /etc/ssl/cert.pem
*  CApath: none
* (304) (IN), TLS handshake, Server hello (2):
* (304) (IN), TLS handshake, Unknown (8):
* (304) (IN), TLS handshake, Certificate (11):
* (304) (IN), TLS handshake, CERT verify (15):
* (304) (IN), TLS handshake, Finished (20):
* (304) (OUT), TLS handshake, Finished (20):
* SSL connection using TLSv1.3 / AEAD-AES128-GCM-SHA256
* ALPN: server accepted h2
* Server certificate:
*  subject: CN=liveliteandwell.com
*  start date: Jun 17 00:50:29 2024 GMT
*  expire date: Sep 15 00:50:28 2024 GMT
*  subjectAltName: host "liveliteandwell.com" matched cert's "liveliteandwell.com"
*  issuer: C=US; O=Let's Encrypt; CN=E6
*  SSL certificate verify ok.
* using HTTP/2
* [HTTP/2] [1] OPENED stream for https://liveliteandwell.com/api/test
* [HTTP/2] [1] [:method: GET]
* [HTTP/2] [1] [:scheme: https]
* [HTTP/2] [1] [:authority: liveliteandwell.com]
* [HTTP/2] [1] [:path: /api/test]
* [HTTP/2] [1] [user-agent: curl/8.4.0]
* [HTTP/2] [1] [accept: */*]
> GET /api/test HTTP/2
> Host: liveliteandwell.com
> User-Agent: curl/8.4.0
> Accept: */*
> 
< HTTP/2 404 
< access-control-allow-origin: *
< alt-svc: h3=":443"; ma=2592000
< content-security-policy: default-src 'self';base-uri 'self';font-src 'self' https: data:;form-action 'self';frame-ancestors 'self';img-src 'self' data:;object-src 'none';script-src 'self';script-src-attr 'none';style-src 'self' https: 'unsafe-inline';upgrade-insecure-requests
< content-type: application/json; charset=utf-8
< cross-origin-opener-policy: same-origin
< cross-origin-resource-policy: same-origin
< date: Mon, 17 Jun 2024 15:05:54 GMT
< etag: W/"1a-8zibdJXT1RIHjViPh/9pjQz5GqI"
< origin-agent-cluster: ?1
< referrer-policy: no-referrer
< server: Caddy
< strict-transport-security: max-age=15552000; includeSubDomains
< x-content-type-options: nosniff
< x-dns-prefetch-control: off
< x-download-options: noopen
< x-frame-options: SAMEORIGIN
< x-permitted-cross-domain-policies: none
< x-xss-protection: 0
< content-length: 26
< 
* Connection #0 to host liveliteandwell.com left intact
{"msg":"Route not found."

caddy log for scenario#4

Jun 17 08:05:54 rp-tailscale caddy[75046]: {"level":"debug","ts":1718636754.0913277,"logger":"events","msg":"event","name":"tls_get_certificate","id":"f957f037-f9e4-434e-a5f3-76854238e571","origin":"tls","data":{"client_hello":{"CipherSuites":[4866,4867,4865,49200,49196,49192,49188,49172,49162,159,107,57,52393,52392,52394,65413,196,136,129,157,61,53,192,132,49199,49195,49191,49187,49171,49161,158,103,51,190,69,156,60,47,186,65,49169,49159,5,4,49170,49160,22,10,255],"ServerName":"liveliteandwell.com","SupportedCurves":[29,23,24,25],"SupportedPoints":"AA==","SignatureSchemes":[2054,1537,1539,2053,1281,1283,2052,1025,1027,513,515],"SupportedProtos":["h2","http/1.1"],"SupportedVersions":[772,771,770,769],"RemoteAddr":{"IP":"192.168.0.28","Port":59331,"Zone":""},"LocalAddr":{"IP":"192.168.100.17","Port":443,"Zone":""}}}}
Jun 17 08:05:54 rp-tailscale caddy[75046]: {"level":"debug","ts":1718636754.091405,"logger":"tls.handshake","msg":"choosing certificate","identifier":"liveliteandwell.com","num_choices":1}
Jun 17 08:05:54 rp-tailscale caddy[75046]: {"level":"debug","ts":1718636754.0914354,"logger":"tls.handshake","msg":"default certificate selection results","identifier":"liveliteandwell.com","subjects":["liveliteandwell.com"],"managed":true,"issuer_key":"acme-v02.api.letsencrypt.org-directory","hash":"8e02fc2b7fd7fdf007881e7004fe00a66666501929f409bdc8344a0ab77e6baf"}
Jun 17 08:05:54 rp-tailscale caddy[75046]: {"level":"debug","ts":1718636754.091454,"logger":"tls.handshake","msg":"matched certificate in cache","remote_ip":"192.168.0.28","remote_port":"59331","subjects":["liveliteandwell.com"],"managed":true,"expiration":1726361429,"hash":"8e02fc2b7fd7fdf007881e7004fe00a66666501929f409bdc8344a0ab77e6baf"}
Jun 17 08:05:54 rp-tailscale caddy[75046]: {"level":"debug","ts":1718636754.1071868,"logger":"http.handlers.reverse_proxy","msg":"selected upstream","dial":"rp-tailscale.esco.ghaar:8443","total_upstreams":1}
Jun 17 08:05:54 rp-tailscale caddy[75046]: {"level":"debug","ts":1718636754.1212757,"logger":"http.handlers.reverse_proxy","msg":"upstream roundtrip","upstream":"rp-tailscale.esco.ghaar:8443","duration":0.013919226,"request":{"remote_ip":"192.168.0.28","remote_port":"59331","client_ip":"192.168.0.28","proto":"HTTP/2.0","method":"GET","host":"liveliteandwell.com","uri":"/api/test","headers":{"X-Forwarded-For":["192.168.0.28"],"X-Forwarded-Proto":["https"],"X-Forwarded-Host":["liveliteandwell.com"],"User-Agent":["curl/8.4.0"],"Accept":["*/*"]},"tls":{"resumed":false,"version":772,"cipher_suite":4865,"proto":"h2","server_name":"liveliteandwell.com"}},"headers":{"X-Xss-Protection":["0"],"Date":["Mon, 17 Jun 2024 15:05:54 GMT"],"Access-Control-Allow-Origin":["*"],"X-Download-Options":["noopen"],"Strict-Transport-Security":["max-age=15552000; includeSubDomains"],"X-Content-Type-Options":["nosniff"],"Keep-Alive":["timeout=5"],"Cross-Origin-Resource-Policy":["same-origin"],"Origin-Agent-Cluster":["?1"],"X-Permitted-Cross-Domain-Policies":["none"],"Content-Type":["application/json; charset=utf-8"],"Content-Length":["26"],"Content-Security-Policy":["default-src 'self';base-uri 'self';font-src 'self' https: data:;form-action 'self';frame-ancestors 'self';img-src 'self' data:;object-src 'none';script-src 'self';script-src-attr 'none';style-src 'self' https: 'unsafe-inline';upgrade-insecure-requests"],"X-Dns-Prefetch-Control":["off"],"X-Frame-Options":["SAMEORIGIN"],"Etag":["W/\"1a-8zibdJXT1RIHjViPh/9pjQz5GqI\""],"Connection":["keep-alive"],"Cross-Origin-Opener-Policy":["same-origin"],"Referrer-Policy":["no-referrer"]},"status":404}
Jun 17 08:05:54 rp-tailscale caddy[75046]: {"level":"info","ts":1718636754.121969,"logger":"http.log.access","msg":"handled request","request":{"remote_ip":"192.168.0.28","remote_port":"59331","client_ip":"192.168.0.28","proto":"HTTP/2.0","method":"GET","host":"liveliteandwell.com","uri":"/api/test","headers":{"User-Agent":["curl/8.4.0"],"Accept":["*/*"]},"tls":{"resumed":false,"version":772,"cipher_suite":4865,"proto":"h2","server_name":"liveliteandwell.com"}},"bytes_read":0,"user_id":"","duration":0.014881596,"size":26,"status":404,"resp_headers":{"Access-Control-Allow-Origin":["*"],"Origin-Agent-Cluster":["?1"],"Strict-Transport-Security":["max-age=15552000; includeSubDomains"],"X-Content-Type-Options":["nosniff"],"Server":["Caddy"],"Alt-Svc":["h3=\":443\"; ma=2592000"],"Content-Type":["application/json; charset=utf-8"],"X-Download-Options":["noopen"],"Content-Security-Policy":["default-src 'self';base-uri 'self';font-src 'self' https: data:;form-action 'self';frame-ancestors 'self';img-src 'self' data:;object-src 'none';script-src 'self';script-src-attr 'none';style-src 'self' https: 'unsafe-inline';upgrade-insecure-requests"],"X-Permitted-Cross-Domain-Policies":["none"],"Cross-Origin-Opener-Policy":["same-origin"],"X-Xss-Protection":["0"],"Date":["Mon, 17 Jun 2024 15:05:54 GMT"],"Cross-Origin-Resource-Policy":["same-origin"],"Content-Length":["26"],"Referrer-Policy":["no-referrer"],"Etag":["W/\"1a-8zibdJXT1RIHjViPh/9pjQz5GqI\""],"X-Dns-Prefetch-Control":["off"],"X-Frame-Options":["SAMEORIGIN"]}}

curl log for scenario#5.a

$ curl -vL https://liveliteandwell.com/api/login
*   Trying 192.168.100.17:443...
* Connected to liveliteandwell.com (192.168.100.17) port 443
* ALPN: curl offers h2,http/1.1
* (304) (OUT), TLS handshake, Client hello (1):
*  CAfile: /etc/ssl/cert.pem
*  CApath: none
* (304) (IN), TLS handshake, Server hello (2):
* (304) (IN), TLS handshake, Unknown (8):
* (304) (IN), TLS handshake, Certificate (11):
* (304) (IN), TLS handshake, CERT verify (15):
* (304) (IN), TLS handshake, Finished (20):
* (304) (OUT), TLS handshake, Finished (20):
* SSL connection using TLSv1.3 / AEAD-AES128-GCM-SHA256
* ALPN: server accepted h2
* Server certificate:
*  subject: CN=liveliteandwell.com
*  start date: Jun 17 00:50:29 2024 GMT
*  expire date: Sep 15 00:50:28 2024 GMT
*  subjectAltName: host "liveliteandwell.com" matched cert's "liveliteandwell.com"
*  issuer: C=US; O=Let's Encrypt; CN=E6
*  SSL certificate verify ok.
* using HTTP/2
* [HTTP/2] [1] OPENED stream for https://liveliteandwell.com/api/login
* [HTTP/2] [1] [:method: GET]
* [HTTP/2] [1] [:scheme: https]
* [HTTP/2] [1] [:authority: liveliteandwell.com]
* [HTTP/2] [1] [:path: /api/login]
* [HTTP/2] [1] [user-agent: curl/8.4.0]
* [HTTP/2] [1] [accept: */*]
> GET /api/login HTTP/2
> Host: liveliteandwell.com
> User-Agent: curl/8.4.0
> Accept: */*
> 
< HTTP/2 404 
< access-control-allow-origin: *
< alt-svc: h3=":443"; ma=2592000
< content-security-policy: default-src 'self';base-uri 'self';font-src 'self' https: data:;form-action 'self';frame-ancestors 'self';img-src 'self' data:;object-src 'none';script-src 'self';script-src-attr 'none';style-src 'self' https: 'unsafe-inline';upgrade-insecure-requests
< content-type: application/json; charset=utf-8
< cross-origin-opener-policy: same-origin
< cross-origin-resource-policy: same-origin
< date: Mon, 17 Jun 2024 15:08:18 GMT
< etag: W/"1a-8zibdJXT1RIHjViPh/9pjQz5GqI"
< origin-agent-cluster: ?1
< referrer-policy: no-referrer
< server: Caddy
< strict-transport-security: max-age=15552000; includeSubDomains
< x-content-type-options: nosniff
< x-dns-prefetch-control: off
< x-download-options: noopen
< x-frame-options: SAMEORIGIN
< x-permitted-cross-domain-policies: none
< x-xss-protection: 0
< content-length: 26
< 
* Connection #0 to host liveliteandwell.com left intact
{"msg":"Route not found."}

caddy log for scenario#5.a

Jun 17 08:08:18 rp-tailscale caddy[75046]: {"level":"debug","ts":1718636898.3161995,"logger":"events","msg":"event","name":"tls_get_certificate","id":"08b30d9b-2a50-44c0-af06-29e1a1982b02","origin":"tls","data":{"client_hello":{"CipherSuites":[4866,4867,4865,49200,49196,49192,49188,49172,49162,159,107,57,52393,52392,52394,65413,196,136,129,157,61,53,192,132,49199,49195,49191,49187,49171,49161,158,103,51,190,69,156,60,47,186,65,49169,49159,5,4,49170,49160,22,10,255],"ServerName":"liveliteandwell.com","SupportedCurves":[29,23,24,25],"SupportedPoints":"AA==","SignatureSchemes":[2054,1537,1539,2053,1281,1283,2052,1025,1027,513,515],"SupportedProtos":["h2","http/1.1"],"SupportedVersions":[772,771,770,769],"RemoteAddr":{"IP":"192.168.0.28","Port":59365,"Zone":""},"LocalAddr":{"IP":"192.168.100.17","Port":443,"Zone":""}}}}
Jun 17 08:08:18 rp-tailscale caddy[75046]: {"level":"debug","ts":1718636898.3162696,"logger":"tls.handshake","msg":"choosing certificate","identifier":"liveliteandwell.com","num_choices":1}
Jun 17 08:08:18 rp-tailscale caddy[75046]: {"level":"debug","ts":1718636898.316299,"logger":"tls.handshake","msg":"default certificate selection results","identifier":"liveliteandwell.com","subjects":["liveliteandwell.com"],"managed":true,"issuer_key":"acme-v02.api.letsencrypt.org-directory","hash":"8e02fc2b7fd7fdf007881e7004fe00a66666501929f409bdc8344a0ab77e6baf"}
Jun 17 08:08:18 rp-tailscale caddy[75046]: {"level":"debug","ts":1718636898.3163176,"logger":"tls.handshake","msg":"matched certificate in cache","remote_ip":"192.168.0.28","remote_port":"59365","subjects":["liveliteandwell.com"],"managed":true,"expiration":1726361429,"hash":"8e02fc2b7fd7fdf007881e7004fe00a66666501929f409bdc8344a0ab77e6baf"}
Jun 17 08:08:18 rp-tailscale caddy[75046]: {"level":"debug","ts":1718636898.3340027,"logger":"http.handlers.reverse_proxy","msg":"selected upstream","dial":"rp-tailscale.esco.ghaar:8443","total_upstreams":1}
Jun 17 08:08:18 rp-tailscale caddy[75046]: {"level":"debug","ts":1718636898.3458438,"logger":"http.handlers.reverse_proxy","msg":"upstream roundtrip","upstream":"rp-tailscale.esco.ghaar:8443","duration":0.01174952,"request":{"remote_ip":"192.168.0.28","remote_port":"59365","client_ip":"192.168.0.28","proto":"HTTP/2.0","method":"GET","host":"liveliteandwell.com","uri":"/api/login","headers":{"X-Forwarded-Host":["liveliteandwell.com"],"User-Agent":["curl/8.4.0"],"Accept":["*/*"],"X-Forwarded-For":["192.168.0.28"],"X-Forwarded-Proto":["https"]},"tls":{"resumed":false,"version":772,"cipher_suite":4865,"proto":"h2","server_name":"liveliteandwell.com"}},"headers":{"X-Frame-Options":["SAMEORIGIN"],"Content-Type":["application/json; charset=utf-8"],"X-Download-Options":["noopen"],"Strict-Transport-Security":["max-age=15552000; includeSubDomains"],"X-Content-Type-Options":["nosniff"],"Content-Length":["26"],"Cross-Origin-Opener-Policy":["same-origin"],"Origin-Agent-Cluster":["?1"],"Referrer-Policy":["no-referrer"],"X-Xss-Protection":["0"],"Etag":["W/\"1a-8zibdJXT1RIHjViPh/9pjQz5GqI\""],"Keep-Alive":["timeout=5"],"Content-Security-Policy":["default-src 'self';base-uri 'self';font-src 'self' https: data:;form-action 'self';frame-ancestors 'self';img-src 'self' data:;object-src 'none';script-src 'self';script-src-attr 'none';style-src 'self' https: 'unsafe-inline';upgrade-insecure-requests"],"Cross-Origin-Resource-Policy":["same-origin"],"X-Dns-Prefetch-Control":["off"],"X-Permitted-Cross-Domain-Policies":["none"],"Date":["Mon, 17 Jun 2024 15:08:18 GMT"],"Connection":["keep-alive"],"Access-Control-Allow-Origin":["*"]},"status":404}
Jun 17 08:08:18 rp-tailscale caddy[75046]: {"level":"info","ts":1718636898.346188,"logger":"http.log.access","msg":"handled request","request":{"remote_ip":"192.168.0.28","remote_port":"59365","client_ip":"192.168.0.28","proto":"HTTP/2.0","method":"GET","host":"liveliteandwell.com","uri":"/api/login","headers":{"User-Agent":["curl/8.4.0"],"Accept":["*/*"]},"tls":{"resumed":false,"version":772,"cipher_suite":4865,"proto":"h2","server_name":"liveliteandwell.com"}},"bytes_read":0,"user_id":"","duration":0.012290657,"size":26,"status":404,"resp_headers":{"X-Download-Options":["noopen"],"Content-Type":["application/json; charset=utf-8"],"Content-Length":["26"],"Etag":["W/\"1a-8zibdJXT1RIHjViPh/9pjQz5GqI\""],"Alt-Svc":["h3=\":443\"; ma=2592000"],"Date":["Mon, 17 Jun 2024 15:08:18 GMT"],"Origin-Agent-Cluster":["?1"],"Referrer-Policy":["no-referrer"],"X-Xss-Protection":["0"],"Cross-Origin-Resource-Policy":["same-origin"],"X-Dns-Prefetch-Control":["off"],"X-Permitted-Cross-Domain-Policies":["none"],"Cross-Origin-Opener-Policy":["same-origin"],"Strict-Transport-Security":["max-age=15552000; includeSubDomains"],"X-Content-Type-Options":["nosniff"],"Server":["Caddy"],"Access-Control-Allow-Origin":["*"],"X-Frame-Options":["SAMEORIGIN"],"Content-Security-Policy":["default-src 'self';base-uri 'self';font-src 'self' https: data:;form-action 'self';frame-ancestors 'self';img-src 'self' data:;object-src 'none';script-src 'self';script-src-attr 'none';style-src 'self' https: 'unsafe-inline';upgrade-insecure-requests"]}}

curl log for scenario#5.b

$ curl -vL -X POST https://liveliteandwell.com/api/login
*   Trying 192.168.100.17:443...
* Connected to liveliteandwell.com (192.168.100.17) port 443
* ALPN: curl offers h2,http/1.1
* (304) (OUT), TLS handshake, Client hello (1):
*  CAfile: /etc/ssl/cert.pem
*  CApath: none
* (304) (IN), TLS handshake, Server hello (2):
* (304) (IN), TLS handshake, Unknown (8):
* (304) (IN), TLS handshake, Certificate (11):
* (304) (IN), TLS handshake, CERT verify (15):
* (304) (IN), TLS handshake, Finished (20):
* (304) (OUT), TLS handshake, Finished (20):
* SSL connection using TLSv1.3 / AEAD-AES128-GCM-SHA256
* ALPN: server accepted h2
* Server certificate:
*  subject: CN=liveliteandwell.com
*  start date: Jun 17 00:50:29 2024 GMT
*  expire date: Sep 15 00:50:28 2024 GMT
*  subjectAltName: host "liveliteandwell.com" matched cert's "liveliteandwell.com"
*  issuer: C=US; O=Let's Encrypt; CN=E6
*  SSL certificate verify ok.
* using HTTP/2
* [HTTP/2] [1] OPENED stream for https://liveliteandwell.com/api/login
* [HTTP/2] [1] [:method: POST]
* [HTTP/2] [1] [:scheme: https]
* [HTTP/2] [1] [:authority: liveliteandwell.com]
* [HTTP/2] [1] [:path: /api/login]
* [HTTP/2] [1] [user-agent: curl/8.4.0]
* [HTTP/2] [1] [accept: */*]
> POST /api/login HTTP/2
> Host: liveliteandwell.com
> User-Agent: curl/8.4.0
> Accept: */*
> 
< HTTP/2 422 
< access-control-allow-origin: *
< alt-svc: h3=":443"; ma=2592000
< content-security-policy: default-src 'self';base-uri 'self';font-src 'self' https: data:;form-action 'self';frame-ancestors 'self';img-src 'self' data:;object-src 'none';script-src 'self';script-src-attr 'none';style-src 'self' https: 'unsafe-inline';upgrade-insecure-requests
< content-type: application/json; charset=utf-8
< cross-origin-opener-policy: same-origin
< cross-origin-resource-policy: same-origin
< date: Mon, 17 Jun 2024 15:28:23 GMT
< etag: W/"22-+wDntknFT/dKSzLkU/F82EU8IG8"
< origin-agent-cluster: ?1
< referrer-policy: no-referrer
< server: Caddy
< strict-transport-security: max-age=15552000; includeSubDomains
< x-content-type-options: nosniff
< x-dns-prefetch-control: off
< x-download-options: noopen
< x-frame-options: SAMEORIGIN
< x-permitted-cross-domain-policies: none
< x-xss-protection: 0
< content-length: 34
< 
* Connection #0 to host liveliteandwell.com left intact
{"msg":"\"Username\" is required"}

You need to either add the root CA cert for your upstream app to your machine Caddy is running on, or specify the tls_trust_pool option to point to your root CA cert to trust.

1 Like

Once again, much obliged @francislavoie.

The issue I run into is that tls_trust_pool is declared as part of tls authentication; which is at the domain level? e.g. doing the below results in

  route {
    # Backend Node
    reverse_proxy @cel_backend_node rp-tailscale.esco.ghaar:8443 {
      transport http {
        #tls_insecure_skip_verify
        tls {
          client_auth {
            mode require_and_verify
            trust_pool file {
              pem_file /tmp/certs/cert.pem
            }
          }
        }
      }
    }
Error: adapting config using caddyfile: parsing caddyfile tokens for 'route': parsing caddyfile tokens for 'reverse_proxy': unrecognized subdirective {, at /etc/caddy/Caddyfile:26, at /etc/caddy/Caddyfile:44

seems tls definitions like so can’t be done inside http blocks. External domain liveliteandwell.com uses normal LE to get its certs; this TLS client capability shouldn’t be altered with. Only the reverse_proxy portion interfacing towards internal domain needs to be tinkered with.

No there’s also tls_trust_pool directly inside of the transport (not in client_auth). You shouldn’t put anything inside tls, and you shouldn’t configure client_auth here.

1 Like

I see. I did read so in reverse_proxy section but didn’t realize its meaning and usage. Please grant me until tomorrow morning to test out placement of tls_trust_pool inside http block.

if only this certificate business was easy :wink:

We’re back to the signed by unknown authority issue for <ext_domain>/api/test

Caddyfile

liveliteandwell.com {
  log
  @cel_backend_node <<CEL
    ({method} == "GET" || {method} == "POST")
    && {path}.startsWith("/api/")
    CEL

  route {
    # Backend Node
    reverse_proxy @cel_backend_node rp-tailscale.esco.ghaar:8443 {
      transport http {
        #tls_insecure_skip_verify
        tls_trust_pool file {
          pem_file /opt/caddy/certificates/ldap_acme_root_ca.crt
         }
       }
    }

    # react router app 
    handle_path / {
      root * /tmp/build
      try_files {path} /index.html
      file_server
    }
    respond 404
  }
Jun 18 07:16:44 rp-tailscale caddy[123948]: {"level":"debug","ts":1718720204.0519593,"logger":"http.handlers.reverse_proxy","msg":"selected upstream","dial":"rp-tailscale.esco.ghaar:8443","total_upstreams":1}
Jun 18 07:16:44 rp-tailscale caddy[123948]: {"level":"debug","ts":1718720204.064225,"logger":"http.handlers.reverse_proxy","msg":"upstream roundtrip","upstream":"rp-tailscale.esco.ghaar:8443","duration":0.012178359,"request":{"remote_ip":"192.168.0.28","remote_port":"50249","client_ip":"192.168.0.28","proto":"HTTP/2.0","method":"GET","host":"liveliteandwell.com","uri":"/api/login","headers":{"X-Forwarded-Proto":["https"],"X-Forwarded-Host":["liveliteandwell.com"],"User-Agent":["curl/8.4.0"],"Accept":["*/*"],"X-Forwarded-For":["192.168.0.28"]},"tls":{"resumed":false,"version":772,"cipher_suite":4865,"proto":"h2","server_name":"liveliteandwell.com"}},"error":"tls: failed to verify certificate: x509: certificate signed by unknown authority"}
Jun 18 07:16:44 rp-tailscale caddy[123948]: {"level":"debug","ts":1718720204.0643835,"logger":"http.log.error","msg":"tls: failed to verify certificate: x509: certificate signed by unknown authority","request":{"remote_ip":"192.168.0.28","remote_port":"50249","client_ip":"192.168.0.28","proto":"HTTP/2.0","method":"GET","host":"liveliteandwell.com","uri":"/api/login","headers":{"User-Agent":["curl/8.4.0"],"Accept":["*/*"]},"tls":{"resumed":false,"version":772,"cipher_suite":4865,"proto":"h2","server_name":"liveliteandwell.com"}},"duration":0.012525927,"status":502,"err_id":"acvuw1w2d","err_trace":"reverseproxy.statusError (reverseproxy.go:1269)"}
Jun 18 07:16:44 rp-tailscale caddy[123948]: {"level":"error","ts":1718720204.0644145,"logger":"http.log.access","msg":"handled request","request":{"remote_ip":"192.168.0.28","remote_port":"50249","client_ip":"192.168.0.28","proto":"HTTP/2.0","method":"GET","host":"liveliteandwell.com","uri":"/api/login","headers":{"User-Agent":["curl/8.4.0"],"Accept":["*/*"]},"tls":{"resumed":false,"version":772,"cipher_suite":4865,"proto":"h2","server_name":"liveliteandwell.com"}},"bytes_read":0,"user_id":"","duration":0.012525927,"size":15,"status":502,"resp_headers":{"Server":["Caddy"],"Alt-Svc":["h3=\":443\"; ma=2592000"],"Content-Type":["text/plain; charset=utf-8"]}}

I see the file name you’re adding says root in it. Is the new cert of upstream signed by the root or an intermediate? You can check this using openssl by running the following command:

openssl x509 -in <cert-pem-filename> -noout -text

Run the command against the new certificate of the upstream and the certificate ldap_acme_root_ca.crt. In openssl’s output, check the following:

  • rp-tailscale.esco.ghaar certificate: the value of X509v3 Authority Key Identifier
  • the certificate ldap_acme_root_ca.crt: The value of X509v3 Subject Key Identifier

If the values mismatch, then it’s very likely the new certificate was signed by the intermediate, not root. You’ll need to either add the intermediate to the trust pool or have the certs be signed by root (ldap_acme_root_ca).

1 Like

Thank you @Mohammed90 for the guidance. Please give me a day or so to execute the commands and revert.

ldap_acme_root_ca: is the certificate of my step-ca root

X509v3 Subject Key Identifier: 11:46:7D:2F:9A:88:10:3A:9A:0E:AD:DD:C1:18:B7:77:CC:CB:51:04

rp-tailscale.esco.ghaar is a certificate issued to the VM at the time of its creation, as part of my cloudinit scripts. It does so by running my custom scripts which amongst other things installs and configures a certbot client followed by request cert for this VM via step-ca ACME implementation. ldap_acme_root_ca.crt is the root cert of this provider (or so i thought :-)).

X509v3 Authority Key Identifier: 08:87:A9:68:8A:B9:A9:77:06:C0:C6:5C:D3:FE:B0:59:62:79:45:97

It seems step-ca by default generates both root and intermediate. And, one is supposed to use the intermediate for further signatures?

intermediate.crt has the following:

X509v3 Subject Key Identifier: 08:87:A9:68:8A:B9:A9:77:06:C0:C6:5C:D3:FE:B0:59:62:79:45:97
X509v3 Authority Key Identifier: 11:46:7D:2F:9A:88:10:3A:9A:0E:AD:DD:C1:18:B7:77:CC:CB:51:04

So, you may have identified the heart of the issue! I need to configure as follows:

 tls_trust_pool file {
    pem_file /opt/caddy/certificates/<intermediate.crt>
 }

I’ll make the change over the weekend and report back!

1 Like

Yes, this is standard practice. The root certificate is typically long-lived and not directly used for leaf certificate issuance. They’re used to sign intermediate certificates which are in turn used to sign leaf certificates for direct usage. The intermediate certificates have shorter lifespan than root certs, but longer than leaf certs. Caddy PKI follows the same pattern.

3 Likes

You should trust the root (not intermediate) in your Caddy config, and your upstream should contain both the intermediate & leaf in its configuration (what it will serve to Caddy). And yes you sign your leaf cert with the intermediate, not the root.

Then when Caddy does trust checks, it will follow the chain, the configured root → intermediate → leaf (where the last two are sent along during the TLS handshake by the upstream).

Browsers have a set of root certs in their trust stores, and servers are expected to send the intermediate and the leaf. You should set it up the same way for connections between Caddy and your upstream.

3 Likes

Thank you @Mohammed90 and @francislavoie . I did not get a chance to digest both your comments together and implement it yet.

Am I to understand correctly that:

  1. instead of signing the cert for rp-tailscale.esco.ghaar with root (which is the case presently), I ought to sign it with my ACME providers’ intermediate certificate
  2. In addition to the root cert, update Ubuntu system store to trust the intermediate cert
  3. Configure Caddyfile for above domain with tls_trust_pool pointing to root cert of my ACME provider

If so, do please grant me a few more days :pray:t4:

No. Only trust the root cert. Your server should be configured with a file which has both the leaf and intermediate in it, not just the leaf.

3 Likes

Francis is right. Your upstream server should send the concatenation of the intermediate and leaf cert.

2 Likes

Thank you again, @Mohammed90 and @francislavoie . Apologies for being dense, but with your recent explanations, and my misunderstandings, please allow me to clarify a few aspects.

My setup:
Browser ↔ VM1 <—> VM2

where:

  • VM1 hosts Caddy 2.0 + react app front-end + node backend
  • VM2 hosts step-ca

VM2
Name : ldap.esco.ghaar
Root: step-ca + ACME provider for internal domain esco.ghaar.
Root cert: root.ca.crt

X509v3 Subject Key Identifier:
 11:46:7D:2F:9A:88:10:3A:9A:0E:AD:DD:C1:18:B7:77:CC:CB:51:04

Intermediate cert: intermediate_ca.crt

X509v3 Subject Key Identifier:
 08:87:A9:68:8A:B9:A9:77:06:C0:C6:5C:D3:FE:B0:59:62:79:45:97
X509v3 Authority Key Identifier:
 11:46:7D:2F:9A:88:10:3A:9A:0E:AD:DD:C1:18:B7:77:CC:CB:51:04

filename ldap_acme_root_ca.crt (propagated to other machines in then network) actually maps to intermediate_ca.crt above; not to root_ca.crt as I previously mis-stated. Apologies. I generated the certs a while ago, and had forgotten that it is not best practice to ship root certs outside of the machine it was generated on and/or secure key storage (like ubikey etc). So, I had renamed trust store file in leafs as ldap_acme_root_ca.crt to fool others in case my network was hijacked. Apparently I fooled myself :man_facepalming:t4:

VM1
Name : rp-tailscale.esco.ghaar
Host cert: Certificate for the machine itself. Stored in /etc/letsencrypt/rp-tailscale.esco.ghaar after executing command:

sudo certbot certonly --standalone -n --agree-tos -m <something> -d `hostname -f` --server https://ldap.esco.ghaar:8443/acme/acme/directory

which issues the following:
a) actual cert for the requested entity (rp-tailscale.esco.ghaar) in cert.pem. (known as leaf in your message?
b) short chain in chain.pem
c) full chian in fullchain.pem
d) private key ofcourse in privkey.pem

Below is the output for fullchain.pem

sudo openssl x509 -in /etc/letsencrypt/live/rp-tailscale.esco.ghaar/fullchain.pem -noout -text
X509v3 Subject Key Identifier:
 2B:F6:10:86:C0:17:91:A3:DC:F8:DF:AE:6D:D1:2F:68:18:03:7A:0A
X509v3 Authority Key Identifier:
  08:87:A9:68:8A:B9:A9:77:06:C0:C6:5C:D3:FE:B0:59:62:79:45:97
X509v3 Subject Alternative Name: critical
 DNS:rp-tailscale.esco.ghaar

Based on above, because X509v3 Authority points to 08:87... I assume the cert was signed by step-ca using intermediate_ca.crt not root_ca.crt. This also confirms both your statements:

you sign your leaf cert with the intermediate, not the root.

So, to make this config work (and wanting to get caddy metrics as well), my Caddyfile should look like

{
  debug
  storage file_system {
    root /opt/caddy
  }

  #crowdsec
  crowdsec {
      api_url <something>
      api_key <something>
      ticker_interval 15s
  }
}

http://liveliteandwell.com {
  redir https://{host}{uri} permanent
}

https://liveliteandwell.com {
  log
  encode gzip zstd

  @cel_backend_node <<CEL
    ({method} == "GET" || {method} == "POST")
    && {path}.startsWith("/api/")
    CEL
  
  @cel_react <<CEL
    ({method} == "GET" || {method} == "POST")
    && ! {path}.startsWith("/api/")
    CEL

  route {
    # Backend Node 
#    reverse_proxy @cel_backend_node rp-tailscale.esco.ghaar:8443 {
#      transport http {
#        #tls_insecure_skip_verify
#        tls_trust_pool file {
#          pem_file /opt/caddy/certificates/ldap_acme_root_ca.crt
#         }
#       }
#    }
    
    # crowdsec based filtering
    crowdsec
    # WORKS: Backend node using non-HTTPS localhost
    reverse_proxy @cel_backend_node localhost:8443

    # React app
    reverse_proxy @cel_react localhost:3000 {
    
    #Cross-Origin-Embedder-Policy: require-corp
    header_down +Content-Security-Policy" "default-src 'self';base-uri 'self';font-src 'self' https: data:;form-action 'self';frame-ancestors 'self';img-src 'self' data:;object-src 'none';script-src 'self';script-src-attr 'none';style-src 'self' https: 'unsafe-inline';upgrade-insecure-requests"
    header_down +Cross-Origin-Opener-Policy "same-origin"
    header_down +Cross-Origin-Resource-Policy "same-origin"
    header_down +X-DNS-Prefetch-Control "off"
    header_down +X-Frame-Options "SAMEORIGIN"
    header_down +Strict-Transport-Security "max-age=15552000; includeSubDomains"
    header_down +X-Download-Options "noopen"
    header_down +X-Content-Type-Options "nosniff"
    #Origin-Agent-Cluster: ?1
    header_down +X-Permitted-Cross-Domain-Policies "none"
    header_down +Referrer-Policy "no-referrer"
    header_down +X-XSS-Protection "0"
    }
    respond 404
  }

  handle_errors {
    respond "{err.status_code} {err.status_text}"
  }
}

rp-tailscale.esco.ghaar:8443 localhost:8443 {
  metrics /metrics
#C0  
tls {
#C1
    ca https://ldap.esco.ghaar:8443/acme/acme/directory
#C2    
   ca_root /etc/ssl/certs/ldap_acme_real_root_ca.pem
  }
 handle_errors {
    respond "{err.status_code} {err.status_text}"
  }
}

If true, then
i) wouldn’t #C2 be dangerous in that step-ca root cert will have to made known outside VM2?
ii) wouldn’t #C1, #C2, #C3 tell Caddy to ask my step-ca to generate a new pair of certs? If so, my intent is not to generate new cert pairs, but to use VM2’s cert; because development environment for when writing Node + react code becomes difficult to manage due to permission issues.

Any thoughts on how best to proceed?

No, shipping the root certificates is okay. In fact, you have to, to establish any kind of trust.

What needs to remain secret is the root private key. That’s the actual must stay secret thing, that’s what gets used to do cryptographic operations.

The certificate contains some metadata (valid domain, issuer name, expiration time, etc), plus the public portion of the key (which as the name implies, is meant to be public) which allows someone to encrypt something using that key which only the owner of the matching private key could decrypt.

Take a look at Chains of Trust - Let's Encrypt, this might help you understand how the chain of trust works. Let’s Encrypt obviously publishes their root certs, because those are installed in the trust stores of all (most) browsers and OSes. There’s no reason you can’t also “publish” (well, doesn’t have to be public to the internet, but “public” to your connecting apps) your root cert.

Let’s Encrypt (and ever other CA) keeps their private keys super mega protected because if anyone gets access to them, they could essentially compromise the entire internet (issue certs for any website and be trusted by all browsers). This involves usually requiring physical access to some machine, by more than one person at the same time holding some key, where all or most of the people involved to unlock it. When they set this up, it’s called a “Root Signing Ceremony”, it’s a whole process, and the end result is the private keys are generated and locked away to only be accessible to issue new intermediate certs on occasion, whenever the time comes, or to do some fancy cross-sign or whatever.

4 Likes

Much obliged, @francislavoie and @Mohammed90.

Case#1
Now with pointing to my ACME provider’s root certificate, I get a new error. Despite of specifying the tls_trust_pool directive, it seems to be reaching out to LE’s acme, not my acme.

Caddyfile changes:

rp-tailscale.esco.ghaar:8443 localhost:8443 {
  metrics /metrics
  tls {
    client_auth {
      mode verify_if_given
      trust_pool file {
         pem_file /opt/caddy/certificates/ldap_acme_actualroot_ca.crt
      }
    }
  }
}

Log

Jun 26 16:48:46 rp-tailscale caddy[329569]: {"level":"info","ts":1719445726.491728,"logger":"tls.obtain","msg":"obtaining certificate","identifier":"rp-tailscale.esco.ghaar"}
Jun 26 16:48:46 rp-tailscale caddy[329569]: {"level":"debug","ts":1719445726.49182,"logger":"events","msg":"event","name":"cert_obtaining","id":"862b9987-7954-4bf9-835a-5bdaab63e96b","origin":"tls","data":{"identifier":"rp-tailscale.esco.ghaar"}}
Jun 26 16:48:46 rp-tailscale caddy[329569]: {"level":"debug","ts":1719445726.4921155,"logger":"tls.obtain","msg":"trying issuer 1/1","issuer":"acme-v02.api.letsencrypt.org-directory"}
Jun 26 16:48:46 rp-tailscale caddy[329569]: {"level":"info","ts":1719445726.4925976,"logger":"tls","msg":"waiting on internal rate limiter","identifiers":["rp-tailscale.esco.ghaar"],"ca":"https://acme-v02.api.letsencrypt.org/directory","account":""}
Jun 26 16:48:46 rp-tailscale caddy[329569]: {"level":"info","ts":1719445726.492621,"logger":"tls","msg":"done waiting on internal rate limiter","identifiers":["rp-tailscale.esco.ghaar"],"ca":"https://acme-v02.api.letsencrypt.org/directory","account":""}
Jun 26 16:48:46 rp-tailscale caddy[329569]: {"level":"info","ts":1719445726.4926353,"logger":"tls","msg":"using ACME account","account_id":"https://acme-v02.api.letsencrypt.org/acme/acct/1621205577","account_contact":[]}
Jun 26 16:48:46 rp-tailscale caddy[329569]: {"level":"debug","ts":1719445726.6134524,"logger":"tls.acme_client","msg":"http request","method":"GET","url":"https://acme-v02.api.letsencrypt.org/directory","headers":{"User-Agent":["Caddy/2.8.4 CertMagic acmez (linux; amd64)"]},"response_headers":{"Cache-Control":["public, max-age=0, no-cache"],"Content-Length":["746"],"Content-Type":["application/json"],"Date":["Wed, 26 Jun 2024 23:48:46 GMT"],"Server":["nginx"],"Strict-Transport-Security":["max-age=604800"],"X-Frame-Options":["DENY"]},"status_code":200}
Jun 26 16:48:46 rp-tailscale caddy[329569]: {"level":"debug","ts":1719445726.6147575,"logger":"tls.acme_client","msg":"creating order","account":"https://acme-v02.api.letsencrypt.org/acme/acct/1621205577","identifiers":["rp-tailscale.esco.ghaar"]}
Jun 26 16:48:46 rp-tailscale caddy[329569]: {"level":"debug","ts":1719445726.6532178,"logger":"tls.acme_client","msg":"http request","method":"HEAD","url":"https://acme-v02.api.letsencrypt.org/acme/new-nonce","headers":{"User-Agent":["Caddy/2.8.4 CertMagic acmez (linux; amd64)"]},"response_headers":{"Cache-Control":["public, max-age=0, no-cache"],"Date":["Wed, 26 Jun 2024 23:48:46 GMT"],"Link":["<https://acme-v02.api.letsencrypt.org/directory>;rel=\"index\""],"Replay-Nonce":["lCR6it1E-0yy8DR-rMn8Conh-OP4OaDeL-qW7OBV4WeACQy6qfA"],"Server":["nginx"],"Strict-Transport-Security":["max-age=604800"],"X-Frame-Options":["DENY"]},"status_code":200}
Jun 26 16:48:46 rp-tailscale caddy[329569]: {"level":"debug","ts":1719445726.6940773,"logger":"tls.acme_client","msg":"http request","method":"POST","url":"https://acme-v02.api.letsencrypt.org/acme/new-order","headers":{"Content-Type":["application/jose+json"],"User-Agent":["Caddy/2.8.4 CertMagic acmez (linux; amd64)"]},"response_headers":{"Boulder-Requester":["1621205577"],"Cache-Control":["public, max-age=0, no-cache"],"Content-Length":["230"],"Content-Type":["application/problem+json"],"Date":["Wed, 26 Jun 2024 23:48:46 GMT"],"Link":["<https://acme-v02.api.letsencrypt.org/directory>;rel=\"index\""],"Replay-Nonce":["1VRjeNN9Y3r8s5YE948LoXtmBao7QgP84BPj_K-lUZuDaUvUOLo"],"Server":["nginx"]},"status_code":400}
Jun 26 16:48:46 rp-tailscale caddy[329569]: {"level":"error","ts":1719445726.6948318,"logger":"tls.obtain","msg":"could not get certificate from issuer","identifier":"rp-tailscale.esco.ghaar","issuer":"acme-v02.api.letsencrypt.org-directory","error":"HTTP 400 urn:ietf:params:acme:error:rejectedIdentifier - Invalid identifiers requested :: Cannot issue for \"rp-tailscale.esco.ghaar\": Domain name does not end with a valid public suffix (TLD)"}
Jun 26 16:48:46 rp-tailscale caddy[329569]: {"level":"debug","ts":1719445726.6952136,"logger":"events","msg":"event","name":"cert_failed","id":"b217aa65-2efb-4cea-8c10-895a367872c3","origin":"tls","data":{"error":{},"identifier":"rp-tailscale.esco.ghaar","issuers":["acme-v02.api.letsencrypt.org-directory"],"renewal":false}}
Jun 26 16:48:46 rp-tailscale caddy[329569]: {"level":"error","ts":1719445726.6954522,"logger":"tls.obtain","msg":"will retry","error":"[rp-tailscale.esco.ghaar] Obtain: [rp-tailscale.esco.ghaar] creating new order: attempt 1: https://acme-v02.api.letsencrypt.org/acme/new-order: HTTP 400 urn:ietf:params:acme:error:rejectedIdentifier - Invalid identifiers requested :: Cannot issue for \"rp-tailscale.esco.ghaar\": Domain name does not end with a valid public suffix (TLD) (ca=https://acme-v02.api.letsencrypt.org/directory)","attempt":1,"retrying_in":60,"elapsed":0.203848675,"max_duration":2592000}

Case 2
Only specifying ca, ca_root as tls options results in what i think is a loop?

Caddyfile:

rp-tailscale.esco.ghaar:8443 localhost:8443 {
  metrics /metrics
  tls {
    ca https://ldap.esco.ghaar:8443/acme/acme/directory
    ca_root /opt/caddy/certificates/ldap_acme_actualroot_ca.crt
  }
}

Log:

Jun 26 16:55:47 rp-tailscale caddy[329621]: {"level":"debug","ts":1719446147.4448357,"logger":"tls.obtain","msg":"trying issuer 1/1","issuer":"ldap.esco.ghaar:8443-acme-acme-directory"}
Jun 26 16:55:47 rp-tailscale caddy[329621]: {"level":"info","ts":1719446147.445753,"logger":"tls.issuance.acme","msg":"waiting on internal rate limiter","identifiers":["localhost"],"ca":"https://ldap.esco.ghaar:8443/acme/acme/directory","account":""}
Jun 26 16:55:47 rp-tailscale caddy[329621]: {"level":"info","ts":1719446147.4457982,"logger":"tls.issuance.acme","msg":"done waiting on internal rate limiter","identifiers":["localhost"],"ca":"https://ldap.esco.ghaar:8443/acme/acme/directory","account":""}
Jun 26 16:55:47 rp-tailscale caddy[329621]: {"level":"info","ts":1719446147.4458234,"logger":"tls.issuance.acme","msg":"using ACME account","account_id":"https://ldap.esco.ghaar:8443/acme/acme/account/4O4RT69wyLGrjJO5xshgUwcG99J3mgkH","account_contact":[]}
Jun 26 16:55:47 rp-tailscale caddy[329621]: {"level":"debug","ts":1719446147.457179,"logger":"tls.issuance.acme.acme_client","msg":"http request","method":"GET","url":"https://ldap.esco.ghaar:8443/acme/acme/directory","headers":{"User-Agent":["Caddy/2.8.4 CertMagic acmez (linux; amd64)"]},"response_headers":{"Content-Length":["322"],"Content-Type":["application/json"],"Date":["Wed, 26 Jun 2024 23:55:47 GMT"]},"status_code":200}
Jun 26 16:55:47 rp-tailscale caddy[329621]: {"level":"debug","ts":1719446147.4575262,"logger":"tls.issuance.acme.acme_client","msg":"creating order","account":"https://ldap.esco.ghaar:8443/acme/acme/account/4O4RT69wyLGrjJO5xshgUwcG99J3mgkH","identifiers":["localhost"]}
Jun 26 16:55:47 rp-tailscale caddy[329621]: {"level":"debug","ts":1719446147.4641097,"logger":"tls.issuance.acme.acme_client","msg":"http request","method":"HEAD","url":"https://ldap.esco.ghaar:8443/acme/acme/new-nonce","headers":{"User-Agent":["Caddy/2.8.4 CertMagic acmez (linux; amd64)"]},"response_headers":{"Cache-Control":["no-store"],"Date":["Wed, 26 Jun 2024 23:55:47 GMT"],"Link":["<https://ldap.esco.ghaar:8443/acme/acme/directory>;rel=\"index\""],"Replay-Nonce":["eTNqVERVeUFGZHBJNVF5RWJseEJaWjVEQ0MwcmpXN1I"]},"status_code":200}
Jun 26 16:55:47 rp-tailscale caddy[329621]: {"level":"debug","ts":1719446147.4899275,"logger":"tls.issuance.acme.acme_client","msg":"http request","method":"POST","url":"https://ldap.esco.ghaar:8443/acme/acme/new-order","headers":{"Content-Type":["application/jose+json"],"User-Agent":["Caddy/2.8.4 CertMagic acmez (linux; amd64)"]},"response_headers":{"Cache-Control":["no-store"],"Content-Length":["413"],"Content-Type":["application/json"],"Date":["Wed, 26 Jun 2024 23:55:47 GMT"],"Link":["<https://ldap.esco.ghaar:8443/acme/acme/directory>;rel=\"index\""],"Location":["https://ldap.esco.ghaar:8443/acme/acme/order/b4I0SW6iROOsdsjwBIXfKpxoN6t7u5WM"],"Replay-Nonce":["R1JoeEsxZ2c2UjV0elE5RmJvTmE5QkprUWk5cTdaRW8"]},"status_code":201}
Jun 26 16:55:47 rp-tailscale caddy[329621]: {"level":"debug","ts":1719446147.4965148,"logger":"tls.issuance.acme.acme_client","msg":"http request","method":"POST","url":"https://ldap.esco.ghaar:8443/acme/acme/authz/wh2f7QuzIZpNXFA2kCrqKYb056g0vN3k","headers":{"Content-Type":["application/jose+json"],"User-Agent":["Caddy/2.8.4 CertMagic acmez (linux; amd64)"]},"response_headers":{"Cache-Control":["no-store"],"Content-Length":["749"],"Content-Type":["application/json"],"Date":["Wed, 26 Jun 2024 23:55:47 GMT"],"Link":["<https://ldap.esco.ghaar:8443/acme/acme/directory>;rel=\"index\""],"Location":["https://ldap.esco.ghaar:8443/acme/acme/authz/wh2f7QuzIZpNXFA2kCrqKYb056g0vN3k"],"Replay-Nonce":["VGMyeWFFWU1TUEFiR05WY1ozRlc0eHNtOXdwRkl3RHM"]},"status_code":200}
Jun 26 16:55:47 rp-tailscale caddy[329621]: {"level":"info","ts":1719446147.49688,"logger":"tls.issuance.acme.acme_client","msg":"trying to solve challenge","identifier":"localhost","challenge_type":"http-01","ca":"https://ldap.esco.ghaar:8443/acme/acme/directory"}
Jun 26 16:55:47 rp-tailscale caddy[329621]: {"level":"debug","ts":1719446147.4976785,"logger":"tls.issuance.acme.acme_client","msg":"waiting for solver before continuing","identifier":"localhost","challenge_type":"http-01"}
Jun 26 16:55:47 rp-tailscale caddy[329621]: {"level":"debug","ts":1719446147.4977171,"logger":"tls.issuance.acme.acme_client","msg":"done waiting for solver","identifier":"localhost","challenge_type":"http-01"}
Jun 26 16:55:47 rp-tailscale caddy[329621]: {"level":"debug","ts":1719446147.5109603,"logger":"tls.issuance.acme.acme_client","msg":"http request","method":"POST","url":"https://ldap.esco.ghaar:8443/acme/acme/challenge/wh2f7QuzIZpNXFA2kCrqKYb056g0vN3k/3N1mvLllsPKsRK18tkCK2f49aPBvAcRS","headers":{"Content-Type":["application/jose+json"],"User-Agent":["Caddy/2.8.4 CertMagic acmez (linux; amd64)"]},"response_headers":{"Cache-Control":["no-store"],"Content-Length":["322"],"Content-Type":["application/json"],"Date":["Wed, 26 Jun 2024 23:55:47 GMT"],"Link":["<https://ldap.esco.ghaar:8443/acme/acme/directory>;rel=\"index\"","<https://ldap.esco.ghaar:8443/acme/acme/authz/wh2f7QuzIZpNXFA2kCrqKYb056g0vN3k>;rel=\"up\""],"Location":["https://ldap.esco.ghaar:8443/acme/acme/challenge/wh2f7QuzIZpNXFA2kCrqKYb056g0vN3k/3N1mvLllsPKsRK18tkCK2f49aPBvAcRS"],"Replay-Nonce":["RWRJUE5tQlFnZ2FWRzNsZ3lDNmo4SUllVGdUVUlCTzg"]},"status_code":200}
Jun 26 16:55:47 rp-tailscale caddy[329621]: {"level":"debug","ts":1719446147.5111501,"logger":"tls.issuance.acme.acme_client","msg":"challenge accepted","identifier":"localhost","challenge_type":"http-01"}
Jun 26 16:55:47 rp-tailscale caddy[329621]: {"level":"debug","ts":1719446147.774031,"logger":"tls.issuance.acme.acme_client","msg":"http request","method":"POST","url":"https://ldap.esco.ghaar:8443/acme/acme/authz/wh2f7QuzIZpNXFA2kCrqKYb056g0vN3k","headers":{"Content-Type":["application/jose+json"],"User-Agent":["Caddy/2.8.4 CertMagic acmez (linux; amd64)"]},"response_headers":{"Cache-Control":["no-store"],"Content-Length":["867"],"Content-Type":["application/json"],"Date":["Wed, 26 Jun 2024 23:55:47 GMT"],"Link":["<https://ldap.esco.ghaar:8443/acme/acme/directory>;rel=\"index\""],"Location":["https://ldap.esco.ghaar:8443/acme/acme/authz/wh2f7QuzIZpNXFA2kCrqKYb056g0vN3k"],"Replay-Nonce":["ZXM5UmlhWGI5cFpxcmVlRzB5RG14emFpN0NUVTRJNk0"]},"status_code":200}
Jun 26 16:55:48 rp-tailscale caddy[329621]: {"level":"debug","ts":1719446148.037198,"logger":"tls.issuance.acme.acme_client","msg":"http request","method":"POST","url":"https://ldap.esco.ghaar:8443/acme/acme/authz/wh2f7QuzIZpNXFA2kCrqKYb056g0vN3k","headers":{"Content-Type":["application/jose+json"],"User-Agent":["Caddy/2.8.4 CertMagic acmez (linux; amd64)"]},"response_headers":{"Cache-Control":["no-store"],"Content-Length":["867"],"Content-Type":["application/json"],"Date":["Wed, 26 Jun 2024 23:55:48 GMT"],"Link":["<https://ldap.esco.ghaar:8443/acme/acme/directory>;rel=\"index\""],"Location":["https://ldap.esco.ghaar:8443/acme/acme/authz/wh2f7QuzIZpNXFA2kCrqKYb056g0vN3k"],"Replay-Nonce":["OEpqMlJjQ2dualFBaG5SOGV5VlJUaDY1cVF4NWRVYkY"]},"status_code":200}
Jun 26 16:55:48 rp-tailscale caddy[329621]: {"level":"debug","ts":1719446148.1533308,"logger":"crowdsec","msg":"processing 1521 deleted decisions","instance_id":"678af988"}
Jun 26 16:55:48 rp-tailscale caddy[329621]: {"level":"debug","ts":1719446148.154281,"logger":"crowdsec","msg":"skipped logging for 1521 deleted decisions","instance_id":"678af988"}
Jun 26 16:55:48 rp-tailscale caddy[329621]: {"level":"debug","ts":1719446148.1542964,"logger":"crowdsec","msg":"finished processing 1521 deleted decisions","instance_id":"678af988"}
Jun 26 16:55:48 rp-tailscale caddy[329621]: {"level":"debug","ts":1719446148.1543002,"logger":"crowdsec","msg":"processing 21658 new decisions","instance_id":"678af988"}
Jun 26 16:55:48 rp-tailscale caddy[329621]: {"level":"debug","ts":1719446148.2583196,"logger":"crowdsec","msg":"skipped logging for 21658 new decisions","instance_id":"678af988"}
Jun 26 16:55:48 rp-tailscale caddy[329621]: {"level":"debug","ts":1719446148.2583697,"logger":"crowdsec","msg":"finished processing 21658 new decisions","instance_id":"678af988"}
Jun 26 16:55:48 rp-tailscale caddy[329621]: {"level":"debug","ts":1719446148.3033783,"logger":"tls.issuance.acme.acme_client","msg":"http request","method":"POST","url":"https://ldap.esco.ghaar:8443/acme/acme/authz/wh2f7QuzIZpNXFA2kCrqKYb056g0vN3k","headers":{"Content-Type":["application/jose+json"],"User-Agent":["Caddy/2.8.4 CertMagic acmez (linux; amd64)"]},"response_headers":{"Cache-Control":["no-store"],"Content-Length":["867"],"Content-Type":["application/json"],"Date":["Wed, 26 Jun 2024 23:55:48 GMT"],"Link":["<https://ldap.esco.ghaar:8443/acme/acme/directory>;rel=\"index\""],"Location":["https://ldap.esco.ghaar:8443/acme/acme/authz/wh2f7QuzIZpNXFA2kCrqKYb056g0vN3k"],"Replay-Nonce":["WFpTNjB0blNaeHdyNHlKaEc4VFZZODhlSnQzOFZ6cUc"]},"status_code":200}
Jun 26 16:55:48 rp-tailscale caddy[329621]: {"level":"debug","ts":1719446148.5679607,"logger":"tls.issuance.acme.acme_client","msg":"http request","method":"POST","url":"https://ldap.esco.ghaar:8443/acme/acme/authz/wh2f7QuzIZpNXFA2kCrqKYb056g0vN3k","headers":{"Content-Type":["application/jose+json"],"User-Agent":["Caddy/2.8.4 CertMagic acmez (linux; amd64)"]},"response_headers":{"Cache-Control":["no-store"],"Content-Length":["867"],"Content-Type":["application/json"],"Date":["Wed, 26 Jun 2024 23:55:48 GMT"],"Link":["<https://ldap.esco.ghaar:8443/acme/acme/directory>;rel=\"index\""],"Location":["https://ldap.esco.ghaar:8443/acme/acme/authz/wh2f7QuzIZpNXFA2kCrqKYb056g0vN3k"],"Replay-Nonce":["R200YXZXMHVzT3E5eHBvaGZRalNyVEhNZG9aZkJXQ0k"]},"status_code":200}
Jun 26 16:55:48 rp-tailscale caddy[329621]: {"level":"debug","ts":1719446148.834724,"logger":"tls.issuance.acme.acme_client","msg":"http request","method":"POST","url":"https://ldap.esco.ghaar:8443/acme/acme/authz/wh2f7QuzIZpNXFA2kCrqKYb056g0vN3k","headers":{"Content-Type":["application/jose+json"],"User-Agent":["Caddy/2.8.4 CertMagic acmez (linux; amd64)"]},"response_headers":{"Cache-Control":["no-store"],"Content-Length":["867"],"Content-Type":["application/json"],"Date":["Wed, 26 Jun 2024 23:55:48 GMT"],"Link":["<https://ldap.esco.ghaar:8443/acme/acme/directory>;rel=\"index\""],"Location":["https://ldap.esco.ghaar:8443/acme/acme/authz/wh2f7QuzIZpNXFA2kCrqKYb056g0vN3k"],"Replay-Nonce":["NU5yOUNFQm9TWm1tU3Z1eHBvSEphRmJpbkF2Y25Ba1A"]},"status_code":200}
Jun 26 16:55:49 rp-tailscale caddy[329621]: {"level":"debug","ts":1719446149.0978398,"logger":"tls.issuance.acme.acme_client","msg":"http request","method":"POST","url":"https://ldap.esco.ghaar:8443/acme/acme/authz/wh2f7QuzIZpNXFA2kCrqKYb056g0vN3k","headers":{"Content-Type":["application/jose+json"],"User-Agent":["Caddy/2.8.4 CertMagic acmez (linux; amd64)"]},"response_headers":{"Cache-Control":["no-store"],"Content-Length":["867"],"Content-Type":["application/json"],"Date":["Wed, 26 Jun 2024 23:55:49 GMT"],"Link":["<https://ldap.esco.ghaar:8443/acme/acme/directory>;rel=\"index\""],"Location":["https://ldap.esco.ghaar:8443/acme/acme/authz/wh2f7QuzIZpNXFA2kCrqKYb056g0vN3k"],"Replay-Nonce":["SlNFaGRWeDhwRHBEUGpRcG5SRmJkeTlGTzlHeVd6aDg"]},"status_code":200}
Jun 26 16:55:49 rp-tailscale caddy[329621]: {"level":"debug","ts":1719446149.3623996,"logger":"tls.issuance.acme.acme_client","msg":"http request","method":"POST","url":"https://ldap.esco.ghaar:8443/acme/acme/authz/wh2f7QuzIZpNXFA2kCrqKYb056g0vN3k","headers":{"Content-Type":["application/jose+json"],"User-Agent":["Caddy/2.8.4 CertMagic acmez (linux; amd64)"]},"response_headers":{"Cache-Control":["no-store"],"Content-Length":["867"],"Content-Type":["application/json"],"Date":["Wed, 26 Jun 2024 23:55:49 GMT"],"Link":["<https://ldap.esco.ghaar:8443/acme/acme/directory>;rel=\"index\""],"Location":["https://ldap.esco.ghaar:8443/acme/acme/authz/wh2f7QuzIZpNXFA2kCrqKYb056g0vN3k"],"Replay-Nonce":["eGZDVlZuMVVUeDgxUHFFaHoyb2luSHY0RkxBZW9YWlI"]},"status_code":200}
Jun 26 16:55:49 rp-tailscale caddy[329621]: {"level":"debug","ts":1719446149.626394,"logger":"tls.issuance.acme.acme_client","msg":"http request","method":"POST","url":"https://ldap.esco.ghaar:8443/acme/acme/authz/wh2f7QuzIZpNXFA2kCrqKYb056g0vN3k","headers":{"Content-Type":["application/jose+json"],"User-Agent":["Caddy/2.8.4 CertMagic acmez (linux; amd64)"]},"response_headers":{"Cache-Control":["no-store"],"Content-Length":["867"],"Content-Type":["application/json"],"Date":["Wed, 26 Jun 2024 23:55:49 GMT"],"Link":["<https://ldap.esco.ghaar:8443/acme/acme/directory>;rel=\"index\""],"Location":["https://ldap.esco.ghaar:8443/acme/acme/authz/wh2f7QuzIZpNXFA2kCrqKYb056g0vN3k"],"Replay-Nonce":["M0dOOTJRbkpibkhTamlXd1FCbHo3TVN1WXlMMWNvRVk"]},"status_code":200}
Jun 26 16:55:49 rp-tailscale caddy[329621]: {"level":"debug","ts":1719446149.8935733,"logger":"tls.issuance.acme.acme_client","msg":"http request","method":"POST","url":"https://ldap.esco.ghaar:8443/acme/acme/authz/wh2f7QuzIZpNXFA2kCrqKYb056g0vN3k","headers":{"Content-Type":["application/jose+json"],"User-Agent":["Caddy/2.8.4 CertMagic acmez (linux; amd64)"]},"response_headers":{"Cache-Control":["no-store"],"Content-Length":["867"],"Content-Type":["application/json"],"Date":["Wed, 26 Jun 2024 23:55:49 GMT"],"Link":["<https://ldap.esco.ghaar:8443/acme/acme/directory>;rel=\"index\""],"Location":["https://ldap.esco.ghaar:8443/acme/acme/authz/wh2f7QuzIZpNXFA2kCrqKYb056g0vN3k"],"Replay-Nonce":["TTZsa0ZsZnBqcjAzNDJlRmdWTFRuOWp5d3IzaGVlZlE"]},"status_code":200}
Jun 26 16:55:50 rp-tailscale caddy[329621]: {"level":"debug","ts":1719446150.1572537,"logger":"tls.issuance.acme.acme_client","msg":"http request","method":"POST","url":"https://ldap.esco.ghaar:8443/acme/acme/authz/wh2f7QuzIZpNXFA2kCrqKYb056g0vN3k","headers":{"Content-Type":["application/jose+json"],"User-Agent":["Caddy/2.8.4 CertMagic acmez (linux; amd64)"]},"response_headers":{"Cache-Control":["no-store"],"Content-Length":["867"],"Content-Type":["application/json"],"Date":["Wed, 26 Jun 2024 23:55:50 GMT"],"Link":["<https://ldap.esco.ghaar:8443/acme/acme/directory>;rel=\"index\""],"Location":["https://ldap.esco.ghaar:8443/acme/acme/authz/wh2f7QuzIZpNXFA2kCrqKYb056g0vN3k"],"Replay-Nonce":["d2tKNlVWNlRNMTYyZnQ2a3hBVUZ4Q0dlMWgyeURpTG8"]},"status_code":200}
Jun 26 16:55:50 rp-tailscale caddy[329621]: {"level":"debug","ts":1719446150.420953,"logger":"tls.issuance.acme.acme_client","msg":"http request","method":"POST","url":"https://ldap.esco.ghaar:8443/acme/acme/authz/wh2f7QuzIZpNXFA2kCrqKYb056g0vN3k","headers":{"Content-Type":["application/jose+json"],"User-Agent":["Caddy/2.8.4 CertMagic acmez (linux; amd64)"]},"response_headers":{"Cache-Control":["no-store"],"Content-Length":["867"],"Content-Type":["application/json"],"Date":["Wed, 26 Jun 2024 23:55:50 GMT"],"Link":["<https://ldap.esco.ghaar:8443/acme/acme/directory>;rel=\"index\""],"Location":["https://ldap.esco.ghaar:8443/acme/acme/authz/wh2f7QuzIZpNXFA2kCrqKYb056g0vN3k"],"Replay-Nonce":["NU5MZk5tQk5MOTJMalRKOWg3bzBSVTRqRU83R3hiSXk"]},"status_code":200}
Jun 26 16:55:50 rp-tailscale caddy[329621]: {"level":"debug","ts":1719446150.6859617,"logger":"tls.issuance.acme.acme_client","msg":"http request","method":"POST","url":"https://ldap.esco.ghaar:8443/acme/acme/authz/wh2f7QuzIZpNXFA2kCrqKYb056g0vN3k","headers":{"Content-Type":["application/jose+json"],"User-Agent":["Caddy/2.8.4 CertMagic acmez (linux; amd64)"]},"response_headers":{"Cache-Control":["no-store"],"Content-Length":["867"],"Content-Type":["application/json"],"Date":["Wed, 26 Jun 2024 23:55:50 GMT"],"Link":["<https://ldap.esco.ghaar:8443/acme/acme/directory>;rel=\"index\""],"Location":["https://ldap.esco.ghaar:8443/acme/acme/authz/wh2f7QuzIZpNXFA2kCrqKYb056g0vN3k"],"Replay-Nonce":["REk2SlJiQ3RmcDYzdWY5eVQxNU93UnJIVEJZSHFOQTM"]},"status_code":200}
Jun 26 16:55:50 rp-tailscale caddy[329621]: {"level":"debug","ts":1719446150.9481037,"logger":"tls.issuance.acme.acme_client","msg":"http request","method":"POST","url":"https://ldap.esco.ghaar:8443/acme/acme/authz/wh2f7QuzIZpNXFA2kCrqKYb056g0vN3k","headers":{"Content-Type":["application/jose+json"],"User-Agent":["Caddy/2.8.4 CertMagic acmez (linux; amd64)"]},"response_headers":{"Cache-Control":["no-store"],"Content-Length":["867"],"Content-Type":["application/json"],"Date":["Wed, 26 Jun 2024 23:55:50 GMT"],"Link":["<https://ldap.esco.ghaar:8443/acme/acme/directory>;rel=\"index\""],"Location":["https://ldap.esco.ghaar:8443/acme/acme/authz/wh2f7QuzIZpNXFA2kCrqKYb056g0vN3k"],"Replay-Nonce":["ZEYwaVIwYllMUDQwOWE5bGRxWEJnUGU3ZHoyQ253QTU"]},"status_code":200}
Jun 26 16:55:51 rp-tailscale caddy[329621]: {"level":"debug","ts":1719446151.213773,"logger":"tls.issuance.acme.acme_client","msg":"http request","method":"POST","url":"https://ldap.esco.ghaar:8443/acme/acme/authz/wh2f7QuzIZpNXFA2kCrqKYb056g0vN3k","headers":{"Content-Type":["application/jose+json"],"User-Agent":["Caddy/2.8.4 CertMagic acmez (linux; amd64)"]},"response_headers":{"Cache-Control":["no-store"],"Content-Length":["867"],"Content-Type":["application/json"],"Date":["Wed, 26 Jun 2024 23:55:51 GMT"],"Link":["<https://ldap.esco.ghaar:8443/acme/acme/directory>;rel=\"index\""],"Location":["https://ldap.esco.ghaar:8443/acme/acme/authz/wh2f7QuzIZpNXFA2kCrqKYb056g0vN3k"],"Replay-Nonce":["U2RnNlQ1SjdJZlFLZ3FkZ092SVp6dHJha3J4NGM5SDk"]},"status_code":200}

You’re changing too much and deviating from the right path. You’re closer to the solution with the config in this comment than now

I assume you’ve used the cert.pem file with your upstream server that serves rp-tailscale.esco.ghaar:8443. Use chain.pem.

If you want Caddy to issue certificates to itself, then you’ll need to configure the pki app and/or use tls directive with the appropriate configuration which I won’t go into here.

Caddy can also act as CA because it embeds step-ca. You can configure that with the acme_server directive. That’s also a different path than the original question here.

To summarize, your upstream server should be using chain.pem, and you configure tls_trust_pool inside transport in reverse_proxy to trust the root certificate only.

1 Like