Skip to content

Commit 14b882b

Browse files
authored
Merge branch 'master' into log-wildcard-fix
2 parents e1cf5da + 7ebe72b commit 14b882b

File tree

9 files changed

+700
-48
lines changed

9 files changed

+700
-48
lines changed

go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ require (
3535
go.opentelemetry.io/contrib/propagators/autoprop v0.63.0
3636
go.opentelemetry.io/otel v1.38.0
3737
go.opentelemetry.io/otel/sdk v1.38.0
38+
go.step.sm/crypto v0.74.0
3839
go.uber.org/automaxprocs v1.6.0
3940
go.uber.org/zap v1.27.1
4041
go.uber.org/zap/exp v0.3.0
@@ -166,7 +167,6 @@ require (
166167
go.opentelemetry.io/otel/metric v1.38.0 // indirect
167168
go.opentelemetry.io/otel/trace v1.38.0
168169
go.opentelemetry.io/proto/otlp v1.7.1 // indirect
169-
go.step.sm/crypto v0.74.0
170170
go.uber.org/multierr v1.11.0 // indirect
171171
golang.org/x/mod v0.29.0 // indirect
172172
golang.org/x/sys v0.38.0

modules/caddypki/adminapi.go

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -222,11 +222,16 @@ func rootAndIntermediatePEM(ca *CA) (root, inter []byte, err error) {
222222
if err != nil {
223223
return root, inter, err
224224
}
225-
inter, err = pemEncodeCert(ca.IntermediateCertificate().Raw)
226-
if err != nil {
227-
return root, inter, err
225+
226+
for _, interCert := range ca.IntermediateCertificateChain() {
227+
pemBytes, err := pemEncodeCert(interCert.Raw)
228+
if err != nil {
229+
return nil, nil, err
230+
}
231+
inter = append(inter, pemBytes...)
228232
}
229-
return root, inter, err
233+
234+
return
230235
}
231236

232237
// caInfo is the response structure for the CA info API endpoint.

modules/caddypki/ca.go

Lines changed: 43 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -75,10 +75,11 @@ type CA struct {
7575
// and module provisioning.
7676
ID string `json:"-"`
7777

78-
storage certmagic.Storage
79-
root, inter *x509.Certificate
80-
interKey any // TODO: should we just store these as crypto.Signer?
81-
mu *sync.RWMutex
78+
storage certmagic.Storage
79+
root *x509.Certificate
80+
interChain []*x509.Certificate
81+
interKey crypto.Signer
82+
mu *sync.RWMutex
8283

8384
rootCertPath string // mainly used for logging purposes if trusting
8485
log *zap.Logger
@@ -127,36 +128,40 @@ func (ca *CA) Provision(ctx caddy.Context, id string, log *zap.Logger) error {
127128
}
128129

129130
// load the certs and key that will be used for signing
130-
var rootCert, interCert *x509.Certificate
131+
var rootCert *x509.Certificate
132+
var rootCertChain, interCertChain []*x509.Certificate
131133
var rootKey, interKey crypto.Signer
132134
var err error
133135
if ca.Root != nil {
134136
if ca.Root.Format == "" || ca.Root.Format == "pem_file" {
135137
ca.rootCertPath = ca.Root.Certificate
136138
}
137-
rootCert, rootKey, err = ca.Root.Load()
139+
rootCertChain, rootKey, err = ca.Root.Load()
140+
rootCert = rootCertChain[0]
138141
} else {
139142
ca.rootCertPath = "storage:" + ca.storageKeyRootCert()
140143
rootCert, rootKey, err = ca.loadOrGenRoot()
141144
}
142145
if err != nil {
143146
return err
144147
}
145-
actualRootLifetime := time.Until(rootCert.NotAfter)
146-
if time.Duration(ca.IntermediateLifetime) >= actualRootLifetime {
147-
return fmt.Errorf("intermediate certificate lifetime must be less than actual root certificate lifetime (%s)", actualRootLifetime)
148-
}
148+
149149
if ca.Intermediate != nil {
150-
interCert, interKey, err = ca.Intermediate.Load()
150+
interCertChain, interKey, err = ca.Intermediate.Load()
151151
} else {
152-
interCert, interKey, err = ca.loadOrGenIntermediate(rootCert, rootKey)
152+
actualRootLifetime := time.Until(rootCert.NotAfter)
153+
if time.Duration(ca.IntermediateLifetime) >= actualRootLifetime {
154+
return fmt.Errorf("intermediate certificate lifetime must be less than actual root certificate lifetime (%s)", actualRootLifetime)
155+
}
156+
157+
interCertChain, interKey, err = ca.loadOrGenIntermediate(rootCert, rootKey)
153158
}
154159
if err != nil {
155160
return err
156161
}
157162

158163
ca.mu.Lock()
159-
ca.root, ca.inter, ca.interKey = rootCert, interCert, interKey
164+
ca.root, ca.interChain, ca.interKey = rootCert, interCertChain, interKey
160165
ca.mu.Unlock()
161166

162167
return nil
@@ -172,21 +177,21 @@ func (ca CA) RootCertificate() *x509.Certificate {
172177
// RootKey returns the CA's root private key. Since the root key is
173178
// not cached in memory long-term, it needs to be loaded from storage,
174179
// which could yield an error.
175-
func (ca CA) RootKey() (any, error) {
180+
func (ca CA) RootKey() (crypto.Signer, error) {
176181
_, rootKey, err := ca.loadOrGenRoot()
177182
return rootKey, err
178183
}
179184

180-
// IntermediateCertificate returns the CA's intermediate
181-
// certificate (public key).
182-
func (ca CA) IntermediateCertificate() *x509.Certificate {
185+
// IntermediateCertificateChain returns the CA's intermediate
186+
// certificate chain.
187+
func (ca CA) IntermediateCertificateChain() []*x509.Certificate {
183188
ca.mu.RLock()
184189
defer ca.mu.RUnlock()
185-
return ca.inter
190+
return ca.interChain
186191
}
187192

188193
// IntermediateKey returns the CA's intermediate private key.
189-
func (ca CA) IntermediateKey() any {
194+
func (ca CA) IntermediateKey() crypto.Signer {
190195
ca.mu.RLock()
191196
defer ca.mu.RUnlock()
192197
return ca.interKey
@@ -207,26 +212,27 @@ func (ca *CA) NewAuthority(authorityConfig AuthorityConfig) (*authority.Authorit
207212
// cert/key directly, since it's unlikely to expire
208213
// while Caddy is running (long lifetime)
209214
var issuerCert *x509.Certificate
210-
var issuerKey any
215+
var issuerKey crypto.Signer
211216
issuerCert = rootCert
212217
var err error
213218
issuerKey, err = ca.RootKey()
214219
if err != nil {
215220
return nil, fmt.Errorf("loading signing key: %v", err)
216221
}
217-
signerOption = authority.WithX509Signer(issuerCert, issuerKey.(crypto.Signer))
222+
signerOption = authority.WithX509Signer(issuerCert, issuerKey)
218223
} else {
219224
// if we're signing with intermediate, we need to make
220225
// sure it's always fresh, because the intermediate may
221226
// renew while Caddy is running (medium lifetime)
222227
signerOption = authority.WithX509SignerFunc(func() ([]*x509.Certificate, crypto.Signer, error) {
223-
issuerCert := ca.IntermediateCertificate()
224-
issuerKey := ca.IntermediateKey().(crypto.Signer)
228+
issuerChain := ca.IntermediateCertificateChain()
229+
issuerCert := issuerChain[0]
230+
issuerKey := ca.IntermediateKey()
225231
ca.log.Debug("using intermediate signer",
226232
zap.String("serial", issuerCert.SerialNumber.String()),
227233
zap.String("not_before", issuerCert.NotBefore.String()),
228234
zap.String("not_after", issuerCert.NotAfter.String()))
229-
return []*x509.Certificate{issuerCert}, issuerKey, nil
235+
return issuerChain, issuerKey, nil
230236
})
231237
}
232238

@@ -252,7 +258,11 @@ func (ca *CA) NewAuthority(authorityConfig AuthorityConfig) (*authority.Authorit
252258

253259
func (ca CA) loadOrGenRoot() (rootCert *x509.Certificate, rootKey crypto.Signer, err error) {
254260
if ca.Root != nil {
255-
return ca.Root.Load()
261+
rootChain, rootSigner, err := ca.Root.Load()
262+
if err != nil {
263+
return nil, nil, err
264+
}
265+
return rootChain[0], rootSigner, nil
256266
}
257267
rootCertPEM, err := ca.storage.Load(ca.ctx, ca.storageKeyRootCert())
258268
if err != nil {
@@ -268,7 +278,7 @@ func (ca CA) loadOrGenRoot() (rootCert *x509.Certificate, rootKey crypto.Signer,
268278
}
269279

270280
if rootCert == nil {
271-
rootCert, err = pemDecodeSingleCert(rootCertPEM)
281+
rootCert, err = pemDecodeCertificate(rootCertPEM)
272282
if err != nil {
273283
return nil, nil, fmt.Errorf("parsing root certificate PEM: %v", err)
274284
}
@@ -314,7 +324,8 @@ func (ca CA) genRoot() (rootCert *x509.Certificate, rootKey crypto.Signer, err e
314324
return rootCert, rootKey, nil
315325
}
316326

317-
func (ca CA) loadOrGenIntermediate(rootCert *x509.Certificate, rootKey crypto.Signer) (interCert *x509.Certificate, interKey crypto.Signer, err error) {
327+
func (ca CA) loadOrGenIntermediate(rootCert *x509.Certificate, rootKey crypto.Signer) (interCertChain []*x509.Certificate, interKey crypto.Signer, err error) {
328+
var interCert *x509.Certificate
318329
interCertPEM, err := ca.storage.Load(ca.ctx, ca.storageKeyIntermediateCert())
319330
if err != nil {
320331
if !errors.Is(err, fs.ErrNotExist) {
@@ -326,10 +337,12 @@ func (ca CA) loadOrGenIntermediate(rootCert *x509.Certificate, rootKey crypto.Si
326337
if err != nil {
327338
return nil, nil, fmt.Errorf("generating new intermediate cert: %v", err)
328339
}
340+
341+
interCertChain = append(interCertChain, interCert)
329342
}
330343

331-
if interCert == nil {
332-
interCert, err = pemDecodeSingleCert(interCertPEM)
344+
if len(interCertChain) == 0 {
345+
interCertChain, err = pemDecodeCertificateChain(interCertPEM)
333346
if err != nil {
334347
return nil, nil, fmt.Errorf("decoding intermediate certificate PEM: %v", err)
335348
}
@@ -346,7 +359,7 @@ func (ca CA) loadOrGenIntermediate(rootCert *x509.Certificate, rootKey crypto.Si
346359
}
347360
}
348361

349-
return interCert, interKey, nil
362+
return interCertChain, interKey, nil
350363
}
351364

352365
func (ca CA) genIntermediate(rootCert *x509.Certificate, rootKey crypto.Signer) (interCert *x509.Certificate, interKey crypto.Signer, err error) {

modules/caddypki/crypto.go

Lines changed: 60 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,15 +17,20 @@ package caddypki
1717
import (
1818
"bytes"
1919
"crypto"
20+
"crypto/ecdsa"
21+
"crypto/ed25519"
22+
"crypto/rsa"
2023
"crypto/x509"
2124
"encoding/pem"
25+
"errors"
2226
"fmt"
2327
"os"
2428

2529
"github.com/caddyserver/certmagic"
30+
"go.step.sm/crypto/pemutil"
2631
)
2732

28-
func pemDecodeSingleCert(pemDER []byte) (*x509.Certificate, error) {
33+
func pemDecodeCertificate(pemDER []byte) (*x509.Certificate, error) {
2934
pemBlock, remaining := pem.Decode(pemDER)
3035
if pemBlock == nil {
3136
return nil, fmt.Errorf("no PEM block found")
@@ -39,6 +44,15 @@ func pemDecodeSingleCert(pemDER []byte) (*x509.Certificate, error) {
3944
return x509.ParseCertificate(pemBlock.Bytes)
4045
}
4146

47+
func pemDecodeCertificateChain(pemDER []byte) ([]*x509.Certificate, error) {
48+
chain, err := pemutil.ParseCertificateBundle(pemDER)
49+
if err != nil {
50+
return nil, fmt.Errorf("failed parsing certificate chain: %w", err)
51+
}
52+
53+
return chain, nil
54+
}
55+
4256
func pemEncodeCert(der []byte) ([]byte, error) {
4357
return pemEncode("CERTIFICATE", der)
4458
}
@@ -70,15 +84,18 @@ type KeyPair struct {
7084
Format string `json:"format,omitempty"`
7185
}
7286

73-
// Load loads the certificate and key.
74-
func (kp KeyPair) Load() (*x509.Certificate, crypto.Signer, error) {
87+
// Load loads the certificate chain and (optional) private key from
88+
// the corresponding files, using the configured format. If a
89+
// private key is read, it will be verified to belong to the first
90+
// certificate in the chain.
91+
func (kp KeyPair) Load() ([]*x509.Certificate, crypto.Signer, error) {
7592
switch kp.Format {
7693
case "", "pem_file":
7794
certData, err := os.ReadFile(kp.Certificate)
7895
if err != nil {
7996
return nil, nil, err
8097
}
81-
cert, err := pemDecodeSingleCert(certData)
98+
chain, err := pemDecodeCertificateChain(certData)
8299
if err != nil {
83100
return nil, nil, err
84101
}
@@ -93,11 +110,49 @@ func (kp KeyPair) Load() (*x509.Certificate, crypto.Signer, error) {
93110
if err != nil {
94111
return nil, nil, err
95112
}
113+
if err := verifyKeysMatch(chain[0], key); err != nil {
114+
return nil, nil, err
115+
}
96116
}
97117

98-
return cert, key, nil
118+
return chain, key, nil
99119

100120
default:
101121
return nil, nil, fmt.Errorf("unsupported format: %s", kp.Format)
102122
}
103123
}
124+
125+
// verifyKeysMatch verifies that the public key in the [x509.Certificate] matches
126+
// the public key of the [crypto.Signer].
127+
func verifyKeysMatch(crt *x509.Certificate, signer crypto.Signer) error {
128+
switch pub := crt.PublicKey.(type) {
129+
case *rsa.PublicKey:
130+
pk, ok := signer.Public().(*rsa.PublicKey)
131+
if !ok {
132+
return fmt.Errorf("private key type %T does not match issuer public key type %T", signer.Public(), pub)
133+
}
134+
if !pub.Equal(pk) {
135+
return errors.New("private key does not match issuer public key")
136+
}
137+
case *ecdsa.PublicKey:
138+
pk, ok := signer.Public().(*ecdsa.PublicKey)
139+
if !ok {
140+
return fmt.Errorf("private key type %T does not match issuer public key type %T", signer.Public(), pub)
141+
}
142+
if !pub.Equal(pk) {
143+
return errors.New("private key does not match issuer public key")
144+
}
145+
case ed25519.PublicKey:
146+
pk, ok := signer.Public().(ed25519.PublicKey)
147+
if !ok {
148+
return fmt.Errorf("private key type %T does not match issuer public key type %T", signer.Public(), pub)
149+
}
150+
if !pub.Equal(pk) {
151+
return errors.New("private key does not match issuer public key")
152+
}
153+
default:
154+
return fmt.Errorf("unsupported key type: %T", pub)
155+
}
156+
157+
return nil
158+
}

0 commit comments

Comments
 (0)