Skip to content
Open
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
23 changes: 9 additions & 14 deletions pkg/cmd/init.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ limitations under the License.
package cmd

import (
"crypto/x509"
"fmt"
"net"
"net/url"
Expand Down Expand Up @@ -46,18 +45,14 @@ func initCerts(cfg *config.Config) (*certchains.CertificateChains, error) {
// we cannot just remove the certs dir and regenerate all the certificates
// because there are some long-lived certs and CAs that shouldn't be swapped
// - for example system:admin client certs, KAS serving CAs
regenCerts, err := certsToRegenerate(certChains)
if err != nil {
return nil, err
}

regenCerts := certsToRegenerate(certChains)
for _, c := range regenCerts {
if err := certChains.Regenerate(c...); err != nil {
return nil, err
}
}

return certChains, err
return certChains, nil
}

func certSetup(cfg *config.Config) (*certchains.CertificateChains, error) {
Expand Down Expand Up @@ -588,9 +583,11 @@ func initKubeconfigs(

// certsToRegenerate returns paths to certificates in the given certificate chains
// bundle that need to be regenerated
func certsToRegenerate(cs *certchains.CertificateChains) ([][]string, error) {
func certsToRegenerate(cs *certchains.CertificateChains) [][]string {
regenCerts := [][]string{}
err := cs.WalkChains(nil, func(certPath []string, c x509.Certificate) error {
for _, entry := range cs.Inventory() {
certPath := entry.Path
c := entry.Certificate
if now := time.Now(); now.Before(c.NotBefore) || now.After(c.NotAfter) {
regenCerts = append(regenCerts, certPath)
}
Expand All @@ -605,18 +602,16 @@ func certsToRegenerate(cs *certchains.CertificateChains) ([][]string, error) {
if timeLeft < until {
regenCerts = append(regenCerts, certPath)
}
return nil
continue
}

// long lived certs
if timeLeft < 18*month {
regenCerts = append(regenCerts, certPath)
}
}

return nil
})

return regenCerts, err
return regenCerts
}

func cleanupStaleKubeconfigs(cfg *config.Config, path string) error {
Expand Down
13 changes: 4 additions & 9 deletions pkg/cmd/init_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,9 @@ import (

func Test_certsToRegenerate(t *testing.T) {
tests := []struct {
name string
chains *certchains.CertificateChains
want [][]string
wantErr bool
name string
chains *certchains.CertificateChains
want [][]string
}{
{
name: "empty chains",
Expand Down Expand Up @@ -118,11 +117,7 @@ func Test_certsToRegenerate(t *testing.T) {

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := certsToRegenerate(tt.chains)
if (err != nil) != tt.wantErr {
t.Errorf("certsToRegenerate() error = %v, wantErr %v", err, tt.wantErr)
return
}
got := certsToRegenerate(tt.chains)
if !reflect.DeepEqual(got, tt.want) {
t.Errorf("certsToRegenerate() = %v, want %v", got, tt.want)
}
Expand Down
59 changes: 7 additions & 52 deletions pkg/util/cryptomaterial/certchains/certchains.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package certchains

import (
"crypto/x509"
"fmt"
"time"

Expand Down Expand Up @@ -60,78 +59,34 @@ func (cs *CertificateChains) Regenerate(certPath ...string) error {
return fmt.Errorf("no such signer: %s", certPath[0])
}

type CertWalkFunc func(certPath []string, c x509.Certificate) error

// WalkChains traverses through the trust chain starting at `rootPath` and applies
// `fn` on all the certificates in the chain tree
func (cs *CertificateChains) WalkChains(rootPath []string, fn CertWalkFunc) error {
if len(rootPath) == 0 {
for _, signerName := range cs.GetSignerNames() {
if err := cs.WalkChains([]string{signerName}, fn); err != nil {
return err
}
}
return nil
}

if signer := cs.GetSigner(rootPath...); signer != nil {
// the path points to a signer
if err := fn(rootPath, *signer.signerConfig.Config.Certs[0]); err != nil {
return fmt.Errorf("failed to execute walk function on %v: %v", rootPath, err)
}

nextNames := append(signer.GetSubCANames(), signer.GetCertNames()...)
for _, name := range nextNames {
if err := cs.WalkChains(append(rootPath, name), fn); err != nil {
return err
}
}
return nil
}
if len(rootPath) == 1 {
// the path is a single element but no such signer exists
return fmt.Errorf("%v is not a path to a signer", rootPath)
}
// the path points to a leaf certificate
signerPath := rootPath[:len(rootPath)-1]
if signer := cs.GetSigner(signerPath...); signer != nil {
cert := signer.signedCertificates[rootPath[len(rootPath)-1]]
if cert == nil {
return fmt.Errorf("the requested element does not exist")
}
return fn(rootPath, *cert.tlsConfig.Certs[0])
}
return fmt.Errorf("a non-leaf fragment of the path '%v' either is not a signer or it doesn't exist", rootPath)
}

func WhenToRotateAtEarliest(cs *CertificateChains) ([]string, time.Time, error) {
var (
certPath []string
rotationDate time.Time
)

err := cs.WalkChains(nil, func(currentPath []string, c x509.Certificate) error {
Comment thread
eggfoobar marked this conversation as resolved.
for _, entry := range cs.Inventory() {
currentPath := entry.Path
c := entry.Certificate
const month = 30 * time.Hour * 24

rotateAt := c.NotAfter.Add(-4 * month)
if !cryptomaterial.IsCertShortLived(&c) {
rotateAt = c.NotAfter.Add(-12 * month)
}
klog.Errorf("%v rotate at: %s", currentPath, rotateAt.String())
klog.Warningf("%v rotate at: %s", currentPath, rotateAt.String())

if rotationDate.IsZero() {
rotationDate = rotateAt
certPath = currentPath
return nil
continue
}

if rotateAt.Before(rotationDate) {
rotationDate = rotateAt
certPath = currentPath
}
}

return nil
})

return certPath, rotationDate, err
return certPath, rotationDate, nil
}
113 changes: 0 additions & 113 deletions pkg/util/cryptomaterial/certchains/certchains_test.go
Original file line number Diff line number Diff line change
@@ -1,14 +1,11 @@
package certchains

import (
"crypto/x509"
"path/filepath"
"strings"
"testing"
"time"

"github.com/stretchr/testify/require"
"k8s.io/apimachinery/pkg/util/diff"
"k8s.io/apiserver/pkg/authentication/user"
)

Expand Down Expand Up @@ -103,116 +100,6 @@ func testChains(t *testing.T, tmpDir string) *CertificateChains {
return ret
}

func TestCertificateChains_WalkChains(t *testing.T) {
tmpDir := t.TempDir()

testChain := testChains(t, tmpDir)

tests := []struct {
name string
path []string
expectedSubjects string
wantErr bool
}{
{
name: "full tree traversal",
path: nil,
wantErr: false,
expectedSubjects: `
CN=test-signer1
CN=test-signer1-subca
CN=test-signer1-subca-too
CN=test-signer1-subca-too-too
CN=test-user2
CN=test-signer1-subca-too-too2
CN=test-user,O=test-group1+O=test-group2
CN=newname.host
CN=test-user,O=test-group1+O=test-group2
CN=test-user2
CN=behind.the.wardrobe.door
CN=test-signer2
CN=bluebirds.fly
CN=test-signer3
CN=test-signer3-subca1
CN=test-user,O=test-group1+O=test-group2
CN=test-user,O=test-group1
CN=castle.brobdingnag`,
},
{
name: "1-level signer",
path: []string{"test-signer2"},
wantErr: false,
expectedSubjects: `
CN=test-signer2
CN=bluebirds.fly`,
},
{
name: "signer w/ subca",
path: []string{"test-signer3"},
wantErr: false,
expectedSubjects: `
CN=test-signer3
CN=test-signer3-subca1
CN=test-user,O=test-group1+O=test-group2
CN=test-user,O=test-group1
CN=castle.brobdingnag`,
},
{
name: "signer/subca",
path: []string{"test-signer3", "test-signer3-subca1"},
wantErr: false,
expectedSubjects: `
CN=test-signer3-subca1
CN=test-user,O=test-group1+O=test-group2`,
},
{
name: "leaf cert",
path: []string{"test-signer2", "test-signer2-server1"},
wantErr: false,
expectedSubjects: `
CN=bluebirds.fly`,
},
{
name: "leaf cert of subca",
path: []string{"test-signer3", "test-signer3-subca1", "test-client1"},
wantErr: false,
expectedSubjects: `
CN=test-user,O=test-group1+O=test-group2`,
},
{
name: "nonexistent signer",
path: []string{"test-signer4"},
wantErr: true,
},
{
name: "nonexistent intermediate signer",
path: []string{"test-signer3", "test-signer3-subca2", "test-client1"},
wantErr: true,
},
{
name: "nonexistent leaf",
path: []string{"test-signer3", "test-signer3-subca1", "test-client2"},
wantErr: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
var subjects string
walkFunc := func(path []string, c x509.Certificate) error {
t.Helper()
subjects += "\n" + strings.Repeat("\t", len(path)-1) + c.Subject.String()
return nil
}

if err := testChain.WalkChains(tt.path, walkFunc); (err != nil) != tt.wantErr {
t.Errorf("CertificateChains.WalkChains() error = %v, wantErr %v", err, tt.wantErr)
}

require.Equal(t, tt.expectedSubjects, subjects, "diff %s", diff.Diff(subjects, tt.expectedSubjects))
})
}
}

func TestWhenToRotateAtEarliest(t *testing.T) {
tmpDir := t.TempDir()

Expand Down
Loading