Skip to content

Commit

Permalink
fear: create root ca.
Browse files Browse the repository at this point in the history
Updates the controller to create the root ca used in all the
certificates used by the Kubewarden stack.

Signed-off-by: José Guilherme Vanz <[email protected]>
  • Loading branch information
jvanz committed Aug 28, 2023
1 parent faa68e9 commit 6e5cd64
Show file tree
Hide file tree
Showing 24 changed files with 1,059 additions and 177 deletions.
2 changes: 1 addition & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# Build the manager binary
FROM golang:1.19 as builder
FROM golang:1.20 as builder

WORKDIR /workspace
# Copy the Go Modules manifests
Expand Down
3 changes: 2 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,7 @@ setup-envtest: $(SETUP_ENVTEST) # Build setup-envtest
unit-tests: manifests generate fmt vet setup-envtest ## Run unit tests.
KUBEBUILDER_ASSETS="$(KUBEBUILDER_ASSETS)" go test ./internal/... -test.v -coverprofile cover.out
KUBEBUILDER_ASSETS="$(KUBEBUILDER_ASSETS)" go test ./pkg/... -test.v -coverprofile cover.out
KUBEBUILDER_ASSETS="$(KUBEBUILDER_ASSETS)" go test . -test.v -coverprofile cover.out

.PHONY: setup-envtest integration-tests
integration-tests: manifests generate fmt vet setup-envtest ## Run integration tests.
Expand All @@ -124,7 +125,7 @@ build: generate fmt vet ## Build manager binary.
go build -o bin/manager .

run: manifests generate fmt vet ## Run a controller from your host.
go run . -deployments-namespace kubewarden --default-policy-server default
go run . -deployments-namespace kubewarden --default-policy-server default -zap-log-level debug

docker-build: test ## Build docker image with the manager.
docker build -t ${IMG} .
Expand Down
11 changes: 7 additions & 4 deletions controllers/admissionpolicy_controller_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,8 +60,8 @@ var _ = Describe("Given an AdmissionPolicy", func() {
Context("and it has a non-empty policy server set on its spec", func() {
var (
policyNamespace = someNamespace.Name
policyName = "scheduled-policy"
policyServerName = "some-policy-server"
policyName = "scheduled-policy"
)
BeforeEach(func() {
Expect(
Expand All @@ -76,16 +76,19 @@ var _ = Describe("Given an AdmissionPolicy", func() {
func(admissionPolicy *policiesv1.AdmissionPolicy) policiesv1.PolicyStatusEnum {
return admissionPolicy.Status.PolicyStatus
},
Equal(policiesv1.PolicyStatusScheduled),
),
)
Equal(policiesv1.PolicyStatusScheduled)))
})
Context("and the targeted policy server is created", func() {
BeforeEach(func() {
Expect(
k8sClient.Create(ctx, policyServer(policyServerName)),
).To(HaveSucceededOrAlreadyExisted())
})
AfterEach(func() {
Expect(
k8sClient.Delete(ctx, policyServer(policyServerName)),
).To(Succeed())
})
It(fmt.Sprintf("should set its policy status to %q", policiesv1.PolicyStatusPending), func() {
Eventually(func(g Gomega) (*policiesv1.AdmissionPolicy, error) {
return getFreshAdmissionPolicy(policyNamespace, policyName)
Expand Down
2 changes: 1 addition & 1 deletion controllers/clusteradmissionpolicy_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ func (r *ClusterAdmissionPolicyReconciler) Reconcile(ctx context.Context, req ct
if apierrors.IsNotFound(err) {
return ctrl.Result{}, nil
}
return ctrl.Result{}, fmt.Errorf("cannot retrieve admission policy: %w", err)
return ctrl.Result{}, fmt.Errorf("cannot retrieve cluster admission policy: %w", err)
}

return startReconciling(ctx, r.Reconciler.Client, r.Reconciler, &clusterAdmissionPolicy)
Expand Down
9 changes: 6 additions & 3 deletions controllers/clusteradmissionpolicy_controller_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,12 @@ import (
"fmt"
"time"

// "github.com/kubewarden/kubewarden-controller/internal/pkg/constants"
policiesv1 "github.com/kubewarden/kubewarden-controller/pkg/apis/policies/v1"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
// admissionregistrationv1 "k8s.io/api/admissionregistration/v1"
// "sigs.k8s.io/controller-runtime/pkg/client"
)

var _ = Describe("Given a ClusterAdmissionPolicy", func() {
Expand Down Expand Up @@ -54,11 +57,11 @@ var _ = Describe("Given a ClusterAdmissionPolicy", func() {
var (
policyName = "scheduled-policy"
policyServerName = "other-policy-server"
policy *policiesv1.ClusterAdmissionPolicy
)
BeforeEach(func() {
Expect(
k8sClient.Create(ctx, clusterAdmissionPolicyWithPolicyServerName(policyName, policyServerName)),
).To(HaveSucceededOrAlreadyExisted())
policy = clusterAdmissionPolicyWithPolicyServerName(policyName, policyServerName)
Expect(k8sClient.Create(ctx, policy)).To(HaveSucceededOrAlreadyExisted())
})
It(fmt.Sprintf("should set its policy status to %q", policiesv1.PolicyStatusScheduled), func() {
Eventually(func(g Gomega) (*policiesv1.ClusterAdmissionPolicy, error) {
Expand Down
10 changes: 8 additions & 2 deletions controllers/policy_utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,12 @@ func startReconciling(ctx context.Context, client client.Client, reconciler admi

_ = setPolicyStatus(ctx, reconciler.DeploymentsNamespace, reconciler.APIReader, policy)
if err := client.Status().Update(ctx, policy); err != nil {
if apierrors.IsConflict(err) {
return ctrl.Result{
Requeue: true,
RequeueAfter: time.Second * 5,
}, nil
}
return ctrl.Result{}, fmt.Errorf("update admission policy status error: %w", err)
}

Expand Down Expand Up @@ -144,8 +150,8 @@ func reconcilePolicy(ctx context.Context, client client.Client, reconciler admis
)

secret := corev1.Secret{}
if err := client.Get(ctx, types.NamespacedName{Namespace: reconciler.DeploymentsNamespace, Name: constants.PolicyServerCARootSecretName}, &secret); err != nil {
return ctrl.Result{}, errors.Wrap(err, "cannot find policy server secret")
if err := client.Get(ctx, types.NamespacedName{Namespace: reconciler.DeploymentsNamespace, Name: constants.KubewardenCARootSecretName}, &secret); err != nil {
return ctrl.Result{}, errors.Wrap(err, "cannot find root CA secret")
}

if policy.IsMutating() {
Expand Down
29 changes: 29 additions & 0 deletions controllers/policyserver_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import (
"github.com/kubewarden/kubewarden-controller/internal/pkg/constants"
policiesv1 "github.com/kubewarden/kubewarden-controller/pkg/apis/policies/v1"
"github.com/pkg/errors"
corev1 "k8s.io/api/core/v1"
apierrors "k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/runtime"
ctrl "sigs.k8s.io/controller-runtime"
Expand Down Expand Up @@ -80,6 +81,12 @@ func (r *PolicyServerReconciler) Reconcile(ctx context.Context, req ctrl.Request
reconcileResult, reconcileErr := r.reconcile(ctx, &policyServer, policies)

if err := r.Client.Status().Update(ctx, &policyServer); err != nil {
if apierrors.IsConflict(err) {
return ctrl.Result{
Requeue: true,
RequeueAfter: time.Second * 5,
}, nil
}
return ctrl.Result{}, fmt.Errorf("update policy server status error: %w", err)
}

Expand Down Expand Up @@ -191,6 +198,28 @@ func (r *PolicyServerReconciler) SetupWithManager(mgr ctrl.Manager) error {
},
}
})).
Watches(&source.Kind{Type: &corev1.Secret{}}, handler.EnqueueRequestsFromMapFunc(func(object client.Object) []reconcile.Request {
// watches for secret to detect when a policy server
// secret change. Therefore, the certificate can be
// recreated and the webhooks updated
secret, ok := object.(*corev1.Secret)
if !ok {
r.Log.Info("object is not type of secret: %+v", "secret", secret)
return []ctrl.Request{}
}

if policyServerName, isPolicyServerSecret := secret.Labels[constants.PolicyServerLabelKey]; isPolicyServerSecret {
return []ctrl.Request{
{
NamespacedName: client.ObjectKey{
Name: policyServerName,
},
},
}
}
return []ctrl.Request{}

Check failure on line 221 in controllers/policyserver_controller.go

View workflow job for this annotation

GitHub Actions / Golangci-lint

unnecessary trailing newline (whitespace)
})).
Complete(r)

return errors.Wrap(err, "failed enrolling controller with manager")
Expand Down
62 changes: 61 additions & 1 deletion controllers/policyserver_controller_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,20 +23,79 @@ import (
policiesv1 "github.com/kubewarden/kubewarden-controller/pkg/apis/policies/v1"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
corev1 "k8s.io/api/core/v1"
apierrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"sigs.k8s.io/controller-runtime/pkg/client"

"github.com/kubewarden/kubewarden-controller/internal/pkg/constants"
)

var _ = Describe("Given a PolicyServer", func() {
var (
policyServerName = "policy-server"
policyServerName = "policy-server"
policyServerNameWithPrefix = policyServer(policyServerName).NameWithPrefix()
)
BeforeEach(func() {
Expect(
k8sClient.Create(ctx, policyServer(policyServerName)),
).To(HaveSucceededOrAlreadyExisted())
})
It("a secret for the policy server certificate should be created", func() {
Eventually(func(g Gomega) ([]string, error) {
secret := &corev1.Secret{}
err := k8sClient.Get(ctx, client.ObjectKey{Name: policyServerNameWithPrefix, Namespace: DeploymentsNamespace}, secret)
if err != nil {
return []string{}, err
}
dataKeys := []string{}
for key := range secret.Data {
dataKeys = append(dataKeys, key)
}
return dataKeys, err
}, 30*time.Second, 250*time.Millisecond).Should(Equal([]string{constants.PolicyServerTLSCert, constants.PolicyServerTLSKey}))
})
When("policy server secret is deleted", func() {
BeforeEach(func() {
Expect(
k8sClient.Delete(ctx, &corev1.Secret{
ObjectMeta: metav1.ObjectMeta{
Name: policyServerNameWithPrefix,
Namespace: DeploymentsNamespace,
},
})).To(Succeed())
Eventually(func(g Gomega) bool {
err := k8sClient.Get(ctx, client.ObjectKey{Name: policyServerNameWithPrefix, Namespace: DeploymentsNamespace}, &corev1.Secret{})
return apierrors.IsNotFound(err)
}, 30*time.Second, 250*time.Millisecond).Should(BeTrue())
})
It("it should be recreated", func() {
Eventually(func(g Gomega) ([]string, error) {
secret := &corev1.Secret{}
err := k8sClient.Get(ctx, client.ObjectKey{Name: policyServerNameWithPrefix, Namespace: DeploymentsNamespace}, secret)
if err != nil {
return []string{}, err
}
dataKeys := []string{}
for key := range secret.Data {
dataKeys = append(dataKeys, key)
}
return dataKeys, err
}, 30*time.Second, 250*time.Millisecond).Should(Equal([]string{constants.PolicyServerTLSCert, constants.PolicyServerTLSKey}))
})
})
When("policy server is deleted", func() {
It("its secret should be deleted as well", func() {
Expect(
k8sClient.Delete(ctx, policyServer(policyServerName)),
).To(Succeed())

Eventually(func(g Gomega) bool {
err := k8sClient.Get(ctx, client.ObjectKey{Name: policyServerNameWithPrefix, Namespace: DeploymentsNamespace}, &corev1.Secret{})
return apierrors.IsNotFound(err)
}, 30*time.Second, 250*time.Millisecond).Should(BeTrue())
})
})
When("it has no assigned policies", func() {
Context("and it is deleted", func() {
BeforeEach(func() {
Expand Down Expand Up @@ -64,6 +123,7 @@ var _ = Describe("Given a PolicyServer", func() {
k8sClient.Create(ctx, clusterAdmissionPolicyWithPolicyServerName(policyName, policyServerName)),
).To(HaveSucceededOrAlreadyExisted())
})

Context("and it is deleted", func() {
BeforeEach(func() {
Expect(
Expand Down
32 changes: 32 additions & 0 deletions controllers/suite_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ package controllers

import (
"context"
"crypto/x509"
"log"
"path/filepath"
"testing"
Expand All @@ -36,6 +37,8 @@ import (
"sigs.k8s.io/controller-runtime/pkg/log/zap"

"github.com/kubewarden/kubewarden-controller/internal/pkg/admission"
"github.com/kubewarden/kubewarden-controller/internal/pkg/admissionregistration"
"github.com/kubewarden/kubewarden-controller/internal/pkg/constants"
//+kubebuilder:scaffold:imports
)

Expand All @@ -48,6 +51,7 @@ var testEnv *envtest.Environment
var ctx context.Context
var cancel context.CancelFunc
var reconciler admission.Reconciler
var caRootSecret *corev1.Secret

const (
DeploymentsNamespace = "kubewarden-integration-tests"
Expand Down Expand Up @@ -125,6 +129,34 @@ var _ = BeforeSuite(func() {
log.Fatalf("could not create namespace %q needed for the integration tests", DeploymentsNamespace)
}

// Create the root CA generated in the main of the controller
caRoot, err := admissionregistration.GenerateCA()
if err != nil {
log.Fatalf("cannot generate policy-server secret CA")
}
caPEMEncoded, err := admissionregistration.PemEncodeCertificate(caRoot.CaCert)
if err != nil {
log.Fatalf("cannot encode policy-server secret CA")
}
caPrivateKeyBytes := x509.MarshalPKCS1PrivateKey(caRoot.CaPrivateKey)
caRootSecret = &corev1.Secret{
ObjectMeta: metav1.ObjectMeta{
Name: constants.KubewardenCARootSecretName,
Namespace: DeploymentsNamespace,
},
Data: map[string][]byte{
constants.CARootCACert: caRoot.CaCert,
constants.CARootCACertPem: caPEMEncoded,
constants.CARootPrivateKeyCertName: caPrivateKeyBytes,
},
Type: corev1.SecretTypeOpaque,
}

// Create the integration tests deployments namespace
if err := k8sClient.Create(ctx, caRootSecret); err != nil {
log.Fatalf("could not create root CA secret: ")
}

go func() {
defer GinkgoRecover()
err = k8sManager.Start(ctx)
Expand Down
2 changes: 2 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -88,3 +88,5 @@ require (
sigs.k8s.io/structured-merge-diff/v4 v4.2.3 // indirect
sigs.k8s.io/yaml v1.3.0 // indirect
)

replace github.com/ereslibre/kube-webhook-wrapper v0.0.2 => github.com/jvanz/kube-webhook-wrapper v0.0.0-20230822174616-a67c0d737762

Check failure on line 92 in go.mod

View workflow job for this annotation

GitHub Actions / Golangci-lint

replacement are not allowed: github.com/ereslibre/kube-webhook-wrapper (gomoddirectives)
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -123,8 +123,6 @@ github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.m
github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ=
github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0=
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
github.com/ereslibre/kube-webhook-wrapper v0.0.2 h1:GaSN5jfSPJV7KNuVzSFflS5Rr5wQNKVsZbL6Gq1ZHYw=
github.com/ereslibre/kube-webhook-wrapper v0.0.2/go.mod h1:vDSrlA2/6bFRlDdVCC0Woe21U5J9s9a7FfSgaxuTPv4=
github.com/evanphx/json-patch v0.5.2/go.mod h1:ZWS5hhDbVDyob71nXKNL0+PWn6ToqBHMikGIFbs31qQ=
github.com/evanphx/json-patch v4.11.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
github.com/evanphx/json-patch v5.6.0+incompatible h1:jBYDEEiFBPxA0v50tFdvOzQQTCvpL6mnFh5mB2/l16U=
Expand Down Expand Up @@ -317,6 +315,8 @@ github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/X
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM=
github.com/jvanz/kube-webhook-wrapper v0.0.0-20230822174616-a67c0d737762 h1:s++gelgq+Od1qgDPfYjJu9/CurV73FSkjXOLubW8w1g=
github.com/jvanz/kube-webhook-wrapper v0.0.0-20230822174616-a67c0d737762/go.mod h1:R4GtKm8O+QH4Bglxefern9eLkVpVKXGwje3uJkSR/T8=
github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00=
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
Expand Down
2 changes: 1 addition & 1 deletion internal/pkg/admission/mutating-webhook.go
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ func (r *Reconciler) mutatingWebhookConfiguration(
Name: fmt.Sprintf("%s.kubewarden.admission", policy.GetUniqueName()),
ClientConfig: admissionregistrationv1.WebhookClientConfig{
Service: &service,
CABundle: admissionSecret.Data[constants.PolicyServerCARootPemName],
CABundle: admissionSecret.Data[constants.CARootCACertPem],
},
Rules: policy.GetRules(),
FailurePolicy: policy.GetFailurePolicy(),
Expand Down
Loading

0 comments on commit 6e5cd64

Please sign in to comment.