Skip to content

Commit

Permalink
Enable filtering entry id's by prefix (#325)
Browse files Browse the repository at this point in the history
* Enable filtering entry id's by prefix

This enables peaceful coexistence of multiple spire-controller-managers
or other managers and manual entries in the same spire-server. Also
provides a cleanup option for migration.

Signed-off-by: Kevin Fox <[email protected]>

* Fix lint

Signed-off-by: Kevin Fox <[email protected]>

* Apply suggestions from code review

Co-authored-by: Andrew Harding <[email protected]>
Signed-off-by: kfox1111 <[email protected]>

* Incorperate feedback

Signed-off-by: Kevin Fox <[email protected]>

* Make cleanup print nicer

Signed-off-by: Kevin Fox <[email protected]>

---------

Signed-off-by: Kevin Fox <[email protected]>
Signed-off-by: kfox1111 <[email protected]>
Co-authored-by: Andrew Harding <[email protected]>
  • Loading branch information
kfox1111 and azdagron authored Apr 2, 2024
1 parent d168d2b commit 88c20a5
Show file tree
Hide file tree
Showing 4 changed files with 94 additions and 15 deletions.
10 changes: 10 additions & 0 deletions api/v1alpha1/controllermanagerconfig_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,16 @@ type ControllerManagerConfigurationSpec struct {
// If specified, only syncs the specified CR types. Defaults to all.
// +optional
Reconcile *ReconcileConfig `json:"reconcile,omitempty"`

// If specified, prefixes each entry id with `<prefix>.`. Entries without the Prefix will be ignored (except ones marked for cleanup, see EntryIDPrefixCleanup).
// +optiional
EntryIDPrefix string `json:"entryIDPrefix,omitempty"`

// If specified, entries with the specified prefix will be removed. If set to "" it will clean up all unprefixed entries.
// It can not be set to the same value as EntryIDPrefix.
// Generally useful when switching from nonprefixed to prefixed, or between two different prefixes.
// +optiional
EntryIDPrefixCleanup *string `json:"entryIDPrefixCleanup,omitempty"`
}

// ReconcileConfig configuration used to enable/disable syncing various types
Expand Down
5 changes: 5 additions & 0 deletions api/v1alpha1/zz_generated.deepcopy.go

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

46 changes: 34 additions & 12 deletions cmd/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,13 @@ func main() {
}
}

func addDotSuffix(val string) string {
if val != "" && !strings.HasSuffix(val, ".") {
val += "."
}
return val
}

func parseConfig() (Config, error) {
var retval Config
var configFileFlag string
Expand Down Expand Up @@ -179,6 +186,17 @@ func parseConfig() (Config, error) {
retval.reconcile = *retval.ctrlConfig.Reconcile
}

retval.ctrlConfig.EntryIDPrefix = addDotSuffix(retval.ctrlConfig.EntryIDPrefix)

printCleanup := "<unset>"
if retval.ctrlConfig.EntryIDPrefixCleanup != nil {
printCleanup = *retval.ctrlConfig.EntryIDPrefixCleanup
*retval.ctrlConfig.EntryIDPrefixCleanup = addDotSuffix(*retval.ctrlConfig.EntryIDPrefixCleanup)
if retval.ctrlConfig.EntryIDPrefix != "" && retval.ctrlConfig.EntryIDPrefix == *retval.ctrlConfig.EntryIDPrefixCleanup {
return retval, fmt.Errorf("if entryIDPrefixCleanup is specified, it can not be the same value as entryIDPrefix")
}
}

setupLog.Info("Config loaded",
"cluster name", retval.ctrlConfig.ClusterName,
"cluster domain", retval.ctrlConfig.ClusterDomain,
Expand All @@ -190,7 +208,9 @@ func parseConfig() (Config, error) {
"handle crs without class name", retval.ctrlConfig.WatchClassless,
"reconcile ClusterSPIFFEIDs", retval.reconcile.ClusterSPIFFEIDs,
"reconcile ClusterFederatedTrustDomains", retval.reconcile.ClusterFederatedTrustDomains,
"reconcile ClusterStaticEntries", retval.reconcile.ClusterStaticEntries)
"reconcile ClusterStaticEntries", retval.reconcile.ClusterStaticEntries,
"entryIDPrefix", retval.ctrlConfig.EntryIDPrefix,
"entryIDPrefixCleanup", printCleanup)

switch {
case retval.ctrlConfig.TrustDomain == "":
Expand Down Expand Up @@ -297,17 +317,19 @@ func run(mainConfig Config) (err error) {
var entryReconciler reconciler.Reconciler
if mainConfig.reconcile.ClusterSPIFFEIDs || mainConfig.reconcile.ClusterStaticEntries {
entryReconciler = spireentry.Reconciler(spireentry.ReconcilerConfig{
TrustDomain: trustDomain,
ClusterName: mainConfig.ctrlConfig.ClusterName,
ClusterDomain: mainConfig.ctrlConfig.ClusterDomain,
K8sClient: mgr.GetClient(),
EntryClient: spireClient,
IgnoreNamespaces: mainConfig.ignoreNamespacesRegex,
GCInterval: mainConfig.ctrlConfig.GCInterval,
ClassName: mainConfig.ctrlConfig.ClassName,
WatchClassless: mainConfig.ctrlConfig.WatchClassless,
ParentIDTemplate: mainConfig.parentIDTemplate,
Reconcile: mainConfig.reconcile,
TrustDomain: trustDomain,
ClusterName: mainConfig.ctrlConfig.ClusterName,
ClusterDomain: mainConfig.ctrlConfig.ClusterDomain,
K8sClient: mgr.GetClient(),
EntryClient: spireClient,
IgnoreNamespaces: mainConfig.ignoreNamespacesRegex,
GCInterval: mainConfig.ctrlConfig.GCInterval,
ClassName: mainConfig.ctrlConfig.ClassName,
WatchClassless: mainConfig.ctrlConfig.WatchClassless,
ParentIDTemplate: mainConfig.parentIDTemplate,
Reconcile: mainConfig.reconcile,
EntryIDPrefix: mainConfig.ctrlConfig.EntryIDPrefix,
EntryIDPrefixCleanup: mainConfig.ctrlConfig.EntryIDPrefixCleanup,
})
}

Expand Down
48 changes: 45 additions & 3 deletions pkg/spireentry/reconciler.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import (
"context"
"crypto/sha256"
"encoding/hex"
"fmt"
"io"
"regexp"
"sort"
Expand All @@ -28,6 +29,7 @@ import (
"time"

"github.com/go-logr/logr"
"github.com/google/uuid"
"github.com/spiffe/go-spiffe/v2/spiffeid"
"google.golang.org/grpc/codes"
corev1 "k8s.io/api/core/v1"
Expand Down Expand Up @@ -68,6 +70,8 @@ type ReconcilerConfig struct {
WatchClassless bool
ParentIDTemplate *template.Template
Reconcile spirev1alpha1.ReconcileConfig
EntryIDPrefix string
EntryIDPrefixCleanup *string

// GCInterval how long to sit idle (i.e. untriggered) before doing
// another reconcile.
Expand Down Expand Up @@ -101,7 +105,7 @@ func (r *entryReconciler) reconcile(ctx context.Context) {
unsupportedFields := r.unsupportedFields

// Load current entries from SPIRE server.
currentEntries, err := r.listEntries(ctx)
currentEntries, deleteOnlyEntries, err := r.listEntries(ctx)
if err != nil {
log.Error(err, "Failed to list SPIRE entries")
return
Expand Down Expand Up @@ -156,6 +160,9 @@ func (r *entryReconciler) reconcile(ctx context.Context) {
// drop the current entry from the list so it isn't added to the
// "to delete" list.
if len(s.Current) == 0 {
if preferredEntry.Entry.ID == "" && r.config.EntryIDPrefix != "" {
preferredEntry.Entry.ID = fmt.Sprintf("%s%s", r.config.EntryIDPrefix, uuid.New())
}
toCreate = append(toCreate, preferredEntry)
} else {
preferredEntry.Entry.ID = s.Current[0].ID
Expand All @@ -172,6 +179,7 @@ func (r *entryReconciler) reconcile(ctx context.Context) {
toDelete = append(toDelete, filterJoinTokenEntries(s.Current)...)
}

toDelete = append(toDelete, deleteOnlyEntries...)
if len(toDelete) > 0 {
r.deleteEntries(ctx, toDelete)
}
Expand Down Expand Up @@ -250,9 +258,43 @@ func (r *entryReconciler) recalculateUnsupportFields(ctx context.Context, log lo
r.nextGetUnsupportedFields = time.Now().Add(10 * time.Minute)
}

func (r *entryReconciler) listEntries(ctx context.Context) ([]spireapi.Entry, error) {
func (r *entryReconciler) shouldProcessOrDeleteEntryID(entry spireapi.Entry) (bool, bool) {
if r.config.EntryIDPrefix == "" {
return true, false
}
if strings.HasPrefix(entry.ID, r.config.EntryIDPrefix) {
return true, false
}
if r.config.EntryIDPrefixCleanup != nil {
cleanupPrefix := *r.config.EntryIDPrefixCleanup
if cleanupPrefix == "" {
return false, !strings.Contains(entry.ID, ".")
}
if strings.HasPrefix(entry.ID, cleanupPrefix) {
return false, true
}
}
return false, false
}

func (r *entryReconciler) listEntries(ctx context.Context) ([]spireapi.Entry, []spireapi.Entry, error) {
// TODO: cache?
return r.config.EntryClient.ListEntries(ctx)
var deleteOnlyEntries []spireapi.Entry
var currentEntries []spireapi.Entry
tmpvals, err := r.config.EntryClient.ListEntries(ctx)
if err != nil {
return currentEntries, deleteOnlyEntries, err
}
for _, value := range tmpvals {
proc, del := r.shouldProcessOrDeleteEntryID(value)
if proc {
currentEntries = append(currentEntries, value)
}
if del {
deleteOnlyEntries = append(deleteOnlyEntries, value)
}
}
return currentEntries, deleteOnlyEntries, nil
}

func (r *entryReconciler) getUnsupportedFields(ctx context.Context) (map[spireapi.Field]struct{}, error) {
Expand Down

0 comments on commit 88c20a5

Please sign in to comment.