Skip to content
This repository has been archived by the owner on Mar 24, 2023. It is now read-only.

Commit

Permalink
automatically regenerate certs and renew CAs when they expire within …
Browse files Browse the repository at this point in the history
…6 months
  • Loading branch information
laverya committed Oct 18, 2019
1 parent c5d433d commit 18ff4fd
Show file tree
Hide file tree
Showing 204 changed files with 6,266 additions and 1,190 deletions.
17 changes: 8 additions & 9 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -49,14 +49,15 @@ require (
github.com/go-stack/stack v1.8.0
github.com/go-test/deep v1.0.1
github.com/gobuffalo/packr v1.30.1 // indirect
github.com/gobwas/glob v0.2.3 // indirect
github.com/golang/mock v1.3.1
github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db // indirect
github.com/golangci/golangci-lint v1.20.0 // indirect
github.com/google/certificate-transparency-go v1.0.21 // indirect
github.com/google/go-github/v18 v18.0.0
github.com/google/go-querystring v1.0.0
github.com/google/uuid v1.1.0 // indirect
github.com/googleapis/gnostic v0.2.0 // indirect
github.com/gordonklaus/ineffassign v0.0.0-20190601041439-ed7b1b5ee0f8 // indirect
github.com/gosuri/uitable v0.0.0-20160404203958-36ee7e946282
github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7 // indirect
github.com/hashicorp/go-getter v0.0.0-20180809191950-4bda8fa99001
Expand All @@ -69,7 +70,6 @@ require (
github.com/imdario/mergo v0.3.6 // indirect
github.com/jarcoal/httpmock v0.0.0-20180424175123-9c70cfe4a1da
github.com/jmoiron/sqlx v1.2.0 // indirect
github.com/lib/pq v1.1.1 // indirect
github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de // indirect
github.com/mailru/easyjson v0.0.0-20180723221831-d5012789d665 // indirect
github.com/mattn/go-runewidth v0.0.3 // indirect
Expand All @@ -80,8 +80,8 @@ require (
github.com/mitchellh/go-testing-interface v0.0.0-20171004221916-a61a99592b77 // indirect
github.com/mitchellh/hashstructure v1.0.0 // indirect
github.com/nwaples/rardecode v0.0.0-20171029023500-e06696f847ae // indirect
github.com/onsi/ginkgo v1.6.0
github.com/onsi/gomega v1.4.2
github.com/onsi/ginkgo v1.10.1
github.com/onsi/gomega v1.7.0
github.com/opencontainers/go-digest v1.0.0-rc1 // indirect
github.com/opencontainers/image-spec v1.0.1 // indirect
github.com/pact-foundation/pact-go v1.0.0-beta.5
Expand All @@ -98,17 +98,16 @@ require (
github.com/spf13/afero v1.2.2
github.com/spf13/cobra v0.0.5
github.com/spf13/viper v1.4.0
github.com/stretchr/testify v1.3.0
github.com/stretchr/testify v1.4.0
github.com/technosophos/moniker v0.0.0-20180509230615-a5dbd03a2245 // indirect
github.com/xlab/handysort v0.0.0-20150421192137-fb3537ed64a1 // indirect
github.com/zclconf/go-cty v0.0.0-20181017232614-01c5aba823a6 // indirect
github.com/ziutek/mymysql v1.5.4 // indirect
go.uber.org/dig v1.7.0
golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586
golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297 // indirect
golang.org/x/crypto v0.0.0-20190923035154-9ee001bba392
golang.org/x/lint v0.0.0-20190930215403-16217165b5de // indirect
golang.org/x/oauth2 v0.0.0-20181120190819-8f65e3013eba
golang.org/x/sys v0.0.0-20190904154756-749cb33beabd // indirect
golang.org/x/text v0.3.2 // indirect
golang.org/x/tools v0.0.0-20191018192435-a1005cf9b2e3 // indirect
google.golang.org/appengine v1.3.0 // indirect
google.golang.org/grpc v1.21.0
gopkg.in/gorp.v1 v1.7.2 // indirect
Expand Down
155 changes: 155 additions & 0 deletions go.sum

Large diffs are not rendered by default.

9 changes: 4 additions & 5 deletions pkg/state/manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -446,9 +446,6 @@ func (m *MManager) AddCert(name string, newCert util.CertType) error {
if state.V1.Certs == nil {
state.V1.Certs = make(map[string]util.CertType)
}
if _, ok := state.V1.Certs[name]; ok {
return state, fmt.Errorf("cert with name %s already exists in state", name)
}
state.V1.Certs[name] = newCert
return state, nil
})
Expand All @@ -464,8 +461,10 @@ func (m *MManager) AddCA(name string, newCA util.CAType) error {
if state.V1.CAs == nil {
state.V1.CAs = make(map[string]util.CAType)
}
if _, ok := state.V1.CAs[name]; ok {
return state, fmt.Errorf("cert with name %s already exists in state", name)
if existing, ok := state.V1.CAs[name]; ok {
if existing.Key != newCA.Key {
return state, fmt.Errorf("cert with name %s already exists in state", name)
}
}
state.V1.CAs[name] = newCA
return state, nil
Expand Down
32 changes: 27 additions & 5 deletions pkg/state/manager_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -841,7 +841,7 @@ func TestMManager_AddCA(t *testing.T) {
name: "colliding ca names",
wantErr: true,
caName: "aCA",
newCA: util.CAType{Cert: "aCert", Key: "aKey"},
newCA: util.CAType{Cert: "bCert", Key: "bKey"},
before: State{
V1: &V1{
Upstream: "abc123",
Expand All @@ -859,6 +859,28 @@ func TestMManager_AddCA(t *testing.T) {
},
},
},
{
name: "colliding ca names with a new cert",
wantErr: false,
caName: "aCA",
newCA: util.CAType{Cert: "bCert", Key: "aKey"},
before: State{
V1: &V1{
Upstream: "abc123",
CAs: map[string]util.CAType{
"aCA": {Cert: "aCert", Key: "aKey"},
},
},
},
expected: State{
V1: &V1{
Upstream: "abc123",
CAs: map[string]util.CAType{
"aCA": {Cert: "bCert", Key: "aKey"},
},
},
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
Expand Down Expand Up @@ -941,10 +963,10 @@ func TestMManager_AddCert(t *testing.T) {
},
},
{
name: "colliding ca names",
wantErr: true,
name: "colliding cert names",
wantErr: false,
certName: "aCert",
newCert: util.CertType{Cert: "aCert", Key: "aKey"},
newCert: util.CertType{Cert: "bCert", Key: "bKey"},
before: State{
V1: &V1{
Upstream: "abc123",
Expand All @@ -957,7 +979,7 @@ func TestMManager_AddCert(t *testing.T) {
V1: &V1{
Upstream: "abc123",
Certs: map[string]util.CertType{
"aCert": {Cert: "aCert", Key: "aKey"},
"aCert": {Cert: "bCert", Key: "bKey"},
},
},
},
Expand Down
41 changes: 39 additions & 2 deletions pkg/templates/ship_context.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package templates
import (
"strings"
"text/template"
"time"

"github.com/go-kit/kit/log"
"github.com/replicatedhq/ship/pkg/state"
Expand Down Expand Up @@ -95,6 +96,31 @@ func (ctx ShipContext) makeCaKey(caName string, caType string) string {
CAs := currentState.CurrentCAs()
if CAs != nil {
if ca, ok := CAs[caName]; ok {
// check the expiry date of the ca's cert
// if it is within the next 6 months, regenerate it
left, err := util.TimeToExpire([]byte(ca.Cert))
if err != nil {
ctx.Logger.Log("method", "makeCaKey", "action", "calculateExpire", "error", err)
return ""
}

// if the existing ca cert expires in more than 6 months
if left > time.Hour*180*24 {
return ca.Key
}

renewedCA, err := util.RenewCA(ca)
if err != nil {
ctx.Logger.Log("method", "makeCaKey", "action", "renewCACert", "error", err)
return ""
}

err = ctx.Manager.AddCA(caName, renewedCA)
if err != nil {
ctx.Logger.Log("method", "makeCaKey", "action", "saveUpdatedCA", "error", err)
return ""
}

return ca.Key
}
}
Expand Down Expand Up @@ -143,11 +169,22 @@ func (ctx ShipContext) makeCertKey(certName string, caName string, hosts string,
certs := currentState.CurrentCerts()
if certs != nil {
if cert, ok := certs[certName]; ok {
return cert.Key
// check the expiry date of the cert
// if it is within the next 6 months, regenerate it
left, err := util.TimeToExpire([]byte(cert.Cert))
if err != nil {
ctx.Logger.Log("method", "makeCertKey", "action", "calculateExpire", "error", err)
return ""
}

// if the existing cert expires in more than 6 months
if left > time.Hour*180*24 {
return cert.Key
}
}
}

// cert does not yet exist - get CA and make a cert with it
// cert does not yet exist or is expired - get CA and make a cert with it
caKey := ctx.makeCaKey(caName, certType)
if caKey == "" {
ctx.Logger.Log("method", "makeCertKey", "error", "caKeyNotPresent")
Expand Down
34 changes: 34 additions & 0 deletions pkg/util/certs.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package util

import (
"crypto/x509"
"encoding/pem"
"fmt"
"strconv"
"strings"
Expand Down Expand Up @@ -122,3 +124,35 @@ func MakeCA(caKind string) (CAType, error) {

return CAType{Cert: string(cert), Key: string(key)}, nil
}

// TimeToExpire takes a certificate, parses it, and returns the duration left until the certificate expires.
func TimeToExpire(cert []byte) (time.Duration, error) {
block, _ := pem.Decode(cert)
parsedCert, err := x509.ParseCertificate(block.Bytes)
if err != nil {
return time.Duration(0), errors.Wrap(err, "parse cert for expiration")
}
return time.Until(parsedCert.NotAfter), nil
}

// RenewCA takes an existing CA and generates a new certificate with the notBefore/notAfter dates updated.
func RenewCA(ca CAType) (CAType, error) {
// regenerate the CA cert *with the same key*
parsedCert, err := helpers.ParseCertificatePEM([]byte(ca.Cert))
if err != nil {
return CAType{}, errors.Wrap(err, "parse ca certificate")
}

parsedKey, err := helpers.ParsePrivateKeyPEM([]byte(ca.Key))
if err != nil {
return CAType{}, errors.Wrap(err, "parse ca key")
}

newCABytes, err := initca.RenewFromSigner(parsedCert, parsedKey)
if err != nil {
return CAType{}, errors.Wrap(err, "renew ca certificate")
}

ca.Cert = string(newCABytes)
return ca, nil
}
Loading

0 comments on commit 18ff4fd

Please sign in to comment.