Skip to content

Commit

Permalink
propagate Secret of type kubernetes.io/service-account-token
Browse files Browse the repository at this point in the history
Signed-off-by: Amir Alavi <[email protected]>
  • Loading branch information
a7i committed Mar 28, 2024
1 parent ff7322a commit ae9f508
Show file tree
Hide file tree
Showing 4 changed files with 98 additions and 14 deletions.
13 changes: 0 additions & 13 deletions pkg/detector/detector.go
Original file line number Diff line number Diff line change
Expand Up @@ -293,19 +293,6 @@ func (d *ResourceDetector) EventFilter(obj interface{}) bool {
}
}

if unstructObj, ok := obj.(*unstructured.Unstructured); ok {
switch unstructObj.GroupVersionKind() {
// The secret, with type 'kubernetes.io/service-account-token', is created along with `ServiceAccount` should be
// prevented from propagating.
// Refer to https://github.com/karmada-io/karmada/pull/1525#issuecomment-1091030659 for more details.
case corev1.SchemeGroupVersion.WithKind("Secret"):
secretType, found, _ := unstructured.NestedString(unstructObj.Object, "type")
if found && secretType == string(corev1.SecretTypeServiceAccountToken) {
return false
}
}
}

// Prevent configmap/extension-apiserver-authentication from propagating as it is generated
// and managed by kube-apiserver.
// Refer to https://github.com/karmada-io/karmada/issues/4228 for more details.
Expand Down
27 changes: 27 additions & 0 deletions pkg/resourceinterpreter/default/native/retain.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ package native

import (
"fmt"
"strings"

appsv1 "k8s.io/api/apps/v1"
batchv1 "k8s.io/api/batch/v1"
Expand All @@ -42,6 +43,7 @@ func getAllDefaultRetentionInterpreter() map[schema.GroupVersionKind]retentionIn
s[corev1.SchemeGroupVersion.WithKind(util.PersistentVolumeClaimKind)] = retainPersistentVolumeClaimFields
s[corev1.SchemeGroupVersion.WithKind(util.PersistentVolumeKind)] = retainPersistentVolumeFields
s[batchv1.SchemeGroupVersion.WithKind(util.JobKind)] = retainJobSelectorFields
s[corev1.SchemeGroupVersion.WithKind(util.SecretKind)] = retainSecretServiceAccountToken
return s
}

Expand Down Expand Up @@ -160,3 +162,28 @@ func retainWorkloadReplicas(desired, observed *unstructured.Unstructured) (*unst

return desired, nil
}

func retainSecretServiceAccountToken(desired *unstructured.Unstructured, observed *unstructured.Unstructured) (retained *unstructured.Unstructured, err error) {
if desired.Object["type"] == string(corev1.SecretTypeServiceAccountToken) {
// retain service-account.uid which is a unique per cluster
serviceAccountUIDPath := []string{"metadata", "annotations", corev1.ServiceAccountUIDKey}
uid, _, err := unstructured.NestedString(observed.Object, serviceAccountUIDPath...)
if err != nil {
return nil, fmt.Errorf("failed to get %s from desired.Object: %+v", corev1.ServiceAccountUIDKey, err)
}
if err := unstructured.SetNestedField(desired.Object, uid, serviceAccountUIDPath...); err != nil {
return nil, fmt.Errorf("failed to set %s for %s %s/%s", strings.Join(serviceAccountUIDPath, "."), desired.GetKind(), desired.GetNamespace(), desired.GetName())
}

// retain token generated by cluster kube-controller-manager
data, _, err := unstructured.NestedStringMap(observed.Object, "data")
if err != nil {
return nil, fmt.Errorf("failed to get .data from desired.Object: %+v", err)
}
if err := unstructured.SetNestedStringMap(desired.Object, data, "data"); err != nil {
return nil, fmt.Errorf("failed to set data for %s %s/%s", desired.GetKind(), desired.GetNamespace(), desired.GetName())
}
}

return desired, nil
}
70 changes: 69 additions & 1 deletion pkg/resourceinterpreter/default/native/retain_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import (

"github.com/stretchr/testify/assert"
appsv1 "k8s.io/api/apps/v1"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"

Expand Down Expand Up @@ -101,10 +102,77 @@ func Test_retainK8sWorkloadReplicas(t *testing.T) {
t.Run(tt.name, func(t *testing.T) {
got, err := retainWorkloadReplicas(tt.args.desired, tt.args.observed)
if (err != nil) != tt.wantErr {
t.Errorf("reflectPodDisruptionBudgetStatus() error = %v, wantErr %v", err, tt.wantErr)
t.Errorf("retainWorkloadReplicas() error = %v, wantErr %v", err, tt.wantErr)
return
}
assert.Equalf(t, tt.want, got, "retainDeploymentFields(%v, %v)", tt.args.desired, tt.args.observed)
})
}
}

func Test_retainSecretServiceAccountToken(t *testing.T) {
createSecret := func(secretType corev1.SecretType, uuid, key, value string) *unstructured.Unstructured {
ret, _ := helper.ToUnstructured(&corev1.Secret{
Type: secretType,
ObjectMeta: metav1.ObjectMeta{
Annotations: map[string]string{
corev1.ServiceAccountUIDKey: uuid,
},
},
Data: map[string][]byte{
key: []byte(value),
},
})
return ret
}

type args struct {
desired *unstructured.Unstructured
observed *unstructured.Unstructured
}
tests := []struct {
name string
args args
want *unstructured.Unstructured
}{
{
name: "secret data and uid are retained for type service-account-token",
args: args{
desired: createSecret(corev1.SecretTypeServiceAccountToken, "111", corev1.ServiceAccountTokenKey, "desired-token"),
observed: createSecret(corev1.SecretTypeServiceAccountToken, "999", corev1.ServiceAccountTokenKey, "observed-token"),
},
want: createSecret(corev1.SecretTypeServiceAccountToken, "999", corev1.ServiceAccountTokenKey, "observed-token"),
},
{
name: "does not retain for type tls",
args: args{
desired: createSecret(corev1.SecretTypeTLS, "111", corev1.TLSCertKey, "desired-cert"),
observed: createSecret(corev1.SecretTypeTLS, "999", corev1.TLSCertKey, "observed-cert"),
},
want: createSecret(corev1.SecretTypeTLS, "111", corev1.TLSCertKey, "desired-cert"),
},
{
name: "does not retain for type basic-auth",
args: args{
desired: createSecret(corev1.SecretTypeBasicAuth, "111", corev1.BasicAuthUsernameKey, "desired-user"),
observed: createSecret(corev1.SecretTypeBasicAuth, "999", corev1.BasicAuthUsernameKey, "observed-user"),
},
want: createSecret(corev1.SecretTypeBasicAuth, "111", corev1.BasicAuthUsernameKey, "desired-user"),
},
{
name: "does not retain for type dockercfg",
args: args{
desired: createSecret(corev1.SecretTypeDockercfg, "111", corev1.DockerConfigKey, "desired-docker-cfg"),
observed: createSecret(corev1.SecretTypeDockercfg, "999", corev1.DockerConfigKey, "observed-docker-cfg"),
},
want: createSecret(corev1.SecretTypeDockercfg, "111", corev1.DockerConfigKey, "desired-docker-cfg"),
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := retainSecretServiceAccountToken(tt.args.desired, tt.args.observed)
assert.Nil(t, err, "retainSecretServiceAccountToken() error = %v", err)
assert.Equalf(t, tt.want, got, "retainSecretServiceAccountToken(%v, %v)", tt.args.desired, tt.args.observed)
})
}
}
2 changes: 2 additions & 0 deletions pkg/util/constants.go
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,8 @@ const (
ClusterRoleBindingKind = "ClusterRoleBinding"
// CRDKind indicates the target resource is a CustomResourceDefinition
CRDKind = "CustomResourceDefinition"
// SecretKind indicates the target resource is a Secret
SecretKind = "Secret"

// ServiceExportKind indicates the target resource is a serviceexport crd
ServiceExportKind = "ServiceExport"
Expand Down

0 comments on commit ae9f508

Please sign in to comment.