Skip to content

Commit

Permalink
feat: add support for created secrets & certs
Browse files Browse the repository at this point in the history
  • Loading branch information
FabianKramm committed Jun 1, 2022
1 parent 30fef39 commit 2d7b3df
Show file tree
Hide file tree
Showing 13 changed files with 582 additions and 3,968 deletions.
3,570 changes: 0 additions & 3,570 deletions .devspace/logs/dev.ports-0.log

This file was deleted.

1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
.idea/
/test
.devspace/
7 changes: 0 additions & 7 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import (
"github.com/loft-sh/vcluster-cert-manager-plugin/pkg/constants"
"github.com/loft-sh/vcluster-cert-manager-plugin/pkg/hooks/ingresses"
"github.com/loft-sh/vcluster-cert-manager-plugin/pkg/syncers/certificates"
"github.com/loft-sh/vcluster-cert-manager-plugin/pkg/syncers/certificatesecrets"
"github.com/loft-sh/vcluster-cert-manager-plugin/pkg/syncers/issuers"
"github.com/loft-sh/vcluster-cert-manager-plugin/pkg/syncers/secrets"
"github.com/loft-sh/vcluster-sdk/plugin"
Expand Down Expand Up @@ -36,12 +35,6 @@ func main() {
klog.Fatalf("Error registering certificate syncer: %v", err)
}

// register certificate secret syncer
err = plugin.Register(certificatesecrets.New(registerCtx))
if err != nil {
klog.Fatalf("Error registering certificate secrets syncer: %v", err)
}

// register issuer syncer
err = plugin.Register(issuers.New(registerCtx))
if err != nil {
Expand Down
7 changes: 6 additions & 1 deletion pkg/constants/constants.go
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
package constants

const (
PluginName = "cert-manager"
PluginName = "cert-manager-plugin"

BackwardSyncAnnotation = "cert-manager.vcluster.loft.sh/sync-backward"

IssuerAnnotation = "cert-manager.io/issuer"
ClusterIssuerAnnotation = "cert-manager.io/cluster-issuer"
)
9 changes: 3 additions & 6 deletions pkg/hooks/ingresses/hook.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,14 @@ package ingresses
import (
"context"
"fmt"
"github.com/loft-sh/vcluster-cert-manager-plugin/pkg/constants"
"github.com/loft-sh/vcluster-sdk/hook"
"github.com/loft-sh/vcluster-sdk/syncer/translator"
"github.com/loft-sh/vcluster-sdk/translate"
networkingv1 "k8s.io/api/networking/v1"
"sigs.k8s.io/controller-runtime/pkg/client"
)

var (
issuerAnnotation = "cert-manager.io/issuer"
)

func NewIngressHook() hook.ClientHook {
return &ingressHook{}
}
Expand Down Expand Up @@ -53,7 +50,7 @@ func (p *ingressHook) MutateUpdatePhysical(ctx context.Context, obj client.Objec
}

func mutateIngress(ingress *networkingv1.Ingress) {
if ingress.Annotations != nil && ingress.Annotations[issuerAnnotation] != "" {
ingress.Annotations[issuerAnnotation] = translate.PhysicalName(ingress.Annotations[issuerAnnotation], ingress.Annotations[translator.NamespaceAnnotation])
if ingress.Annotations != nil && ingress.Annotations[constants.IssuerAnnotation] != "" {
ingress.Annotations[constants.IssuerAnnotation] = translate.PhysicalName(ingress.Annotations[constants.IssuerAnnotation], ingress.Annotations[translator.NamespaceAnnotation])
}
}
133 changes: 133 additions & 0 deletions pkg/syncers/certificates/mapping.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
package certificates

import (
context2 "context"
certmanagerv1 "github.com/cert-manager/cert-manager/pkg/apis/certmanager/v1"
"github.com/loft-sh/vcluster-cert-manager-plugin/pkg/constants"
"github.com/loft-sh/vcluster-sdk/clienthelper"
"github.com/loft-sh/vcluster-sdk/syncer"
"github.com/loft-sh/vcluster-sdk/syncer/context"
"github.com/loft-sh/vcluster-sdk/translate"
networkingv1 "k8s.io/api/networking/v1"
"k8s.io/apimachinery/pkg/types"
"sigs.k8s.io/controller-runtime/pkg/builder"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/handler"
"sigs.k8s.io/controller-runtime/pkg/reconcile"
"sigs.k8s.io/controller-runtime/pkg/source"
"strings"
)

var (
IndexByIngressCertificate = "indexbyingresscertificate"
)

var _ syncer.IndicesRegisterer = &certificateSyncer{}

func (s *certificateSyncer) RegisterIndices(ctx *context.RegisterContext) error {
err := ctx.VirtualManager.GetFieldIndexer().IndexField(ctx.Context, &networkingv1.Ingress{}, IndexByIngressCertificate, func(rawObj client.Object) []string {
return certificateNamesFromIngress(rawObj.(*networkingv1.Ingress))
})
if err != nil {
return err
}

return s.NamespacedTranslator.RegisterIndices(ctx)
}

var _ syncer.ControllerModifier = &certificateSyncer{}

func (s *certificateSyncer) ModifyController(ctx *context.RegisterContext, builder *builder.Builder) (*builder.Builder, error) {
builder = builder.Watches(&source.Kind{Type: &networkingv1.Ingress{}}, handler.EnqueueRequestsFromMapFunc(mapIngresses))
return builder, nil
}

func (s *certificateSyncer) shouldSyncBackwards(pCertificate, vCertificate *certmanagerv1.Certificate) (bool, types.NamespacedName) {
// we sync secrets that were generated from certificates or issuers into the vcluster
if vCertificate != nil && vCertificate.Annotations != nil && vCertificate.Annotations[constants.BackwardSyncAnnotation] == "true" {
return true, types.NamespacedName{
Namespace: vCertificate.Namespace,
Name: vCertificate.Name,
}
} else if pCertificate == nil {
return false, types.NamespacedName{}
}

name := s.nameByIngress(pCertificate)
if name.Name != "" {
return true, name
}

return false, types.NamespacedName{}
}

func (s *certificateSyncer) nameByIngress(pObj client.Object) types.NamespacedName {
vIngress := &networkingv1.Ingress{}
err := clienthelper.GetByIndex(context2.TODO(), s.virtualClient, vIngress, IndexByIngressCertificate, pObj.GetName())
if err == nil && vIngress.Name != "" {
for _, secret := range vIngress.Spec.TLS {
if translate.PhysicalName(secret.SecretName, vIngress.Namespace) == pObj.GetName() {
return types.NamespacedName{
Name: secret.SecretName,
Namespace: vIngress.Namespace,
}
}
}
}

return types.NamespacedName{}
}

func (s *certificateSyncer) PhysicalToVirtual(pObj client.Object) types.NamespacedName {
namespacedName := s.NamespacedTranslator.PhysicalToVirtual(pObj)
if namespacedName.Name != "" {
return namespacedName
}

namespacedName = s.nameByIngress(pObj)
if namespacedName.Name != "" {
return namespacedName
}

return types.NamespacedName{}
}

func certificateNamesFromIngress(ingress *networkingv1.Ingress) []string {
certificates := []string{}

// Do not include certificate.Spec.SecretName here as this will be handled separately by a different controller
if ingress.Annotations != nil && (ingress.Annotations[constants.IssuerAnnotation] != "" || ingress.Annotations[constants.ClusterIssuerAnnotation] != "") {
for _, secret := range ingress.Spec.TLS {
if secret.SecretName == "" {
continue
}

certificates = append(certificates, translate.PhysicalName(secret.SecretName, ingress.Namespace))
certificates = append(certificates, ingress.Namespace+"/"+secret.SecretName)
}
}
return certificates
}

func mapIngresses(obj client.Object) []reconcile.Request {
ingress, ok := obj.(*networkingv1.Ingress)
if !ok {
return nil
}

requests := []reconcile.Request{}
names := certificateNamesFromIngress(ingress)
for _, name := range names {
splitted := strings.Split(name, "/")
if len(splitted) == 2 {
requests = append(requests, reconcile.Request{
NamespacedName: types.NamespacedName{
Namespace: splitted[0],
Name: splitted[1],
},
})
}
}

return requests
}
58 changes: 57 additions & 1 deletion pkg/syncers/certificates/syncer.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,15 @@ import (
func New(ctx *context.RegisterContext) syncer.Syncer {
return &certificateSyncer{
NamespacedTranslator: translator.NewNamespacedTranslator(ctx, "certificate", &certmanagerv1.Certificate{}),

virtualClient: ctx.VirtualManager.GetClient(),
}
}

type certificateSyncer struct {
translator.NamespacedTranslator

virtualClient client.Client
}

var _ syncer.Initializer = &certificateSyncer{}
Expand All @@ -28,7 +32,17 @@ func (s *certificateSyncer) Init(ctx *context.RegisterContext) error {
}

func (s *certificateSyncer) SyncDown(ctx *context.SyncContext, vObj client.Object) (ctrl.Result, error) {
return s.SyncDownCreate(ctx, vObj, s.translate(vObj.(*certmanagerv1.Certificate)))
vCertificate := vObj.(*certmanagerv1.Certificate)

// was certificate created by ingress?
shouldSync, _ := s.shouldSyncBackwards(nil, vCertificate)
if shouldSync {
// delete here as certificate is no longer needed
ctx.Log.Infof("delete virtual certificate %s/%s, because physical got deleted", vObj.GetNamespace(), vObj.GetName())
return ctrl.Result{}, ctx.VirtualClient.Delete(ctx.Context, vObj)
}

return s.SyncDownCreate(ctx, vObj, s.translate(vCertificate))
}

func (s *certificateSyncer) Sync(ctx *context.SyncContext, pObj client.Object, vObj client.Object) (ctrl.Result, error) {
Expand All @@ -48,6 +62,48 @@ func (s *certificateSyncer) Sync(ctx *context.SyncContext, pObj client.Object, v
return ctrl.Result{}, nil
}

// was certificate created by ingress?
shouldSync, _ := s.shouldSyncBackwards(pCertificate, vCertificate)
if shouldSync {
updated, err := s.translateUpdateBackwards(pCertificate, vCertificate)
if err != nil {
return ctrl.Result{}, err
}
if updated != nil {
ctx.Log.Infof("update virtual certificate %s/%s, because spec is out of sync", vCertificate.Namespace, vCertificate.Name)
return ctrl.Result{}, s.virtualClient.Update(ctx.Context, updated)
}

return ctrl.Result{}, nil
}

// did the certificate change?
return s.SyncDownUpdate(ctx, vObj, s.translateUpdate(pCertificate, vCertificate))
}

var _ syncer.UpSyncer = &certificateSyncer{}

func (s *certificateSyncer) SyncUp(ctx *context.SyncContext, pObj client.Object) (ctrl.Result, error) {
pCertificate := pObj.(*certmanagerv1.Certificate)

// was certificate created by ingress?
shouldSync, vName := s.shouldSyncBackwards(pCertificate, nil)
if shouldSync {
ctx.Log.Infof("create virtual certificate %s/%s, because physical is there and virtual is missing", vName.Namespace, vName.Name)
vCertificate, err := s.translateBackwards(pCertificate, vName)
if err != nil {
return ctrl.Result{}, err
}

return ctrl.Result{}, s.virtualClient.Create(ctx.Context, vCertificate)
}

managed, err := s.IsManaged(pObj)
if err != nil {
return ctrl.Result{}, err
}
if !managed {
return ctrl.Result{}, nil
}
return syncer.DeleteObject(ctx, pObj)
}
81 changes: 81 additions & 0 deletions pkg/syncers/certificates/translate.go
Original file line number Diff line number Diff line change
@@ -1,9 +1,15 @@
package certificates

import (
"context"
certmanagerv1 "github.com/cert-manager/cert-manager/pkg/apis/certmanager/v1"
"github.com/loft-sh/vcluster-cert-manager-plugin/pkg/constants"
"github.com/loft-sh/vcluster-sdk/clienthelper"
"github.com/loft-sh/vcluster-sdk/syncer/translator"
"github.com/loft-sh/vcluster-sdk/translate"
"k8s.io/apimachinery/pkg/api/equality"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/types"
"sigs.k8s.io/controller-runtime/pkg/client"
)

Expand Down Expand Up @@ -56,6 +62,81 @@ func rewriteSpec(vObjSpec *certmanagerv1.CertificateSpec, namespace string) *cer
return vObjSpec
}

func (s *certificateSyncer) translateBackwards(pObj *certmanagerv1.Certificate, name types.NamespacedName) (*certmanagerv1.Certificate, error) {
vCertificate := &certmanagerv1.Certificate{
ObjectMeta: metav1.ObjectMeta{
Name: name.Name,
Namespace: name.Namespace,
Labels: pObj.Labels,
Annotations: map[string]string{},
},
Spec: certmanagerv1.CertificateSpec{},
}
for k, v := range pObj.Annotations {
vCertificate.Annotations[k] = v
}
vCertificate.Annotations[constants.BackwardSyncAnnotation] = "true"

// rewrite spec
vCertificateSpec, err := s.rewriteSpecBackwards(&pObj.Spec, name)
if err != nil {
return nil, err
}

vCertificate.Spec = *vCertificateSpec
return vCertificate, nil
}

func (s *certificateSyncer) translateUpdateBackwards(pObj, vObj *certmanagerv1.Certificate) (*certmanagerv1.Certificate, error) {
var updated *certmanagerv1.Certificate

// check annotations & labels
if !equality.Semantic.DeepEqual(pObj.Labels, vObj.Labels) {
updated = newIfNil(updated, vObj)
updated.Labels = pObj.Labels
}

// check annotations
newAnnotations := map[string]string{}
for k, v := range pObj.Annotations {
newAnnotations[k] = v
}
newAnnotations[constants.BackwardSyncAnnotation] = "true"
if !equality.Semantic.DeepEqual(newAnnotations, vObj.Annotations) {
updated = newIfNil(updated, vObj)
updated.Annotations = newAnnotations
}

// update spec
vSpec, err := s.rewriteSpecBackwards(&pObj.Spec, types.NamespacedName{Namespace: vObj.Namespace, Name: vObj.Name})
if err != nil {
return nil, err
}
if !equality.Semantic.DeepEqual(*vSpec, vObj.Spec) {
updated = newIfNil(updated, vObj)
updated.Spec = *vSpec
}

return updated, nil
}

func (s *certificateSyncer) rewriteSpecBackwards(pObjSpec *certmanagerv1.CertificateSpec, vName types.NamespacedName) (*certmanagerv1.CertificateSpec, error) {
vObjSpec := pObjSpec.DeepCopy()

// find issuer
vObjSpec.SecretName = vName.Name
if vObjSpec.IssuerRef.Kind == "Issuer" {
// try to find issuer
issuer := &certmanagerv1.Issuer{}
err := clienthelper.GetByIndex(context.TODO(), s.virtualClient, issuer, translator.IndexByPhysicalName, vObjSpec.IssuerRef.Name)
if err == nil {
vObjSpec.IssuerRef.Name = issuer.Name
}
}

return vObjSpec, nil
}

func newIfNil(updated *certmanagerv1.Certificate, pObj *certmanagerv1.Certificate) *certmanagerv1.Certificate {
if updated == nil {
return pObj.DeepCopy()
Expand Down
Loading

0 comments on commit 2d7b3df

Please sign in to comment.