Avoiding TLS/CA race condition on Capsule installation (#7)

* Avoiding race condition on Capsule installation

The TLS Secret controller needs to wait for the CA one in order to
retrieve the updated one, otherwise it could use a non-stored CA.

* Linting

Minor linting fixes

* Makefile for Docker Image

Providing `make docker-image` to speed-up generation of Docker image
This commit is contained in:
Dario Tranchitella
2020-07-25 17:21:36 +02:00
committed by GitHub
parent 812b16fcff
commit bd7218ba44
11 changed files with 29 additions and 23 deletions
+4
View File
@@ -5,3 +5,7 @@ k8s:
.PHONY: crds
crds:
operator-sdk generate crds
.PHONY: docker-image
docker-image:
operator-sdk build quay.io/clastix/capsule:latest
-1
View File
@@ -71,7 +71,6 @@ func main() {
// controller-runtime)
pflag.CommandLine.AddGoFlagSet(flag.CommandLine)
var v bool
pflag.BoolVarP(&v, "version", "v", false, "Print the Capsule version and exit")
+2 -2
View File
@@ -38,7 +38,7 @@ func (c CapsuleCa) ExpiresIn(now time.Time) (time.Duration, error) {
if !c.isAlreadyValid(now) {
return time.Nanosecond, CaNotYetValidError{}
}
return time.Duration(c.ca.NotAfter.Unix() - now.Unix()) * time.Second, nil
return time.Duration(c.ca.NotAfter.Unix()-now.Unix()) * time.Second, nil
}
func (c CapsuleCa) CaCertificatePem() (b *bytes.Buffer, err error) {
@@ -108,7 +108,7 @@ func NewCertificateAuthorityFromBytes(certBytes, keyBytes []byte) (s *CapsuleCa,
}
s = &CapsuleCa{
ca: cert,
ca: cert,
privateKey: key,
}
+1 -1
View File
@@ -19,7 +19,7 @@ func TestNewCertificateAuthorityFromBytes(t *testing.T) {
assert.Nil(t, err)
var crt *bytes.Buffer
crt, err =ca.CaCertificatePem()
crt, err = ca.CaCertificatePem()
assert.Nil(t, err)
var key *bytes.Buffer
+2 -2
View File
@@ -1,12 +1,12 @@
package cert
type CaNotYetValidError struct {}
type CaNotYetValidError struct{}
func (CaNotYetValidError) Error() string {
return "The current CA is not yet valid"
}
type CaExpiredError struct {}
type CaExpiredError struct{}
func (CaExpiredError) Error() string {
return "The current CA is expired"
+8
View File
@@ -0,0 +1,8 @@
package secret
type MissingCaError struct {
}
func (MissingCaError) Error() string {
return "CA has not been created yet, please generate a new"
}
+2 -12
View File
@@ -63,17 +63,7 @@ func (r *ReconcileSecret) GetCertificateAuthority() (ca cert.Ca, err error) {
}
if instance.Data == nil {
ca, err = cert.GenerateCertificateAuthority()
if err != nil {
return
}
instance.Data = map[string][]byte{}
crt, _ := ca.CaCertificatePem()
instance.Data[Cert] = crt.Bytes()
key, _ := ca.CaPrivateKeyPem()
instance.Data[PrivateKey] = key.Bytes()
return nil, MissingCaError{}
}
ca, err = cert.NewCertificateAuthorityFromBytes(instance.Data[Cert], instance.Data[PrivateKey])
@@ -86,4 +76,4 @@ func (r *ReconcileSecret) GetCertificateAuthority() (ca cert.Ca, err error) {
func filterByName(objName, desired string) bool {
return objName == desired
}
}
@@ -16,6 +16,7 @@ package secret
import (
"bytes"
"context"
"errors"
"time"
v1 "k8s.io/api/admissionregistration/v1"
@@ -89,7 +90,12 @@ func caReconcile(r *ReconcileSecret, request reconcile.Request) (reconcile.Resul
var ca cert.Ca
var rq time.Duration
ca, err = r.GetCertificateAuthority()
if err != nil {
if err != nil && errors.Is(err, MissingCaError{}) {
ca, err = cert.GenerateCertificateAuthority()
if err != nil {
return reconcile.Result{}, err
}
} else if err != nil {
return reconcile.Result{}, err
}
@@ -150,7 +156,7 @@ func caReconcile(r *ReconcileSecret, request reconcile.Request) (reconcile.Resul
r.logger.Info("Capsule CA has been updated, we need to trigger TLS update too")
tls := &corev1.Secret{}
err = r.client.Get(context.TODO(), types.NamespacedName{
Namespace: "capsuel-system",
Namespace: "capsule-system",
Name: TlsSecretName,
}, tls)
if err != nil {
@@ -130,7 +130,7 @@ func tlsReconcile(r *ReconcileSecret, request reconcile.Request) (reconcile.Resu
}
var res controllerutil.OperationResult
t := &corev1.Secret{ObjectMeta: instance.ObjectMeta,}
t := &corev1.Secret{ObjectMeta: instance.ObjectMeta}
res, err = controllerutil.CreateOrUpdate(context.TODO(), r.client, t, func() error {
t.Data = instance.Data
return nil
+1 -1
View File
@@ -62,7 +62,7 @@ func add(mgr manager.Manager, r reconcile.Reconciler) error {
}
// Watch for changes to primary resource Tenant
err = c.Watch(&source.Kind{Type: &capsulev1alpha1.Tenant{}}, &handler.EnqueueRequestForObject{}, )
err = c.Watch(&source.Kind{Type: &capsulev1alpha1.Tenant{}}, &handler.EnqueueRequestForObject{})
if err != nil {
return err
}
-1
View File
@@ -56,7 +56,6 @@ func (r *ownerRef) Handle(ctx context.Context, req admission.Request) admission.
return admission.Errored(http.StatusBadRequest, err)
}
g := utils.UserGroupList(req.UserInfo.Groups)
if !g.IsInCapsuleGroup() {
// user requested NS creation is not a Capsule user, so skipping the validation checks