Skip to content

Commit 0a86fe9

Browse files
authored
Merge pull request #253 from ArkaSaha30/fix-cert-duration-check
Handle invalid ValidityDuration user input
2 parents f018f64 + f7a0e41 commit 0a86fe9

File tree

5 files changed

+75
-20
lines changed

5 files changed

+75
-20
lines changed

internal/controller/utils.go

Lines changed: 48 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -566,11 +566,27 @@ func getPeerCertName(etcdClusterName string) string {
566566
return peerCertName
567567
}
568568

569-
func createCMCertificateConfig(ec *ecv1alpha1.EtcdCluster) *certInterface.Config {
569+
// parseValidityDuration parses a duration string and returns the parsed duration.
570+
// If the customizedDuration is empty, it returns the defaultDuration.
571+
// Returns an error if the duration string cannot be parsed.
572+
func parseValidityDuration(customizedDuration string, defaultDuration time.Duration) (time.Duration, error) {
573+
if customizedDuration == "" {
574+
return defaultDuration, nil
575+
}
576+
duration, err := time.ParseDuration(customizedDuration)
577+
if err != nil {
578+
return 0, fmt.Errorf("failed to parse ValidityDuration: %w", err)
579+
}
580+
return duration, nil
581+
}
582+
583+
func createCMCertificateConfig(ec *ecv1alpha1.EtcdCluster) (*certInterface.Config, error) {
570584
cmConfig := ec.Spec.TLS.ProviderCfg.CertManagerCfg
571-
duration, err := time.ParseDuration(cmConfig.ValidityDuration)
585+
586+
// Set default duration to 90 days for cert-manager if not provided
587+
duration, err := parseValidityDuration(cmConfig.ValidityDuration, certInterface.DefaultCertManagerValidity)
572588
if err != nil {
573-
log.Printf("Failed to parse ValidityDuration: %s", err)
589+
return nil, err
574590
}
575591

576592
var getAltNames certInterface.AltNames
@@ -580,7 +596,12 @@ func createCMCertificateConfig(ec *ecv1alpha1.EtcdCluster) *certInterface.Config
580596
IPs: make([]net.IP, len(cmConfig.AltNames.DNSNames)),
581597
}
582598
} else {
583-
defaultDNSNames := []string{fmt.Sprintf("%s.svc.cluster.local", cmConfig.CommonName)}
599+
// Use wildcard DNS for the cluster's headless service to cover all pods
600+
// This allows the certificate to work for pod-0, pod-1, etc.
601+
defaultDNSNames := []string{
602+
fmt.Sprintf("*.%s.%s.svc.cluster.local", ec.Name, ec.Namespace),
603+
fmt.Sprintf("%s.%s.svc.cluster.local", ec.Name, ec.Namespace),
604+
}
584605
getAltNames = certInterface.AltNames{
585606
DNSNames: defaultDNSNames,
586607
}
@@ -596,14 +617,16 @@ func createCMCertificateConfig(ec *ecv1alpha1.EtcdCluster) *certInterface.Config
596617
"issuerKind": cmConfig.IssuerKind,
597618
},
598619
}
599-
return config
620+
return config, nil
600621
}
601622

602-
func createAutoCertificateConfig(ec *ecv1alpha1.EtcdCluster) *certInterface.Config {
623+
func createAutoCertificateConfig(ec *ecv1alpha1.EtcdCluster) (*certInterface.Config, error) {
603624
autoConfig := ec.Spec.TLS.ProviderCfg.AutoCfg
604-
duration, err := time.ParseDuration(autoConfig.ValidityDuration)
625+
626+
// Set default duration to 365 days for auto provider if not provided
627+
duration, err := parseValidityDuration(autoConfig.ValidityDuration, certInterface.DefaultAutoValidity)
605628
if err != nil {
606-
log.Printf("Failed to parse ValidityDuration: %s", err)
629+
return nil, err
607630
}
608631

609632
var altNames certInterface.AltNames
@@ -613,7 +636,12 @@ func createAutoCertificateConfig(ec *ecv1alpha1.EtcdCluster) *certInterface.Conf
613636
IPs: make([]net.IP, len(autoConfig.AltNames.DNSNames)),
614637
}
615638
} else {
616-
defaultDNSNames := []string{fmt.Sprintf("%s.svc.cluster.local", autoConfig.CommonName)}
639+
// Use wildcard DNS for the cluster's headless service to cover all pods
640+
// This allows the certificate to work for pod-0, pod-1, etc.
641+
defaultDNSNames := []string{
642+
fmt.Sprintf("*.%s.%s.svc.cluster.local", ec.Name, ec.Namespace),
643+
fmt.Sprintf("%s.%s.svc.cluster.local", ec.Name, ec.Namespace),
644+
}
617645
altNames = certInterface.AltNames{
618646
DNSNames: defaultDNSNames,
619647
}
@@ -625,7 +653,7 @@ func createAutoCertificateConfig(ec *ecv1alpha1.EtcdCluster) *certInterface.Conf
625653
ValidityDuration: duration,
626654
AltNames: altNames,
627655
}
628-
return config
656+
return config, nil
629657
}
630658

631659
func createCertificate(ec *ecv1alpha1.EtcdCluster, ctx context.Context, c client.Client, certName string) error {
@@ -647,17 +675,23 @@ func createCertificate(ec *ecv1alpha1.EtcdCluster, ctx context.Context, c client
647675
secretKey := client.ObjectKey{Name: certName, Namespace: ec.Namespace}
648676
switch {
649677
case ec.Spec.TLS.ProviderCfg.AutoCfg != nil:
650-
autoConfig := createAutoCertificateConfig(ec)
678+
autoConfig, err := createAutoCertificateConfig(ec)
679+
if err != nil {
680+
return fmt.Errorf("error creating auto certificate config: %w", err)
681+
}
651682
createCertErr := cert.EnsureCertificateSecret(ctx, secretKey, autoConfig)
652683
if createCertErr != nil {
653-
log.Printf("Error creating certificate: %s", createCertErr)
684+
return fmt.Errorf("error creating auto certificate: %w", createCertErr)
654685
}
655686
return nil
656687
case ec.Spec.TLS.ProviderCfg.CertManagerCfg != nil:
657-
cmConfig := createCMCertificateConfig(ec)
688+
cmConfig, err := createCMCertificateConfig(ec)
689+
if err != nil {
690+
return fmt.Errorf("error creating cert-manager certificate config: %w", err)
691+
}
658692
createCertErr := cert.EnsureCertificateSecret(ctx, secretKey, cmConfig)
659693
if createCertErr != nil {
660-
log.Printf("Error creating certificate: %s", createCertErr)
694+
return fmt.Errorf("error creating cert-manager certificate: %w", createCertErr)
661695
}
662696
return nil
663697
default:

pkg/certificate/auto/provider.go

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,9 @@ import (
2828
)
2929

3030
const (
31-
DefaultValidity = 365 * 24 * time.Hour
31+
// oneYearInHours is used to convert validity duration to years for transport.SelfCert.
32+
// transport.SelfCert expects the validity parameter to be in years (uint).
33+
oneYearInHours = 365 * 24 * time.Hour
3234
)
3335

3436
type Provider struct {
@@ -218,9 +220,14 @@ func checkKeyPair(cert *x509.Certificate, privateKey crypto.PrivateKey) error {
218220
// https://github.com/etcd-io/etcd/blob/b87bc1c3a275d7d4904f4d201b963a2de2264f0d/client/pkg/transport/listener.go#L275
219221
func (ac *Provider) createNewSecret(ctx context.Context, secretKey client.ObjectKey,
220222
cfg *interfaces.Config) error {
221-
validity := DefaultValidity
223+
validity := interfaces.DefaultAutoValidity
222224
if cfg.ValidityDuration != 0 {
223-
validity = cfg.ValidityDuration * time.Hour
225+
validity = cfg.ValidityDuration
226+
}
227+
228+
// Validate validity duration: minimum is 1 year as required by etcd
229+
if validity < oneYearInHours {
230+
return fmt.Errorf("validity duration must be at least 1 year (365 days), got: %v", validity)
224231
}
225232

226233
tmpDir, err := os.MkdirTemp("", fmt.Sprintf("etcd-auto-cert-%s-*", secretKey.Name))
@@ -252,7 +259,15 @@ func (ac *Provider) createNewSecret(ctx context.Context, secretKey client.Object
252259

253260
log.Printf("calling SelfCert with hosts: %v", hosts)
254261

255-
tlsInfo, selfCertErr := transport.SelfCert(zap.NewNop(), tmpDir, hosts, uint(validity/DefaultValidity))
262+
// Convert validity duration to years for transport.SelfCert
263+
// transport.SelfCert expects the validity parameter to be in years (uint)
264+
validityYears := uint(validity / oneYearInHours)
265+
if validityYears == 0 {
266+
// This should not happen due to the earlier check, but adding as a safeguard
267+
return fmt.Errorf("validity duration converts to 0 years, must be at least 1 year")
268+
}
269+
270+
tlsInfo, selfCertErr := transport.SelfCert(zap.NewNop(), tmpDir, hosts, validityYears)
256271
if selfCertErr != nil {
257272
return fmt.Errorf("certificate creation via transport.SelfCert failed: %w", selfCertErr)
258273
}

pkg/certificate/interfaces/interface.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,12 @@ const (
4646
// with a delay of RetryInterval between consecutive retries
4747
MaxRetries = 36
4848
RetryInterval = 5 * time.Second
49+
50+
// DefaultAutoValidity is the default validity duration for auto-generated certificates (365 days)
51+
DefaultAutoValidity = 365 * 24 * time.Hour
52+
53+
// DefaultCertManagerValidity is the default validity duration for cert-manager certificates (90 days)
54+
DefaultCertManagerValidity = 90 * 24 * time.Hour
4955
)
5056

5157
// AltNames contains the domain names and IP addresses that will be added

test/e2e/auto_provider_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ import (
2929
const (
3030
autoCertificateName = "sample-cert"
3131
autoCertificateNamespace = "default"
32-
autoCertificateValidity = auto.DefaultValidity
32+
autoCertificateValidity = interfaces.DefaultAutoValidity
3333
)
3434

3535
func TestAutoProvider(t *testing.T) {

test/e2e/cert_manager_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ const (
3030
cmIssuerType = "ClusterIssuer"
3131
cmCertificateName = "sample-cert"
3232
cmCertificateNamespace = "default"
33-
cmCertificateValidity = 24 * time.Hour
33+
cmCertificateValidity = interfaces.DefaultCertManagerValidity
3434
)
3535

3636
var cmIssuer = &certv1.ClusterIssuer{

0 commit comments

Comments
 (0)