Skip to content
This repository has been archived by the owner on Jan 20, 2022. It is now read-only.

Always renew certificates #313

Merged
merged 1 commit into from
Apr 28, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion pkg/pki/BUILD.bazel
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
load("@io_bazel_rules_go//go:def.bzl", "go_library")
load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test")

go_library(
name = "go_default_library",
Expand All @@ -18,3 +18,9 @@ go_library(
"//vendor/k8s.io/klog:go_default_library",
],
)

go_test(
name = "go_default_test",
srcs = ["parse_test.go"],
embed = [":go_default_library"],
)
66 changes: 61 additions & 5 deletions pkg/pki/certs.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,71 @@ import (
"crypto/rsa"
"crypto/x509"
"fmt"
"os"
"reflect"
"sort"
"strconv"
"strings"
"time"

certutil "k8s.io/client-go/util/cert"
"k8s.io/klog"
)

// Renew certificates with less than 60 days left.
const RENEW_THRESHOLD = 60 * 24 * time.Hour
// CertDuration controls how long we issue certificates for. We set
// it to a longer time period, primarily because we don't have a nice
// means of rotation. This was historically one year, but we now set
// it to two years, with kubernetes LTS proposing one year's support.
var CertDuration = 2 * 365 * 24 * time.Hour

// CertRenewalMinTimeLeft is the minimum amount of validity required on
// a certificate to reuse it. Becasue we set this (much) higher than
// CertDuration, we will now always reissue certificates.
var CertMinTimeLeft = 20 * 365 * 24 * time.Hour

// ParseHumanDuration parses a go-style duration string, but
// recognizes additional suffixes: d means "day" and is interpreted as
// 24 hours; y means "year" and is interpreted as 365 days.
func ParseHumanDuration(s string) (time.Duration, error) {
if strings.HasSuffix(s, "y") {
s = strings.TrimSuffix(s, "y")
n, err := strconv.Atoi(s)
if err != nil {
return time.Duration(0), err
}
return time.Duration(n) * 365 * 24 * time.Hour, nil
}

if strings.HasSuffix(s, "d") {
s = strings.TrimSuffix(s, "d")
n, err := strconv.Atoi(s)
if err != nil {
return time.Duration(0), err
}
return time.Duration(n) * 24 * time.Hour, nil
}

return time.ParseDuration(s)

}

func init() {
if s := os.Getenv("ETCD_MANAGER_CERT_DURATION"); s != "" {
v, err := ParseHumanDuration(s)
if err != nil {
klog.Fatalf("failed to parse ETCD_MANAGER_CERT_DURATION=%q", s)
}
CertDuration = v
}

if s := os.Getenv("ETCD_MANAGER_CERT_MIN_TIME_LEFT"); s != "" {
v, err := ParseHumanDuration(s)
if err != nil {
klog.Fatalf("failed to parse ETCD_MANAGER_CERT_MIN_TIME_LEFT=%q", s)
}
CertMinTimeLeft = v
}
}

type Keypair struct {
Certificate *x509.Certificate
Expand Down Expand Up @@ -47,8 +102,8 @@ func EnsureKeypair(store MutableKeypair, config certutil.Config, signer *Keypair

match := true

if match && time.Until(cert.NotAfter) <= RENEW_THRESHOLD {
klog.Infof("certificate expired or almost expired, not valid after %s; will regenerate", cert.NotAfter.Format(time.RFC3339))
if match && time.Until(cert.NotAfter) <= CertMinTimeLeft {
klog.Infof("existing certificate not valid after %s; will regenerate", cert.NotAfter.Format(time.RFC3339))
match = false
}

Expand Down Expand Up @@ -113,7 +168,8 @@ func EnsureKeypair(store MutableKeypair, config certutil.Config, signer *Keypair
var cert *x509.Certificate
var err error
if signer != nil {
cert, err = NewSignedCert(&config, keypair.PrivateKey, signer.Certificate, signer.PrivateKey)
duration := CertDuration
cert, err = NewSignedCert(&config, keypair.PrivateKey, signer.Certificate, signer.PrivateKey, duration)
} else {
cert, err = certutil.NewSelfSignedCACert(config, keypair.PrivateKey)
}
Expand Down
6 changes: 2 additions & 4 deletions pkg/pki/certutils.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,6 @@ const (
RSAPrivateKeyBlockType = "RSA PRIVATE KEY"

rsaKeySize = 2048

duration365d = time.Hour * 24 * 365
)

// EncodeCertPEM returns PEM-endcoded certificate data
Expand All @@ -67,7 +65,7 @@ func NewPrivateKey() (*rsa.PrivateKey, error) {
}

// NewSignedCert creates a signed certificate using the given CA certificate and key
func NewSignedCert(cfg *certutil.Config, key crypto.Signer, caCert *x509.Certificate, caKey crypto.Signer) (*x509.Certificate, error) {
func NewSignedCert(cfg *certutil.Config, key crypto.Signer, caCert *x509.Certificate, caKey crypto.Signer, duration time.Duration) (*x509.Certificate, error) {
serial, err := cryptorand.Int(cryptorand.Reader, new(big.Int).SetInt64(math.MaxInt64))
if err != nil {
return nil, err
Expand All @@ -88,7 +86,7 @@ func NewSignedCert(cfg *certutil.Config, key crypto.Signer, caCert *x509.Certifi
IPAddresses: cfg.AltNames.IPs,
SerialNumber: serial,
NotBefore: caCert.NotBefore,
NotAfter: time.Now().Add(duration365d).UTC(),
NotAfter: time.Now().Add(duration).UTC(),
KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature,
ExtKeyUsage: cfg.Usages,
}
Expand Down
34 changes: 34 additions & 0 deletions pkg/pki/parse_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package pki

import (
"testing"
"time"
)

func TestParseHumanDuration(t *testing.T) {
grid := []struct {
Value string
Expected time.Duration
}{
{"10m", 10 * time.Minute},
{"24h", 24 * time.Hour},
{"1d", 24 * time.Hour},
{"10d", 10 * 24 * time.Hour},
{"365d", 365 * 24 * time.Hour},
{"1y", 365 * 24 * time.Hour},
{"10y", 10 * 365 * 24 * time.Hour},
}

for _, g := range grid {
g := g
t.Run(g.Value, func(t *testing.T) {
actual, err := ParseHumanDuration(g.Value)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if actual != g.Expected {
t.Fatalf("unexpected value; expected=%v, actual=%v", g.Expected, actual)
}
})
}
}
2 changes: 1 addition & 1 deletion test/integration/upgradedowngrade_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,7 @@ func TestUpgradeDowngrade(t *testing.T) {
t.Fatalf("error reading test key after downgrade: %v", err)
}
if v != "worldv3" {
t.Fatalf("unexpected test key value after upgrade: %q", v)
t.Fatalf("unexpected test key value after downgrade: %q", v)
}

n1.AssertVersion(t, fromVersion)
Expand Down