Skip to content

Commit

Permalink
wip: sigining key secret generation
Browse files Browse the repository at this point in the history
  • Loading branch information
katallaxie authored Dec 9, 2024
1 parent 57ba9b4 commit d9c0263
Show file tree
Hide file tree
Showing 11 changed files with 221 additions and 245 deletions.
2 changes: 1 addition & 1 deletion api/v1alpha1/nats_operator_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ type NatsOperatorSpec struct {
// PublicKey is the public key that should be used to verify the JWT
PublicKey corev1.SecretReference `json:"public_key,omitempty"`
// SigningKeys is a list of references to secrets that contain the signing keys
SigningKeys []NatsSigningKey `json:"signing_keys,omitempty"`
SigningKeys []NatsSigningKeyReference `json:"signing_keys,omitempty"`
}

type NatsOperatorStatus struct {
Expand Down
36 changes: 34 additions & 2 deletions api/v1alpha1/nats_signing_key_types.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package v1alpha1

import (
"github.com/nats-io/nkeys"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

Expand All @@ -14,10 +15,24 @@ const (
SigningKeyPhaseFailed SigningKeyPhase = "Failed"
)

// SigningKeyType is a type that represents the type of the NKey.
//
// +enum
// +kubebuilder:validation:Enum={Operator,Account,User}
type SigningKeyType string

// NatsSigningKeyReference is a reference to a NatsSigningKey
type NatsSigningKeyReference struct {
// Name is the name of the NatsSigningKey
Name string `json:"name"`
}

// NatsSigningKeySpec defines the desired state of SigningKey
type NatsSigningKeySpec struct {
// DeleteSecret is a flag that indicates if the secret should be deleted.
DeleteSecret bool `json:"deleteSecret,omitempty"`
// Type is the type of the NKey.
Type SigningKeyType `json:"type"`
// PreventDeletion is a flag that indicates if the key should be locked to prevent deletion.
PreventDeletion bool `json:"prevent_deletion,omitempty"`
}

// NatsSigningKeyStatus defines the observed state of SigningKey
Expand Down Expand Up @@ -46,6 +61,23 @@ type NatsSigningKey struct {
Status NatsSigningKeyStatus `json:"status,omitempty"`
}

// Keys returns the keys of the NKey.
func (sk *NatsSigningKey) Keys() (nkeys.KeyPair, error) {
var keys nkeys.KeyPair
var err error

switch sk.Spec.Type {
case "Operator":
keys, err = nkeys.CreateOperator()
case "Account":
keys, err = nkeys.CreateAccount()
case "User":
keys, err = nkeys.CreateUser()
}

return keys, err
}

//+kubebuilder:object:root=true

// NatsSigningKeyList contains a list of NatsSigningKey
Expand Down
21 changes: 17 additions & 4 deletions api/v1alpha1/zz_generated.deepcopy.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 5 additions & 0 deletions cmd/operator/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,11 @@ func setupControllers(mgr ctrl.Manager) error {
return err
}

err = controllers.NewNatsSigningKeyReconciler(mgr).SetupWithManager(mgr)
if err != nil {
return err
}

return nil
}

Expand Down
132 changes: 119 additions & 13 deletions controllers/natssigningkey_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,21 @@ package controllers

import (
"context"
"fmt"
"math"
"time"

"k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/client-go/tools/record"
ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
"sigs.k8s.io/controller-runtime/pkg/log"
"sigs.k8s.io/controller-runtime/pkg/predicate"
"sigs.k8s.io/controller-runtime/pkg/reconcile"

"github.com/nats-io/nkeys"
"github.com/zeiss/natz-operator/api/v1alpha1"
natsv1alpha1 "github.com/zeiss/natz-operator/api/v1alpha1"
"github.com/zeiss/natz-operator/pkg/status"
"github.com/zeiss/pkg/conv"
Expand All @@ -19,6 +25,11 @@ import (
corev1 "k8s.io/api/core/v1"
)

const (
EventReasonSigningKeyFailed EventReason = "SigningKeyFailed"
EventReasonSigningKeySynchronized EventReason = "SigningKeySynchronized"
)

// NatsSigningKeyReconciler ...
type NatsSigningKeyReconciler struct {
client.Client
Expand All @@ -43,10 +54,6 @@ func NewNatsSigningKeyReconciler(mgr ctrl.Manager) *NatsSigningKeyReconciler {
// Reconcile ...
// nolint:gocyclo
func (r *NatsSigningKeyReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
log := log.FromContext(ctx)

log.Info("reconcile signing key", "name", req.Name, "namespace", req.Namespace)

sk := &natsv1alpha1.NatsSigningKey{}
if err := r.Get(ctx, req.NamespacedName, sk); err != nil {
// Request object not found, could have been deleted after reconcile request.
Expand All @@ -60,23 +67,35 @@ func (r *NatsSigningKeyReconciler) Reconcile(ctx context.Context, req ctrl.Reque
return r.reconcileResources(ctx, sk)
}

func (r *NatsSigningKeyReconciler) reconcileResources(ctx context.Context, sk *natsv1alpha1.NatsSigningKey) (ctrl.Result, error) {
log := log.FromContext(ctx)
func (r *NatsSigningKeyReconciler) reconcileSigningKey(ctx context.Context, obj *natsv1alpha1.NatsSigningKey) error {
if !controllerutil.ContainsFinalizer(obj, natsv1alpha1.FinalizerName) {
controllerutil.AddFinalizer(obj, natsv1alpha1.FinalizerName)
return r.Update(ctx, obj)
}

return nil
}

func (r *NatsSigningKeyReconciler) reconcileResources(ctx context.Context, sk *natsv1alpha1.NatsSigningKey) (ctrl.Result, error) {
err := r.reconcileStatus(ctx, sk)
if err != nil {
log.Error(err, "failed to reconcile status", "name", sk.Name, "namespace", sk.Namespace)
return ctrl.Result{}, err
}

err = r.reconcileSigningKey(ctx, sk)
if err != nil {
return ctrl.Result{}, err
}

err = r.reconcileSecret(ctx, sk)
if err != nil {
return ctrl.Result{}, err
}

return r.ManageSuccess(ctx, sk)
}

func (r *NatsSigningKeyReconciler) reconcileStatus(ctx context.Context, sk *natsv1alpha1.NatsSigningKey) error {
log := log.FromContext(ctx)

log.Info("reconcile status", "name", sk.Name, "namespace", sk.Namespace)

phase := natsv1alpha1.SigningKeyPhaseSynchronized

if sk.Status.Phase != phase {
Expand All @@ -88,6 +107,75 @@ func (r *NatsSigningKeyReconciler) reconcileStatus(ctx context.Context, sk *nats
return nil
}

func (r *NatsSigningKeyReconciler) reconcileSecret(ctx context.Context, sk *natsv1alpha1.NatsSigningKey) error {

Check failure on line 110 in controllers/natssigningkey_controller.go

View workflow job for this annotation

GitHub Actions / lint

cyclomatic complexity 13 of func `(*NatsSigningKeyReconciler).reconcileSecret` is high (> 10) (gocyclo)
secret := &corev1.Secret{}
secretName := client.ObjectKey{
Namespace: sk.Namespace,
Name: sk.Name,
}

err := r.Get(ctx, secretName, secret)
if !errors.IsNotFound(err) {
return err
}

secret.Namespace = sk.Namespace
secret.Name = sk.Name
secret.Type = "natz.zeiss.com/nats-signing-key"
secret.Annotations = map[string]string{
v1alpha1.OwnerAnnotation: fmt.Sprintf("%s/%s", secret.Namespace, secret.Name),
}

var keys nkeys.KeyPair
switch sk.Spec.Type {
case "Operator":
keys, err = nkeys.CreateOperator()
if err != nil {
return err
}
case "Account":
keys, err = nkeys.CreateAccount()
if err != nil {
return err
}
case "User":
keys, err = nkeys.CreateUser()
if err != nil {
return err
}
}

seed, err := keys.Seed()
if err != nil {
return err
}

public, err := keys.PublicKey()
if err != nil {
return err
}

data := map[string][]byte{}
data[OPERATOR_SEED_KEY] = seed
data[OPERATOR_PUBLIC_KEY] = []byte(public)

op, err := controllerutil.CreateOrUpdate(ctx, r.Client, secret, func() error {
secret.Data = data

return controllerutil.SetControllerReference(sk, secret, r.Scheme)
})
if err != nil {
r.Recorder.Event(sk, corev1.EventTypeWarning, conv.String(EventReasonSigningKeyFailed), "secret creation failed")
return err
}

if op == controllerutil.OperationResultCreated || op == controllerutil.OperationResultUpdated {
r.Recorder.Event(sk, corev1.EventTypeNormal, conv.String(EventReasonSigningKeySynchronized), "secret created or updated")
}

return nil
}

func (r *NatsSigningKeyReconciler) reconcileDelete(ctx context.Context, sk *natsv1alpha1.NatsSigningKey) (ctrl.Result, error) {
// Remove our finalizer from the list.
controllerutil.RemoveFinalizer(sk, natsv1alpha1.FinalizerName)
Expand Down Expand Up @@ -137,11 +225,29 @@ func (r *NatsSigningKeyReconciler) ManageSuccess(ctx context.Context, obj *natsv
return ctrl.Result{Requeue: true}, nil
}

r.Recorder.Event(obj, corev1.EventTypeNormal, conv.String(EventReasonOperatorSynchronized), "operator synchronized")
r.Recorder.Event(obj, corev1.EventTypeNormal, conv.String(EventReasonOperatorSynchronized), "signing key synchronized")

return ctrl.Result{}, nil
}

// ManageError ...
func (r *NatsSigningKeyReconciler) ManageError(ctx context.Context, obj *natsv1alpha1.NatsSigningKey, err error) (ctrl.Result, error) {
status.SetNatzSigningKeyCondition(obj, status.NewSigningKeyFailedCondition(obj, err))

if err := r.Client.Status().Update(ctx, obj); err != nil {
return ctrl.Result{Requeue: true, RequeueAfter: time.Second}, err
}

r.Recorder.Event(obj, corev1.EventTypeWarning, conv.String(EventReasonSigningKeyFailed), "secret synchronization failed")

var retryInterval time.Duration

return reconcile.Result{
RequeueAfter: time.Duration(math.Min(float64(retryInterval.Nanoseconds()*2), float64(time.Hour.Nanoseconds()*6))),
Requeue: true,
}, nil
}

// SetupWithManager sets up the controller with the Manager.
func (r *NatsSigningKeyReconciler) SetupWithManager(mgr ctrl.Manager) error {
return ctrl.NewControllerManagedBy(mgr).
Expand Down
10 changes: 10 additions & 0 deletions examples/operator.yaml
Original file line number Diff line number Diff line change
@@ -1,5 +1,15 @@
apiVersion: natz.zeiss.com/v1alpha1
kind: NatsSigningKey
metadata:
name: natsoperator-sample
spec:
prevent_deletion: true
type: Operator
---
apiVersion: natz.zeiss.com/v1alpha1
kind: NatsOperator
metadata:
name: natsoperator-sample
spec:
signing_keys:
- name: natsoperator-sample
Loading

0 comments on commit d9c0263

Please sign in to comment.