fluxcd / kustomize-controller

The GitOps Toolkit Kustomize reconciler
https://fluxcd.io
Apache License 2.0
248 stars 178 forks source link

kustomize controller unable to decrypt kms encoded yaml file data #1147

Open Satcomproj opened 2 months ago

Satcomproj commented 2 months ago

i am using S3 as source and before pushing the data to S3 i am encrypting the data with kms key. i have added a irsa role to decrypt the kms key to the kustomization controller service account. but kustomize controller is failing to decode the data.

getting error: "error":"failed to decode Kubernetes YAML from /tmp/kustomization-3479328615/resources/prod/org_ns79.yaml: MalformedYAMLError: yaml: control characters are not allowed "}

here is the complete code

package main

import ( "bytes" "encoding/gob" "fmt" "io/fs" "io/ioutil" "log" "time"

"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/session"
"github.com/aws/aws-sdk-go/service/kms"
"github.com/aws/aws-sdk-go/service/s3"
"gopkg.in/yaml.v2"

)

type Organization struct { APIVersion string yaml:"apiVersion" Kind string yaml:"kind" Metadata Metadata yaml:"metadata" Spec Spec yaml:"spec" Status Status yaml:"status" }

type Metadata struct { Annotations map[string]interface{} yaml:"annotations" CreationTime time.Time yaml:"creationTimestamp" Generation int yaml:"generation" Name string yaml:"name" ResourceVersion string yaml:"resourceVersion" UID string yaml:"uid" }

type Spec struct { ConfigRef ConfigRef yaml:"configRef" OrganizationType string yaml:"organizationType" Scopes []Scope yaml:"scopes" }

type ConfigRef struct { Name string yaml:"name" }

type Scope struct { AccessMode string yaml:"accessMode" AuthClientRef AuthClientRef yaml:"authClientRef" IsolationRef IsolationRef yaml:"isolationRef" }

type AuthClientRef struct { Name string yaml:"name" }

type IsolationRef struct { Name string yaml:"name" Namespace string yaml:"namespace,omitempty" }

type Status struct { Conditions []Condition yaml:"conditions" }

type Condition struct { LastTransitionTime time.Time yaml:"lastTransitionTime" Message string yaml:"message" ObservedGeneration int yaml:"observedGeneration" Reason string yaml:"reason" Status string yaml:"status" Type string yaml:"type" }

func main() {

bucket := "gitops-flux-1"
key := "resources/prod/org_ns79.yaml"
var org Organization

fmt.Println("test add org")

dataOrg, err := ioutil.ReadFile("org_1.yaml")
if err != nil {
    fmt.Println("Error reading encrypted YAML file:", err)
}

if err := yaml.Unmarshal(dataOrg, &org); err != nil {
    log.Fatalf("failed to unmarshal YAML: %v", err)
}
fmt.Println(org)

updateOrg(&org)

keyID := "280653b3-28d8-4154-bcea-3391fb0797cb"

data, err := yaml.Marshal(org)
if err != nil {
    log.Fatalf(" failed to create updated spec : %v", err)
    return
}

WriteToAExternalFile("resource_status.yaml", data, 0644)

cypherData, err := encryptDataWithKMS(data, keyID)
if err != nil {
    log.Fatalf(" failed to encrypt data : %v", err)
    return
}

if err = uploadFileToS3(bucket, key, cypherData); err != nil {
    log.Fatalf("failed to upload file %v", err)
}

}

func WriteToAExternalFile(filename string, data []byte, perm fs.FileMode) error { err := ioutil.WriteFile(filename, data, perm) if err != nil { fmt.Println("Error writing YAML file:", err) return err }

fmt.Printf(" file  %s created successfully!", filename)
return nil

}

func StructToBytes(org Organization) ([]byte, error) { var buffer bytes.Buffer encoder := gob.NewEncoder(&buffer) err := encoder.Encode(org) if err != nil { return nil, err } return buffer.Bytes(), nil }

func BytesToStruct(data []byte) (*Organization, error) { var org Organization buffer := bytes.NewBuffer(data) decoder := gob.NewDecoder(buffer) err := decoder.Decode(&org) if err != nil { return nil, err } return &org, nil }

func encryptDataWithKMS(data []byte, keyID string) ([]byte, error) { // Create a new session sess, err := session.NewSession(&aws.Config{ Region: aws.String("us-east-1"), // Optionally provide credentials, leave this out to use default credentials. // Credentials: credentials.NewStaticCredentials("YOUR_AWS_ACCESS_KEY_ID", "YOUR_AWS_SECRET_ACCESS_KEY", ""), }) if err != nil { fmt.Println("Error creating session:", err) return nil, err } // Create a KMS client kmsClient := kms.New(sess)

// Encrypt the data using the specified KMS key
params := &kms.EncryptInput{
    KeyId:     aws.String(keyID),
    Plaintext: data, // by default encoded base 64
}

resp, err := kmsClient.Encrypt(params)
if err != nil {
    return nil, err
}

//ciphertextBase64 := base64.StdEncoding.EncodeToString(resp.CiphertextBlob)
// Return the encrypted data (ciphertext)
//return bytes.NewBufferString(ciphertextBase64).Bytes(), nil

return resp.CiphertextBlob, nil

}

func updateOrg(org Organization) Organization {

org.Metadata.Name = "98e64260-f154-4d0d-be7d-fa217ea72937"
//fmt.Println(org)
return org

}

func uploadFileToS3(bucketName, fileName string, fileContent []byte) error { // Create a new AWS session sess, err := session.NewSession(&aws.Config{ Region: aws.String("us-east-1"), // Optionally provide credentials, leave this out to use default credentials. // Credentials: credentials.NewStaticCredentials("YOUR_AWS_ACCESS_KEY_ID", "YOUR_AWS_SECRET_ACCESS_KEY", ""), }) if err != nil { fmt.Println("Error creating session:", err) return err }

// Create S3 service client
svc := s3.New(sess)

// Upload file to S3 bucket
_, err = svc.PutObject(&s3.PutObjectInput{
    Bucket: aws.String(bucketName),
    Key:    aws.String(fileName),
    Body:   bytes.NewReader(fileContent),
})
if err != nil {
    return err
}
return nil

}

1.is it a valid approach?

  1. apart from adding irsa i have not done anything else. here is the kustomization resouce

apiVersion: kustomize.toolkit.fluxcd.io/v1 kind: Kustomization metadata: name: flux-test-kustomization namespace: flux-test-1 spec: interval: 1m sourceRef: kind: Bucket name: my-bucket namespace: flux-system path: /resources/prod prune: true

stefanprodan commented 2 months ago

You need to use the sops CLI to encrypt the Kubernetes secret, then enabled decryption in the Flux Kustomization. Docs here: