Skip to content
This repository has been archived by the owner on Jul 16, 2024. It is now read-only.

Commit

Permalink
Add renew certificates subcommand
Browse files Browse the repository at this point in the history
  • Loading branch information
pytimer committed Dec 22, 2019
1 parent 5081db4 commit 87bda94
Show file tree
Hide file tree
Showing 5 changed files with 170 additions and 0 deletions.
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,12 @@ On the machine being removed, run
etcdadm reset
```

### Renewal certificates

```
etcdadm certs renew
```

## Advanced Usage

### Creating a new cluster from a snapshot
Expand Down
55 changes: 55 additions & 0 deletions certs/certs.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import (
"crypto/x509"
"fmt"

"github.com/pkg/errors"
log "sigs.k8s.io/etcdadm/pkg/logrus"

certutil "k8s.io/client-go/util/cert"
Expand All @@ -34,6 +35,60 @@ import (
"sigs.k8s.io/etcdadm/constants"
)

// GetDefaultCertList returns all of the certificates etcdadm requires to function.
func GetDefaultCertList() []string {
return []string{
//constants.EtcdCACertAndKeyBaseName,
constants.EtcdServerCertAndKeyBaseName,
constants.EtcdPeerCertAndKeyBaseName,
constants.EtcdctlClientCertAndKeyBaseName,
constants.APIServerEtcdClientCertAndKeyBaseName,
}
}

// RenewUsingLocalCA executes certificate renewal using local certificate authorities for generating new certs.
func RenewUsingLocalCA(pkiDir, name string) (bool, error) {
// reads the current certificate
cert, err := pkiutil.TryLoadCertFromDiskIgnoreExpirationDate(pkiDir, name)
if err != nil {
return false, err
}

// extract the certificate config
cfg := certToConfig(cert)

// reads the CA
caCert, caKey, err := loadCertificateAuthority(pkiDir, constants.EtcdCACertAndKeyBaseName)
if err != nil {
return false, err
}

// create a new certificate with the same config
newCert, newKey, err := pkiutil.NewCertAndKey(caCert, caKey, cfg)
if err != nil {
return false, errors.Wrapf(err, "failed to renew certificate %s", name)
}

// writes the new certificate to disk
if err := pkiutil.WriteCertAndKey(pkiDir, name, newCert, newKey); err != nil {
return false, errors.Wrapf(err, "failed to write new certificate %s", name)
}

return true, nil
}

func certToConfig(cert *x509.Certificate) certutil.Config {
return certutil.Config{
CommonName: cert.Subject.CommonName,
Organization: cert.Subject.Organization,
AltNames: certutil.AltNames{
IPs: cert.IPAddresses,
DNSNames: cert.DNSNames,
},
Usages: cert.ExtKeyUsage,
}
}

// CreatePKIAssets will create and write to disk all PKI assets necessary to establish the control plane.
// If the PKI assets already exists in the target folder, they are used only if evaluated equal; otherwise an error is returned.
func CreatePKIAssets(cfg *apis.EtcdAdmConfig) error {
Expand Down
13 changes: 13 additions & 0 deletions certs/pkiutil/pki_helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,19 @@ func TryLoadCertAndKeyFromDisk(pkiPath, name string) (*x509.Certificate, *rsa.Pr
return cert, key, nil
}

// TryLoadCertFromDiskIgnoreExpirationDate tries to load the cert from the disk and ignore expiration date
func TryLoadCertFromDiskIgnoreExpirationDate(pkiPath, name string) (*x509.Certificate, error) {
certificatePath := pathForCert(pkiPath, name)

certs, err := certutil.CertsFromFile(certificatePath)
if err != nil {
return nil, fmt.Errorf("couldn't load the certificate file %s: %v", certificatePath, err)
}

// We are only putting one certificate in the certificate pem file, so it's safe to just pick the first one
return certs[0], nil
}

// TryLoadCertFromDisk tries to load the cert from the disk and validates that it is valid
func TryLoadCertFromDisk(pkiPath, name string) (*x509.Certificate, error) {
certificatePath := pathForCert(pkiPath, name)
Expand Down
50 changes: 50 additions & 0 deletions certs/pkiutil/pki_helpers_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -307,6 +307,56 @@ func TestTryLoadCertAndKeyFromDisk(t *testing.T) {
}
}

func TestTryLoadCertFromDiskIgnoreExpirationDate(t *testing.T) {
tmpdir, err := ioutil.TempDir("", "")
if err != nil {
t.Fatalf("Couldn't create tmpdir")
}
defer os.RemoveAll(tmpdir)

caCert, _, err := NewCertificateAuthority()
if err != nil {
t.Errorf(
"failed to create cert and key with an error: %v",
err,
)
}
err = WriteCert(tmpdir, "foo", caCert)
if err != nil {
t.Errorf(
"failed to write cert and key with an error: %v",
err,
)
}

var tests = []struct {
path string
name string
expected bool
}{
{
path: "",
name: "",
expected: false,
},
{
path: tmpdir,
name: "foo",
expected: true,
},
}
for _, rt := range tests {
_, actual := TryLoadCertFromDiskIgnoreExpirationDate(rt.path, rt.name)
if (actual == nil) != rt.expected {
t.Errorf(
"failed TryLoadCertFromDiskIgnoreExpirationDate:\n\texpected: %t\n\t actual: %t",
rt.expected,
(actual == nil),
)
}
}
}

func TestTryLoadCertFromDisk(t *testing.T) {
tmpdir, err := ioutil.TempDir("", "")
if err != nil {
Expand Down
46 changes: 46 additions & 0 deletions cmd/certs.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package cmd

import (
"github.com/spf13/cobra"
"sigs.k8s.io/etcdadm/certs"
"sigs.k8s.io/etcdadm/constants"
log "sigs.k8s.io/etcdadm/pkg/logrus"
)

// newCmdCerts returns main command for certs phase
func newCmdCerts() *cobra.Command {
cmd := &cobra.Command{
Use: "certs",
Short: "Commands related to handling etcdadm certificates",
}

cmd.AddCommand(newCmdCertsRenewal())

return cmd
}

func newCmdCertsRenewal() *cobra.Command {
var certificatesDir string
cmd := &cobra.Command{
Use: "renew",
Short: "Renew certificates for a etcd cluster",
Run: func(cmd *cobra.Command, args []string) {
for _, name := range certs.GetDefaultCertList() {
renewed, err := certs.RenewUsingLocalCA(certificatesDir, name)
if err != nil {
log.Fatalf("[renew] Error renew certificate %s: %v", name, err)
}
if !renewed {
log.Fatalf("Certificates %s can't be renewed.\n", name)
}
}
},
}
cmd.PersistentFlags().StringVar(&certificatesDir, "certs-dir", constants.DefaultCertificateDir, "certificates directory")

return cmd
}

func init() {
rootCmd.AddCommand(newCmdCerts())
}

0 comments on commit 87bda94

Please sign in to comment.