caddyserver / caddy

Fast and extensible multi-platform HTTP/1-2-3 web server with automatic HTTPS
https://caddyserver.com
Apache License 2.0
55.53k stars 3.91k forks source link

Caddy PKI without Root key #6290

Closed apollo13 closed 1 month ago

apollo13 commented 2 months ago

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?

mholt commented 2 months ago

If there is no root, how can there be an intermediate? :thinking: I guess I don't fully understand what you're asking.

apollo13 commented 2 months ago

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: @.***>

francislavoie commented 2 months ago

As a workaround, you could set your intermediate as the root and then set sign_with_root.

apollo13 commented 2 months ago

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: @.***>

francislavoie commented 2 months ago

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.

mholt commented 2 months ago

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.

apollo13 commented 2 months ago

This simple diff makes it work for me.

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
    }
}
mholt commented 2 months ago

@apollo13 I think that would be okay. Want to submit a PR for review?

apollo13 commented 2 months ago

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: @.***>

mholt commented 2 months ago

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

apollo13 commented 2 months ago

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.