Skip to content

Commit

Permalink
Merge pull request #370 from h3poteto/iss-239
Browse files Browse the repository at this point in the history
refs #239 Use plain text when parsing yaml is failed
  • Loading branch information
h3poteto authored Jun 3, 2021
2 parents e116af9 + fd2608a commit dafdfab
Show file tree
Hide file tree
Showing 4 changed files with 89 additions and 68 deletions.
66 changes: 35 additions & 31 deletions controllers/kmssecret_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,8 @@ import (
"github.com/aws/aws-sdk-go/service/kms"
"github.com/go-logr/logr"
"github.com/go-yaml/yaml"
v1 "k8s.io/api/core/v1"
"github.com/h3poteto/controller-klog/pkg/ctrklog"
corev1 "k8s.io/api/core/v1"
apierrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
Expand All @@ -53,91 +54,90 @@ type KMSSecretReconciler struct {
// +kubebuilder:rbac:groups=core,resources=events,verbs=create;patch

func (r *KMSSecretReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
log := r.Log.WithValues("kmssecret", req.NamespacedName)

ctx = ctrklog.SetController(ctx, "kmssecret")
// An error has occur when KMSSecret is deleted.
log.Info("fetching KMSSecret resources")
ctrklog.Info(ctx, "fetching KMSSecret resources")

kind := secretv1beta1.KMSSecret{}
if err := r.Client.Get(ctx, req.NamespacedName, &kind); err != nil {
log.Error(err, "failed to get KMSSecret resources")

ctrklog.Errorf(ctx, "failed to get KMSSecret: %v", err)
return ctrl.Result{}, client.IgnoreNotFound(err)
}

log = log.WithValues("secret_name", kind.Name)
ctx = ctrklog.SetObject(ctx, kind.Name)

decryptedData, err := decryptData(kind.Spec.EncryptedData, kind.Spec.Region)
decryptedData, err := decryptData(ctx, kind.Spec.EncryptedData, kind.Spec.Region)
if err != nil {
log.Error(err, "failed to decrypt data")
ctrklog.Errorf(ctx, "failed to decrypt data: %v", err)

return ctrl.Result{}, err
}

shasum := shasumData(decryptedData)

log.Info("checking if an existing Secret exists for this resource")
secret := v1.Secret{}
ctrklog.Info(ctx, "checking if an existing Secret for this resource")
secret := corev1.Secret{}
err = r.Client.Get(ctx, client.ObjectKey{Namespace: kind.Namespace, Name: kind.Name}, &secret)

// Create a new Secret if there is no secret associated with KMSSecret.
if apierrors.IsNotFound(err) {
log.Info("could not find existing Secret for KMSSecret, creating one...")
ctrklog.Info(ctx, "could not find existing Secret for KMSSecret, creating one...")

secret := buildSecret(kind, decryptedData)
if err := r.Client.Create(ctx, secret); err != nil {
log.Error(err, "failed to create Secret resource")
ctrklog.Errorf(ctx, "failed to create Secret %s/%s: %v", secret.Namespace, secret.Name, err)
return ctrl.Result{}, err
}

r.Recorder.Eventf(&kind, v1.EventTypeNormal, "Created", "Created Secret %q", secret.Name)
log.Info("created Secret resource for KMSSecret")
r.Recorder.Eventf(&kind, corev1.EventTypeNormal, "Created", "Created Secret %s/%s", secret.Namespace, secret.Name)
ctrklog.Infof(ctx, "created Secret %s/%s", secret.Namespace, secret.Name)

kind.Status.SecretsSum = shasum
if err := r.Client.Update(ctx, &kind); err != nil {
log.Error(err, "failed to update KMSSecret status")
ctrklog.Errorf(ctx, "failed to update KMSSecret %s/%s: %v", kind.Namespace, kind.Name, err)
return ctrl.Result{}, err
}
log.Info("updated KMSSecret resource status")
ctrklog.Infof(ctx, "updated KMSSecret resource status %s/%s", kind.Namespace, kind.Name)

return ctrl.Result{}, nil
}
if err != nil {
log.Error(err, "failed to get Secret for KMSSecret resource")
ctrklog.Errorf(ctx, "failed to get Secret for KMSSecret %s/%s: %v", kind.Namespace, kind.Name, err)
return ctrl.Result{}, err
}

// Check status and update secret if there are differences.
if kind.Status.SecretsSum != shasum {
log.Info("encryptedData is updated, so updating secret resource", "old_secrets_sum", kind.Status.SecretsSum)
ctrklog.Infof(ctx, "encryptedData is updated, so updating secret resource", "old_secrets_sum", kind.Status.SecretsSum)
secret := buildSecret(kind, decryptedData)
if err := r.Client.Update(ctx, secret); err != nil {
log.Error(err, "failed to update Secret resource")
ctrklog.Errorf(ctx, "failed to update Secret %s/%s: %v", secret.Namespace, secret.Name, err)
return ctrl.Result{}, err
}
r.Recorder.Eventf(&kind, v1.EventTypeNormal, "Updated", "Updated Secret %q", secret.Name)
log.Info("updated Secret resource for KMSSecret")
r.Recorder.Eventf(&kind, corev1.EventTypeNormal, "Updated", "Updated Secret %s/%s", secret.Namespace, secret.Name)
ctrklog.Info(ctx, "updated Secret %s/%s", secret.Namespace, secret.Name)

kind.Status.SecretsSum = shasum
if err := r.Client.Update(ctx, &kind); err != nil {
log.Error(err, "failed to update KMSSecret status")
ctrklog.Errorf(ctx, "failed to update KMSSecret %s/%s: %v", kind.Namespace, kind.Name, err)
return ctrl.Result{}, err
}
}

log.Info("resource status synced")
ctrklog.Info(ctx, "resource status synced")

return ctrl.Result{}, nil
}

func (r *KMSSecretReconciler) SetupWithManager(mgr ctrl.Manager) error {
return ctrl.NewControllerManagedBy(mgr).
For(&secretv1beta1.KMSSecret{}).
Owns(&v1.Secret{}).
Owns(&corev1.Secret{}).
Complete(r)
}

func buildSecret(kind secretv1beta1.KMSSecret, decryptedData map[string][]byte) *v1.Secret {
secret := v1.Secret{
func buildSecret(kind secretv1beta1.KMSSecret, decryptedData map[string][]byte) *corev1.Secret {
secret := corev1.Secret{
ObjectMeta: metav1.ObjectMeta{
Name: kind.Name,
Namespace: kind.Namespace,
Expand All @@ -146,13 +146,13 @@ func buildSecret(kind secretv1beta1.KMSSecret, decryptedData map[string][]byte)
Annotations: kind.Spec.Template.GetAnnotations(),
},
Data: decryptedData,
Type: v1.SecretTypeOpaque,
Type: corev1.SecretTypeOpaque,
}
return &secret
}

// decryptData decrypt data using AWS KMS.
func decryptData(encryptedData map[string][]byte, region string) (map[string][]byte, error) {
func decryptData(ctx context.Context, encryptedData map[string][]byte, region string) (map[string][]byte, error) {
sess := session.Must(session.NewSessionWithOptions(session.Options{
SharedConfigState: session.SharedConfigEnable,
}))
Expand All @@ -164,11 +164,15 @@ func decryptData(encryptedData map[string][]byte, region string) (map[string][]b
}
decrypted, err := svc.Decrypt(input)
if err != nil {
ctrklog.Errorf(ctx, "failed to decrypt: %v", err)
return nil, err
}
value, err = yamlParse(decrypted.Plaintext)
plain := decrypted.Plaintext
value, err = yamlParse(plain)
if err != nil {
return nil, fmt.Errorf("failed to yaml parse: %w", err)
ctrklog.Warningf(ctx, "failed to yaml parse for %s, so insert plain text", key)
decryptedData[key] = plain
continue
}
decryptedData[key] = value

Expand Down
82 changes: 48 additions & 34 deletions e2e/e2e_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -139,44 +139,58 @@ var _ = Describe("E2E", func() {
return true, nil
})
})
AfterEach(func() {
ctx := context.Background()
err := k8sClient.Delete(ctx, kmsSecret)
if err != nil {
panic(err)
}
})
Context("Encrypted data using aws cli", func() {
const (
key = "PASSWORD"
value = "my_password"
)
BeforeEach(func() {
keyID := os.Getenv("KMS_KEY_ID")
if keyID == "" {
panic(fmt.Errorf("KMS_KEY_ID is required"))
}
data, err := util.EncryptString(value, keyID, os.Getenv("AWS_REGION"))
if err != nil {
panic(err)
}
kmsSecret = fixtures.NewKMSSecret(ns, "test-secret", region, map[string][]byte{
key: data,
})
})
It("Secret data should be decrepted", func() {
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Minute)
defer cancel()
Expect(setupError).To(BeNil())
res := corev1.Secret{}
err := wait.Poll(1*time.Second, 5*time.Minute, func() (bool, error) {
err := k8sClient.Get(ctx, types.NamespacedName{Namespace: kmsSecret.Namespace, Name: kmsSecret.Name}, &res)
decrypt := func(key, value, expected string) {
BeforeEach(func() {
keyID := os.Getenv("KMS_KEY_ID")
if keyID == "" {
panic(fmt.Errorf("KMS_KEY_ID is required"))
}
data, err := util.EncryptString(value, keyID, os.Getenv("AWS_REGION"))
if err != nil {
if apierrors.IsNotFound(err) {
return false, nil
}
klog.Error(err)
return false, err
panic(err)
}
return true, nil
kmsSecret = fixtures.NewKMSSecret(ns, "test-secret", region, map[string][]byte{
key: data,
})
})
It("Secret data should be decrepted", func() {
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Minute)
defer cancel()
Expect(setupError).To(BeNil())
res := corev1.Secret{}
err := wait.Poll(1*time.Second, 5*time.Minute, func() (bool, error) {
err := k8sClient.Get(ctx, types.NamespacedName{Namespace: kmsSecret.Namespace, Name: kmsSecret.Name}, &res)
if err != nil {
if apierrors.IsNotFound(err) {
return false, nil
}
klog.Error(err)
return false, err
}
return true, nil
})
Expect(err).To(BeNil())
val, ok := res.Data[key]
Expect(ok).To(BeTrue())
Expect(string(val)).To(Equal(expected))
})
Expect(err).To(BeNil())
val, ok := res.Data[key]
Expect(ok).To(BeTrue())
Expect(string(val)).To(Equal(value))
}
Context("Value is plain text", func() {
decrypt("api_key", "hogehoge", "hogehoge")
})
Context("Value is yaml object", func() {
decrypt("api_key", "hoge: fuga", "hoge: fuga")
})
Context("Value is yaml formatted text", func() {
decrypt("api_key", "--- hogehoge", "hogehoge")
})
})
})
Expand Down
3 changes: 2 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,13 @@ require (
github.com/aws/aws-sdk-go v1.37.32
github.com/go-logr/logr v0.4.0
github.com/go-yaml/yaml v2.1.0+incompatible
github.com/h3poteto/controller-klog v0.1.1
github.com/onsi/ginkgo v1.15.0
github.com/onsi/gomega v1.10.5
k8s.io/api v0.20.2
k8s.io/apimachinery v0.20.2
k8s.io/client-go v0.20.2
k8s.io/klog/v2 v2.5.0
k8s.io/klog/v2 v2.9.0
k8s.io/utils v0.0.0-20210111153108-fddb29f9d009
sigs.k8s.io/controller-runtime v0.8.1
sigs.k8s.io/yaml v1.2.0
Expand Down
6 changes: 4 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,8 @@ github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
github.com/h3poteto/controller-klog v0.1.1 h1:xWhVndnSBZA+BapMT+L6NUGHasTQ9V7y3F5HZgw5yY4=
github.com/h3poteto/controller-klog v0.1.1/go.mod h1:lahmY34hv5eFsCpspiQON/4dej5CYkFUSmFEikVKAjQ=
github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q=
github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8=
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
Expand Down Expand Up @@ -693,8 +695,8 @@ k8s.io/gengo v0.0.0-20201113003025-83324d819ded/go.mod h1:FiNAH4ZV3gBg2Kwh89tzAE
k8s.io/klog/v2 v2.0.0/go.mod h1:PBfzABfn139FHAV07az/IF9Wp1bkk3vpT2XSJ76fSDE=
k8s.io/klog/v2 v2.2.0/go.mod h1:Od+F08eJP+W3HUb4pSrPpgp9DGU4GzlpG/TmITuYh/Y=
k8s.io/klog/v2 v2.4.0/go.mod h1:Od+F08eJP+W3HUb4pSrPpgp9DGU4GzlpG/TmITuYh/Y=
k8s.io/klog/v2 v2.5.0 h1:8mOnjf1RmUPW6KRqQCfYSZq/K20Unmp3IhuZUhxl8KI=
k8s.io/klog/v2 v2.5.0/go.mod h1:hy9LJ/NvuK+iVyP4Ehqva4HxZG/oXyIS3n3Jmire4Ec=
k8s.io/klog/v2 v2.9.0 h1:D7HV+n1V57XeZ0m6tdRkfknthUaM06VFbWldOFh8kzM=
k8s.io/klog/v2 v2.9.0/go.mod h1:hy9LJ/NvuK+iVyP4Ehqva4HxZG/oXyIS3n3Jmire4Ec=
k8s.io/kube-openapi v0.0.0-20201113171705-d219536bb9fd h1:sOHNzJIkytDF6qadMNKhhDRpc6ODik8lVC6nOur7B2c=
k8s.io/kube-openapi v0.0.0-20201113171705-d219536bb9fd/go.mod h1:WOJ3KddDSol4tAGcJo0Tvi+dK12EcqSLqcWsryKMpfM=
k8s.io/utils v0.0.0-20201110183641-67b214c5f920/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA=
Expand Down

0 comments on commit dafdfab

Please sign in to comment.