Repository: caddyserver/caddy (language: go)

Issue:
Caddy PKI without Root key
Hi, according to the docs the caddy pki can be configured like this:
```
{
	pki {
		ca local {
			root {
				format pem_file
				cert /path/to/root.pem
				key /path/to/root.key
			}
			intermediate {
				format pem_file
				cert /path/to/intermediate.pem
				key /path/to/intermediate.key
			}
		}
	}
}
```
if I am solely using the intermediate to issue certificates, then there is no reason to supply the root key. Would it be possible to allow configuration of caddy without a root key?


Discussion / hints:
If there is no root, how can there be an intermediate? :thinking: I guess I don't fully understand what you're asking.
The root exists but could be a public CA that issues me an intermediate certificate to use or the root exists in a HSM and as such the key is not exportable. So I can supply the cert which is public information but I cannot access the root private key (or simply don't want to give the caddy server access to it). Does that make sense?

On Thu, May 2, 2024, at 16:49, Matt Holt wrote:
> If there is no root, how can there be an intermediate? 🤔 I guess I 
> don't fully understand what you're asking.
>
> —
> Reply to this email directly, view it on GitHub 
> <https://github.com/caddyserver/caddy/issues/6290#issuecomment-2090706301>, 
> or unsubscribe 
> <https://github.com/notifications/unsubscribe-auth/AAAT5C6FVCKXILHJJOP7D3TZAJG7NAVCNFSM6AAAAABHDVM2TOVHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHMZDAOJQG4YDMMZQGE>.
> You are receiving this because you authored the thread.Message ID: 
> ***@***.***>

As a workaround, you could set your `intermediate` as the `root` and then set `sign_with_root`. 
Hm have to check if that then return the correct chain with acme_server

On Thu, May 2, 2024, at 17:13, Francis Lavoie wrote:
> As a workaround, you could set your `intermediate` as the `root` and 
> then set `sign_with_root`.
>
> —
> Reply to this email directly, view it on GitHub 
> <https://github.com/caddyserver/caddy/issues/6290#issuecomment-2090778691>, 
> or unsubscribe 
> <https://github.com/notifications/unsubscribe-auth/AAAT5C74DRWLGUHNRMIG7Q3ZAJJYVAVCNFSM6AAAAABHDVM2TOVHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHMZDAOJQG43TQNRZGE>.
> You are receiving this because you authored the thread.Message ID: 
> ***@***.***>

Oh, yeah I think it wouldn't.

Also I just realized we never wired up `sign_with_root` via Caddyfile for the `acme_server` handler, it's only available via JSON config.
Ok, gotcha, so the root exists, you're talking about just not loading it into the config.

We could probably work on making that possible.
This simple diff makes it work for me.
```diff
diff --git a/modules/caddypki/crypto.go b/modules/caddypki/crypto.go
index 386ce629..24c04686 100644
--- a/modules/caddypki/crypto.go
+++ b/modules/caddypki/crypto.go
@@ -78,18 +78,21 @@ func (kp KeyPair) Load() (*x509.Certificate, crypto.Signer, error) {
                if err != nil {
                        return nil, nil, err
                }
-               keyData, err := os.ReadFile(kp.PrivateKey)
-               if err != nil {
-                       return nil, nil, err
-               }
-
                cert, err := pemDecodeSingleCert(certData)
                if err != nil {
                        return nil, nil, err
                }
-               key, err := certmagic.PEMDecodePrivateKey(keyData)
-               if err != nil {
-                       return nil, nil, err
+
+               var key crypto.Signer = nil
+               if kp.PrivateKey != "" {
+                       keyData, err := os.ReadFile(kp.PrivateKey)
+                       if err != nil {
+                               return nil, nil, err
+                       }
+                       key, err = certmagic.PEMDecodePrivateKey(keyData)
+                       if err != nil {
+                               return nil, nil, err
+                       }
                }
 
                return cert, key, nil
```

Now I was able to configure the acme server like this:
```
{
        http_port 8080
        https_port 8443

        skip_install_trust
	pki {
		ca caddy {
			name caddy
		}
		ca local {
			root {
				format pem_file
				cert /home/florian/sources/caddy/certs/ca.pem
			}
			intermediate {
				format pem_file
				cert /home/florian/sources/caddy/certs/intermediate.crt
				key /home/florian/sources/caddy/certs/intermediate.pem
			}
		}
	}
}

localhost {
	tls {
		issuer internal {
			ca caddy
		}
	}
	acme_server {
		ca local
	}
}
```
@apollo13 I think that would be okay. Want to submit a PR for review?
Will do, not sure if I will manage writing the tests. This is the first time I am doing something with caddy at all -- might need some help there then. Out of curiosity, is the ca stuff pluggable? Ie could I write a plugin that would call out to another CA to issue the certs instead of providing caddy with the private keys?

On Fri, May 3, 2024, at 17:32, Matt Holt wrote:
> @apollo13 <https://github.com/apollo13> I think that would be okay. 
> Want to submit a PR for review?
>
> —
> Reply to this email directly, view it on GitHub 
> <https://github.com/caddyserver/caddy/issues/6290#issuecomment-2093250033>, 
> or unsubscribe 
> <https://github.com/notifications/unsubscribe-auth/AAAT5CY764MW6IIFJOO6RODZAOUXDAVCNFSM6AAAAABHDVM2TOVHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHMZDAOJTGI2TAMBTGM>.
> You are receiving this because you were mentioned.Message ID: 
> ***@***.***>

Sure, we can help out. (Might just take us some time, heh, quite busy!)

> could I write a plugin that would call out to another CA to issue the certs instead of providing caddy with the private keys?

The `pki` app's job is literally to generate signing keys and issue certs. If your sites need certs from another CA, you can configure cert issuers: https://caddyserver.com/docs/caddyfile/options#cert-issuer
> Sure, we can help out. (Might just take us some time, heh, quite busy!)

No worries.

> If your sites need certs from another CA, you can configure cert issuers: https://caddyserver.com/docs/caddyfile/options#cert-issuer

But those cannot be used from `acme_server`. Think of it like having your CA on a yubikey and then issuing certs via `acme_server` for the other caddy instances.

Fix patch:
diff --git a/modules/caddypki/crypto.go b/modules/caddypki/crypto.go
index 386ce629274..324a4fcfafb 100644
--- a/modules/caddypki/crypto.go
+++ b/modules/caddypki/crypto.go
@@ -78,18 +78,21 @@ func (kp KeyPair) Load() (*x509.Certificate, crypto.Signer, error) {
 		if err != nil {
 			return nil, nil, err
 		}
-		keyData, err := os.ReadFile(kp.PrivateKey)
-		if err != nil {
-			return nil, nil, err
-		}
-
 		cert, err := pemDecodeSingleCert(certData)
 		if err != nil {
 			return nil, nil, err
 		}
-		key, err := certmagic.PEMDecodePrivateKey(keyData)
-		if err != nil {
-			return nil, nil, err
+
+		var key crypto.Signer
+		if kp.PrivateKey != "" {
+			keyData, err := os.ReadFile(kp.PrivateKey)
+			if err != nil {
+				return nil, nil, err
+			}
+			key, err = certmagic.PEMDecodePrivateKey(keyData)
+			if err != nil {
+				return nil, nil, err
+			}
 		}
 
 		return cert, key, nil


Test patch: