Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add option for custom annotations and labels on sealing keypairs #1250

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions cmd/controller/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@ func bindControllerFlags(f *controller.Flags, fs *flag.FlagSet) {
fs.StringVar(&f.LabelSelector, "label-selector", "", "Label selector which can be used to filter sealed secrets.")
fs.IntVar(&f.RateLimitPerSecond, "rate-limit", 2, "Number of allowed sustained request per second for verify endpoint")
fs.IntVar(&f.RateLimitBurst, "rate-limit-burst", 2, "Number of requests allowed to exceed the rate limit per second for verify endpoint")
fs.StringVar(&f.PrivateKeyAnnotations, "privatekey-annotations", "", "Comma-separated list of additional annotations to be put on renewed sealing keys.")
fs.StringVar(&f.PrivateKeyLabels, "privatekey-labels", "", "Comma-separated list of additional labels to be put on renewed sealing keys.")

fs.BoolVar(&f.OldGCBehavior, "old-gc-behavior", false, "Revert to old GC behavior where the controller deletes secrets instead of delegating that to k8s itself.")

Expand Down
4 changes: 3 additions & 1 deletion helm/sealed-secrets/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,9 @@ The command removes all the Kubernetes components associated with the chart and
| `keyrenewperiod` | Specifies key renewal period. Default 30 days | `""` |
| `rateLimit` | Number of allowed sustained request per second for verify endpoint | `""` |
| `rateLimitBurst` | Number of requests allowed to exceed the rate limit per second for verify endpoint | `""` |
| `additionalNamespaces` | List of namespaces used to manage the Sealed Secrets | `[]` |
| `additionalNamespaces` | List of namespaces used to manage the Sealed Secrets | `[]`
| `privateKeyAnnotations` | Map of annotations to be set on the sealing keypairs | `{}` |
| `privateKeyLabels` | Map of labels to be set on the sealing keypairs | `{}` |
| `logInfoStdout` | Specifies whether the Sealed Secrets controller will log info to stdout | `false` |
| `command` | Override default container command | `[]` |
| `args` | Override default container args | `[]` |
Expand Down
22 changes: 22 additions & 0 deletions helm/sealed-secrets/templates/deployment.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,28 @@ spec:
- --additional-namespaces
- {{ join "," .Values.additionalNamespaces | quote }}
{{- end }}
{{- if $.Values.privateKeyAnnotations }}
{{- $privatekeyAnnotations := ""}}
{{- range $k, $v := $.Values.privateKeyAnnotations }}
{{- if not (and $v (kindIs "string" $v)) }}
{{ fail "Annotation values have to be strings"}}
{{- end }}
{{- $privatekeyAnnotations = printf "%s=%s,%s" $k $v $privatekeyAnnotations}}
{{- end }}
- --privatekey-annotations
- {{ trimSuffix "," $privatekeyAnnotations | quote }}
{{- end }}
{{- if $.Values.privateKeyLabels }}
{{- $privateKeyLabels := ""}}
{{- range $k, $v := $.Values.privateKeyLabels }}
{{- if not (and $v (kindIs "string" $v)) }}
{{ fail "Label values have to be strings"}}
{{- end }}
{{- $privateKeyLabels = printf "%s=%s,%s" $k $v $privateKeyLabels}}
{{- end }}
- --privatekey-labels
- {{ trimSuffix "," $privateKeyLabels | quote }}
{{- end }}
{{- if .Values.logInfoStdout }}
- --log-info-stdout
{{- end }}
Expand Down
6 changes: 6 additions & 0 deletions helm/sealed-secrets/values.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,12 @@ rateLimitBurst: ""
## @param additionalNamespaces List of namespaces used to manage the Sealed Secrets
##
additionalNamespaces: []
## @param privateKeyAnnotations Map of annotations to be set on the sealing keypairs
##
privateKeyAnnotations: {}
## @param privateKeyLabels Map of labels to be set on the sealing keypairs
##
privateKeyLabels: {}
## @param logInfoStdout Specifies whether the Sealed Secrets controller will log info to stdout
##
logInfoStdout: false
Expand Down
4 changes: 2 additions & 2 deletions pkg/controller/keyregistry.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,13 +47,13 @@ func NewKeyRegistry(client kubernetes.Interface, namespace, keyPrefix, keyLabel
}
}

func (kr *KeyRegistry) generateKey(ctx context.Context, validFor time.Duration, cn string) (string, error) {
func (kr *KeyRegistry) generateKey(ctx context.Context, validFor time.Duration, cn string, privateKeyAnnotations string, privateKeyLabels string) (string, error) {
key, cert, err := generatePrivateKeyAndCert(kr.keysize, validFor, cn)
if err != nil {
return "", err
}
certs := []*x509.Certificate{cert}
generatedName, err := writeKey(ctx, kr.client, key, certs, kr.namespace, kr.keyLabel, kr.keyPrefix)
generatedName, err := writeKey(ctx, kr.client, key, certs, kr.namespace, kr.keyLabel, kr.keyPrefix, privateKeyAnnotations, privateKeyLabels)
if err != nil {
return "", err
}
Expand Down
37 changes: 31 additions & 6 deletions pkg/controller/keys.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"crypto/x509"
"encoding/pem"
"errors"
"strings"
"time"

"github.com/bitnami-labs/sealed-secrets/pkg/crypto"
Expand Down Expand Up @@ -52,7 +53,7 @@ func writeKeyWithCreationTime(t metav1.Time) writeKeyOpt {
return func(opts *writeKeyOpts) { opts.creationTime = t }
}

func writeKey(ctx context.Context, client kubernetes.Interface, key *rsa.PrivateKey, certs []*x509.Certificate, namespace, label, prefix string, optSetters ...writeKeyOpt) (string, error) {
func writeKey(ctx context.Context, client kubernetes.Interface, key *rsa.PrivateKey, certs []*x509.Certificate, namespace, krLabel, prefix string, additionalAnnotations string, additionalLabels string, optSetters ...writeKeyOpt) (string, error) {
var opts writeKeyOpts
for _, o := range optSetters {
o(&opts)
Expand All @@ -62,13 +63,37 @@ func writeKey(ctx context.Context, client kubernetes.Interface, key *rsa.Private
for _, cert := range certs {
certbytes = append(certbytes, pem.EncodeToMemory(&pem.Block{Type: certUtil.CertificateBlockType, Bytes: cert.Raw})...)
}

labels := map[string]string{
krLabel: "active",
}

annotations := map[string]string{}

if additionalLabels != "" {
for _, label := range removeDuplicates(strings.Split(additionalLabels, ",")) {
key := strings.Split(label, "=")[0]
value := strings.Split(label, "=")[1]
if key != krLabel {
labels[key] = value
}
}
}

if additionalAnnotations != "" {
for _, label := range removeDuplicates(strings.Split(additionalAnnotations, ",")) {
key := strings.Split(label, "=")[0]
value := strings.Split(label, "=")[1]
annotations[key] = value
}
}

secret := v1.Secret{
ObjectMeta: metav1.ObjectMeta{
Namespace: namespace,
GenerateName: prefix,
Labels: map[string]string{
label: "active",
},
Namespace: namespace,
GenerateName: prefix,
Labels: labels,
Annotations: annotations,
CreationTimestamp: opts.creationTime,
},
Data: map[string][]byte{
Expand Down
42 changes: 40 additions & 2 deletions pkg/controller/keys_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,16 @@ import (
"io"
mathrand "math/rand"
"reflect"
"strings"
"testing"
"time"

"github.com/bitnami-labs/sealed-secrets/pkg/crypto"
v1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/client-go/kubernetes/fake"
ktesting "k8s.io/client-go/testing"
certUtil "k8s.io/client-go/util/cert"
"k8s.io/client-go/util/keyutil"
)
Expand Down Expand Up @@ -86,7 +89,12 @@ func TestWriteKey(t *testing.T) {

client := fake.NewSimpleClientset()

_, err = writeKey(ctx, client, key, []*x509.Certificate{cert}, "myns", "label", "mykey")
namespace := "myns"
defaultLabel := "default-label"
myKey := "mykey"
additionalAnnotations := "testAnnotation1=additional.annotation,test.annotation.2=test/2"
additionalLabels := "testLabel1=additional.label,test.label.2=test/2"
_, err = writeKey(ctx, client, key, []*x509.Certificate{cert}, namespace, defaultLabel, myKey, additionalAnnotations, additionalLabels)
if err != nil {
t.Errorf("writeKey() failed with: %v", err)
}
Expand All @@ -95,7 +103,37 @@ func TestWriteKey(t *testing.T) {

if a := findAction(client, "create", "secrets"); a == nil {
t.Errorf("writeKey didn't create a secret")
} else if a.GetNamespace() != "myns" {
} else if a.GetNamespace() != namespace {
t.Errorf("writeKey() created key in wrong namespace!")
}
a := findAction(client, "create", "secrets").(ktesting.CreateActionImpl)
secret, _ := runtime.DefaultUnstructuredConverter.ToUnstructured(a.Object)
generateName := secret["metadata"].(map[string]interface{})["generateName"].(string)

if generateName != myKey {
t.Errorf("writeKey didn't set the correct name")
}

labels := secret["metadata"].(map[string]interface{})["labels"]
annotations := secret["metadata"].(map[string]interface{})["annotations"]

if labels.(map[string]interface{})[defaultLabel] != "active" {
t.Errorf("writeKey didn't set default label")
}

for _, label := range strings.Split(additionalLabels, ",") {
labelKey := strings.Split(label, "=")[0]
labelValue := strings.Split(label, "=")[1]
if labels.(map[string]interface{})[labelKey] != labelValue {
t.Errorf("writeKey didn't set label " + labelKey + " to value '" + labelValue + "'")
}
}

for _, annotation := range strings.Split(additionalAnnotations, ",") {
annotationKey := strings.Split(annotation, "=")[0]
annotationValue := strings.Split(annotation, "=")[1]
if annotations.(map[string]interface{})[annotationKey] != annotationValue {
t.Errorf("writeKey didn't set annotation '" + annotationKey + "' to value '" + annotationValue + "'")
}
}
}
42 changes: 22 additions & 20 deletions pkg/controller/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,22 +35,24 @@ var (

// Flags to configure the controller
type Flags struct {
KeyPrefix string
KeySize int
ValidFor time.Duration
MyCN string
KeyRenewPeriod time.Duration
AcceptV1Data bool
KeyCutoffTime string
NamespaceAll bool
AdditionalNamespaces string
LabelSelector string
RateLimitPerSecond int
RateLimitBurst int
OldGCBehavior bool
UpdateStatus bool
SkipRecreate bool
LogInfoToStdout bool
KeyPrefix string
KeySize int
ValidFor time.Duration
MyCN string
KeyRenewPeriod time.Duration
AcceptV1Data bool
KeyCutoffTime string
NamespaceAll bool
AdditionalNamespaces string
LabelSelector string
RateLimitPerSecond int
RateLimitBurst int
OldGCBehavior bool
UpdateStatus bool
SkipRecreate bool
LogInfoToStdout bool
PrivateKeyAnnotations string
PrivateKeyLabels string
}

func initKeyPrefix(keyPrefix string) (string, error) {
Expand Down Expand Up @@ -109,18 +111,18 @@ func myNamespace() string {
// Initialises the first key and starts the rotation job. returns an early trigger function.
// A period of 0 deactivates automatic rotation, but manual rotation (e.g. triggered by SIGUSR1)
// is still honoured.
func initKeyRenewal(ctx context.Context, registry *KeyRegistry, period, validFor time.Duration, cutoffTime time.Time, cn string) (func(), error) {
func initKeyRenewal(ctx context.Context, registry *KeyRegistry, period, validFor time.Duration, cutoffTime time.Time, cn string, privateKeyAnnotations string, privateKeyLabels string) (func(), error) {
// Create a new key if it's the first key,
// or if it's older than cutoff time.
if len(registry.keys) == 0 || registry.mostRecentKey.orderingTime.Before(cutoffTime) {
if _, err := registry.generateKey(ctx, validFor, cn); err != nil {
if _, err := registry.generateKey(ctx, validFor, cn, privateKeyAnnotations, privateKeyLabels); err != nil {
return nil, err
}
}

// wrapper function to log error thrown by generateKey function
keyGenFunc := func() {
if _, err := registry.generateKey(ctx, validFor, cn); err != nil {
if _, err := registry.generateKey(ctx, validFor, cn, privateKeyAnnotations, privateKeyLabels); err != nil {
log.Errorf("Failed to generate new key : %v\n", err)
}
}
Expand Down Expand Up @@ -181,7 +183,7 @@ func Main(f *Flags, version string) error {
}
}

trigger, err := initKeyRenewal(ctx, keyRegistry, f.KeyRenewPeriod, f.ValidFor, ct, f.MyCN)
trigger, err := initKeyRenewal(ctx, keyRegistry, f.KeyRenewPeriod, f.ValidFor, ct, f.MyCN, f.PrivateKeyAnnotations, f.PrivateKeyLabels)
if err != nil {
return err
}
Expand Down
20 changes: 10 additions & 10 deletions pkg/controller/main_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ func TestInitKeyRegistry(t *testing.T) {
// Add a key to the controller for second test
validFor := time.Hour
cn := "my-cn"
_, err = registry.generateKey(ctx, validFor, cn)
_, err = registry.generateKey(ctx, validFor, cn, "", "")
if err != nil {
t.Fatal(err)
}
Expand Down Expand Up @@ -91,7 +91,7 @@ func TestInitKeyRotation(t *testing.T) {

validFor := time.Hour
cn := "my-cn"
keyGenTrigger, err := initKeyRenewal(ctx, registry, 0, validFor, time.Time{}, cn)
keyGenTrigger, err := initKeyRenewal(ctx, registry, 0, validFor, time.Time{}, cn, "", "")
if err != nil {
t.Fatalf("initKeyRenewal() returned err: %v", err)
}
Expand Down Expand Up @@ -132,7 +132,7 @@ func TestInitKeyRotationTick(t *testing.T) {

validFor := time.Hour
cn := "my-cn"
_, err = initKeyRenewal(ctx, registry, 100*time.Millisecond, validFor, time.Time{}, cn)
_, err = initKeyRenewal(ctx, registry, 100*time.Millisecond, validFor, time.Time{}, cn, "", "")
if err != nil {
t.Fatalf("initKeyRenewal() returned err: %v", err)
}
Expand Down Expand Up @@ -173,7 +173,7 @@ func TestReuseKey(t *testing.T) {
client := fake.NewSimpleClientset()
client.PrependReactor("create", "secrets", generateNameReactor)

_, err = writeKey(ctx, client, key, []*x509.Certificate{cert}, "namespace", SealedSecretsKeyLabel, "prefix")
_, err = writeKey(ctx, client, key, []*x509.Certificate{cert}, "namespace", SealedSecretsKeyLabel, "prefix", "", "")
if err != nil {
t.Errorf("writeKey() failed with: %v", err)
}
Expand All @@ -187,7 +187,7 @@ func TestReuseKey(t *testing.T) {

validFor := time.Hour
cn := "my-cn"
_, err = initKeyRenewal(ctx, registry, 0, validFor, time.Time{}, cn)
_, err = initKeyRenewal(ctx, registry, 0, validFor, time.Time{}, cn, "", "")
if err != nil {
t.Fatalf("initKeyRenewal() returned err: %v", err)
}
Expand Down Expand Up @@ -222,7 +222,7 @@ func TestRenewStaleKey(t *testing.T) {
client := fake.NewSimpleClientset()
client.PrependReactor("create", "secrets", generateNameReactor)

_, err = writeKey(ctx, client, key, []*x509.Certificate{cert}, "namespace", SealedSecretsKeyLabel, "prefix")
_, err = writeKey(ctx, client, key, []*x509.Certificate{cert}, "namespace", SealedSecretsKeyLabel, "prefix", "", "")
if err != nil {
t.Errorf("writeKey() failed with: %v", err)
}
Expand All @@ -234,7 +234,7 @@ func TestRenewStaleKey(t *testing.T) {

validFor := time.Hour
cn := "my-cn"
_, err = initKeyRenewal(ctx, registry, period, validFor, time.Time{}, cn)
_, err = initKeyRenewal(ctx, registry, period, validFor, time.Time{}, cn, "", "")
if err != nil {
t.Fatalf("initKeyRenewal() returned err: %v", err)
}
Expand Down Expand Up @@ -278,7 +278,7 @@ func TestKeyCutoff(t *testing.T) {
client := fake.NewSimpleClientset()
client.PrependReactor("create", "secrets", generateNameReactor)

_, err = writeKey(ctx, client, key, []*x509.Certificate{cert}, "namespace", SealedSecretsKeyLabel, "prefix",
_, err = writeKey(ctx, client, key, []*x509.Certificate{cert}, "namespace", SealedSecretsKeyLabel, "prefix", "", "",
writeKeyWithCreationTime(metav1.NewTime(time.Now().Add(-oldAge))))
if err != nil {
t.Errorf("writeKey() failed with: %v", err)
Expand All @@ -294,7 +294,7 @@ func TestKeyCutoff(t *testing.T) {
// by setting cutoff to "now" we effectively force the creation of a new key.
validFor := time.Hour
cn := "my-cn"
_, err = initKeyRenewal(ctx, registry, period, validFor, time.Now(), cn)
_, err = initKeyRenewal(ctx, registry, period, validFor, time.Now(), cn, "", "")
if err != nil {
t.Fatalf("initKeyRenewal() returned err: %v", err)
}
Expand Down Expand Up @@ -358,7 +358,7 @@ func TestLegacySecret(t *testing.T) {

validFor := time.Hour
cn := "my-cn"
_, err = initKeyRenewal(ctx, registry, 0, validFor, time.Time{}, cn)
_, err = initKeyRenewal(ctx, registry, 0, validFor, time.Time{}, cn, "", "")
if err != nil {
t.Fatalf("initKeyRenewal() returned err: %v", err)
}
Expand Down