Implement SubjectTransformer
This makes it possible to replace cert subjects with wildcards, for example Related: #280
This commit is contained in:
parent
52cbe735c6
commit
b29d2a03a0
@ -130,6 +130,7 @@ func expiresAt(cert *x509.Certificate) time.Time {
|
||||
//
|
||||
// This method is safe for concurrent use.
|
||||
func (cfg *Config) CacheManagedCertificate(ctx context.Context, domain string) (Certificate, error) {
|
||||
domain = cfg.transformSubject(ctx, nil, domain)
|
||||
cert, err := cfg.loadManagedCertificate(ctx, domain)
|
||||
if err != nil {
|
||||
return cert, err
|
||||
|
35
config.go
35
config.go
@ -136,9 +136,17 @@ type Config struct {
|
||||
// storage is properly configured and has sufficient
|
||||
// space, you can disable this check to reduce I/O
|
||||
// if that is expensive for you.
|
||||
// EXPERIMENTAL: Option subject to change or removal.
|
||||
// EXPERIMENTAL: Subject to change or removal.
|
||||
DisableStorageCheck bool
|
||||
|
||||
// SubjectTransformer is a hook that can transform the
|
||||
// subject (SAN) of a certificate being loaded or issued.
|
||||
// For example, a common use case is to replace the
|
||||
// left-most label with an asterisk (*) to become a
|
||||
// wildcard certificate.
|
||||
// EXPERIMENTAL: Subject to change or removal.
|
||||
SubjectTransformer func(ctx context.Context, domain string) string
|
||||
|
||||
// Set a logger to enable logging. If not set,
|
||||
// a default logger will be created.
|
||||
Logger *zap.Logger
|
||||
@ -485,6 +493,10 @@ func (cfg *Config) obtainCert(ctx context.Context, name string, interactive bool
|
||||
return fmt.Errorf("no issuers configured; impossible to obtain or check for existing certificate in storage")
|
||||
}
|
||||
|
||||
log := cfg.Logger.Named("obtain")
|
||||
|
||||
name = cfg.transformSubject(ctx, log, name)
|
||||
|
||||
// if storage has all resources for this certificate, obtain is a no-op
|
||||
if cfg.storageHasCertResourcesAnyIssuer(ctx, name) {
|
||||
return nil
|
||||
@ -497,8 +509,6 @@ func (cfg *Config) obtainCert(ctx context.Context, name string, interactive bool
|
||||
return fmt.Errorf("failed storage check: %v - storage is probably misconfigured", err)
|
||||
}
|
||||
|
||||
log := cfg.Logger.Named("obtain")
|
||||
|
||||
log.Info("acquiring lock", zap.String("identifier", name))
|
||||
|
||||
// ensure idempotency of the obtain operation for this name
|
||||
@ -736,6 +746,10 @@ func (cfg *Config) renewCert(ctx context.Context, name string, force, interactiv
|
||||
return fmt.Errorf("no issuers configured; impossible to renew or check existing certificate in storage")
|
||||
}
|
||||
|
||||
log := cfg.Logger.Named("renew")
|
||||
|
||||
name = cfg.transformSubject(ctx, log, name)
|
||||
|
||||
// ensure storage is writeable and readable
|
||||
// TODO: this is not necessary every time; should only perform check once every so often for each storage, which may require some global state...
|
||||
err := cfg.checkStorage(ctx)
|
||||
@ -743,8 +757,6 @@ func (cfg *Config) renewCert(ctx context.Context, name string, force, interactiv
|
||||
return fmt.Errorf("failed storage check: %v - storage is probably misconfigured", err)
|
||||
}
|
||||
|
||||
log := cfg.Logger.Named("renew")
|
||||
|
||||
log.Info("acquiring lock", zap.String("identifier", name))
|
||||
|
||||
// ensure idempotency of the renew operation for this name
|
||||
@ -1083,6 +1095,19 @@ func (cfg *Config) getChallengeInfo(ctx context.Context, identifier string) (Cha
|
||||
return Challenge{Challenge: chalInfo}, true, nil
|
||||
}
|
||||
|
||||
func (cfg *Config) transformSubject(ctx context.Context, logger *zap.Logger, name string) string {
|
||||
if cfg.SubjectTransformer == nil {
|
||||
return name
|
||||
}
|
||||
transformedName := cfg.SubjectTransformer(ctx, name)
|
||||
if logger != nil && transformedName != name {
|
||||
logger.Debug("transformed subject name",
|
||||
zap.String("original", name),
|
||||
zap.String("transformed", transformedName))
|
||||
}
|
||||
return transformedName
|
||||
}
|
||||
|
||||
// checkStorage tests the storage by writing random bytes
|
||||
// to a random key, and then loading those bytes and
|
||||
// comparing the loaded value. If this fails, the provided
|
||||
|
Loading…
Reference in New Issue
Block a user