Skip to content

Commit

Permalink
Added decryption of patches and components.
Browse files Browse the repository at this point in the history
Cleaned SOPS tests.

Signed-off-by: Yuriy <[email protected]>
  • Loading branch information
vlasov-y committed Dec 4, 2024
1 parent 29080cb commit 49770ea
Show file tree
Hide file tree
Showing 39 changed files with 346 additions and 287 deletions.
10 changes: 6 additions & 4 deletions docs/spec/v1/kustomizations.md
Original file line number Diff line number Diff line change
Expand Up @@ -725,7 +725,7 @@ For more information, see [remote clusters/Cluster-API](#remote-clusterscluster-
### Decryption

`.spec.decryption` is an optional field to specify the configuration to decrypt
Secrets that are a part of the Kustomization.
Secrets, ConfigMaps and patches that are a part of the Kustomization.

Since Secrets are either plain text or `base64` encoded, it's unsafe to store
them in plain text in a public or private Git repository. In order to store
Expand All @@ -734,9 +734,11 @@ encrypt your Kubernetes Secret data with [age](https://age-encryption.org/v1/)
and/or [OpenPGP](https://www.openpgp.org) keys, or with provider implementations
like Azure Key Vault, GCP KMS or Hashicorp Vault.

**Note:** You should encrypt only the `data/stringData` section of the Kubernetes
Secret, encrypting the `metadata`, `kind` or `apiVersion` fields is not supported.
An easy way to do this is by appending `--encrypted-regex '^(data|stringData)$'`
Also, you may want to encrypt some parts of resources as well. In order to do that,
you may encrypt patches as well.

**Note:** You must leave `metadata`, `kind` or `apiVersion` in plain text.
An easy way to do this is to limit encrypted keys by appending `--encrypted-regex '^(data|stringData)$'`
to your `sops --encrypt` command.

It has two fields:
Expand Down
4 changes: 2 additions & 2 deletions internal/controller/kustomization_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -599,8 +599,8 @@ func (r *KustomizationReconciler) build(ctx context.Context,
}

// Decrypt Kustomize EnvSources files before build
if err = dec.DecryptEnvSources(dirPath); err != nil {
return nil, fmt.Errorf("error decrypting env sources: %w", err)
if err = dec.DecryptSources(dirPath); err != nil {
return nil, fmt.Errorf("error decrypting sources: %w", err)
}

m, err := generator.SecureBuild(workDir, dirPath, !r.NoRemoteBases)
Expand Down
109 changes: 36 additions & 73 deletions internal/controller/kustomization_decryptor_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,18 +43,18 @@ func TestKustomizationReconciler_Decryptor(t *testing.T) {
g.Expect(err).NotTo(HaveOccurred(), "failed to create vault client")

// create a master key on the vault transit engine
path, data := "sops/keys/firstkey", map[string]interface{}{"type": "rsa-4096"}
path, data := "sops/keys/vault", map[string]interface{}{"type": "rsa-4096"}
_, err = cli.Logical().Write(path, data)
g.Expect(err).NotTo(HaveOccurred(), "failed to write key")

// encrypt the testdata vault secret
cmd := exec.Command("sops", "--hc-vault-transit", cli.Address()+"/v1/sops/keys/firstkey", "--encrypt", "--encrypted-regex", "^(data|stringData)$", "--in-place", "./testdata/sops/secret.vault.yaml")
cmd := exec.Command("sops", "--hc-vault-transit", cli.Address()+"/v1/sops/keys/vault", "--encrypt", "--encrypted-regex", "^(data|stringData)$", "--in-place", "./testdata/sops/algorithms/vault.yaml")
err = cmd.Run()
g.Expect(err).NotTo(HaveOccurred(), "failed to encrypt file")

// defer the testdata vault secret decryption, to leave a clean testdata vault secret
defer func() {
cmd := exec.Command("sops", "--hc-vault-transit", cli.Address()+"/v1/sops/keys/firstkey", "--decrypt", "--encrypted-regex", "^(data|stringData)$", "--in-place", "./testdata/sops/secret.vault.yaml")
cmd := exec.Command("sops", "--hc-vault-transit", cli.Address()+"/v1/sops/keys/firstkey", "--decrypt", "--encrypted-regex", "^(data|stringData)$", "--in-place", "./testdata/sops/algorithms/vault.yaml")
err = cmd.Run()
}()

Expand All @@ -70,36 +70,23 @@ func TestKustomizationReconciler_Decryptor(t *testing.T) {
artifactChecksum, err := testServer.ArtifactFromDir("testdata/sops", artifactName)
g.Expect(err).ToNot(HaveOccurred())

overlayArtifactName := "sops-" + randStringRunes(5)
overlayChecksum, err := testServer.ArtifactFromDir("testdata/test-dotenv", overlayArtifactName)
g.Expect(err).ToNot(HaveOccurred())

repositoryName := types.NamespacedName{
Name: fmt.Sprintf("sops-%s", randStringRunes(5)),
Namespace: id,
}

overlayRepositoryName := types.NamespacedName{
Name: fmt.Sprintf("sops-%s", randStringRunes(5)),
Namespace: id,
}

err = applyGitRepository(repositoryName, artifactName, "main/"+artifactChecksum)
g.Expect(err).NotTo(HaveOccurred())

err = applyGitRepository(overlayRepositoryName, overlayArtifactName, "main/"+overlayChecksum)
g.Expect(err).NotTo(HaveOccurred())

pgpKey, err := os.ReadFile("testdata/sops/pgp.asc")
pgpKey, err := os.ReadFile("testdata/sops/keys/pgp.asc")
g.Expect(err).ToNot(HaveOccurred())
ageKey, err := os.ReadFile("testdata/sops/age.txt")
ageKey, err := os.ReadFile("testdata/sops/keys/age.txt")
g.Expect(err).ToNot(HaveOccurred())

sopsSecretKey := types.NamespacedName{
Name: "sops-" + randStringRunes(5),
Namespace: id,
}

sopsSecret := &corev1.Secret{
ObjectMeta: metav1.ObjectMeta{
Name: sopsSecretKey.Name,
Expand Down Expand Up @@ -153,64 +140,40 @@ func TestKustomizationReconciler_Decryptor(t *testing.T) {
return obj.Status.LastAppliedRevision == "main/"+artifactChecksum
}, timeout, time.Second).Should(BeTrue())

overlayKustomizationName := fmt.Sprintf("sops-%s", randStringRunes(5))
overlayKs := kustomization.DeepCopy()
overlayKs.ResourceVersion = ""
overlayKs.Name = overlayKustomizationName
overlayKs.Spec.SourceRef.Name = overlayRepositoryName.Name
overlayKs.Spec.SourceRef.Namespace = overlayRepositoryName.Namespace
overlayKs.Spec.Path = "./testdata/test-dotenv/overlays"

g.Expect(k8sClient.Create(context.TODO(), overlayKs)).To(Succeed())

g.Eventually(func() bool {
var obj kustomizev1.Kustomization
_ = k8sClient.Get(context.Background(), client.ObjectKeyFromObject(overlayKs), &obj)
return obj.Status.LastAppliedRevision == "main/"+overlayChecksum
}, timeout, time.Second).Should(BeTrue())

t.Run("decrypts SOPS secrets", func(t *testing.T) {
g := NewWithT(t)

var pgpSecret corev1.Secret
g.Expect(k8sClient.Get(context.TODO(), types.NamespacedName{Name: "sops-pgp", Namespace: id}, &pgpSecret)).To(Succeed())
g.Expect(pgpSecret.Data["secret"]).To(Equal([]byte(`my-sops-pgp-secret`)))

var ageSecret corev1.Secret
g.Expect(k8sClient.Get(context.TODO(), types.NamespacedName{Name: "sops-age", Namespace: id}, &ageSecret)).To(Succeed())
g.Expect(ageSecret.Data["secret"]).To(Equal([]byte(`my-sops-age-secret`)))

var daySecret corev1.Secret
g.Expect(k8sClient.Get(context.TODO(), types.NamespacedName{Name: "sops-day", Namespace: id}, &daySecret)).To(Succeed())
g.Expect(string(daySecret.Data["secret"])).To(Equal("day=Tuesday\n"))

var yearSecret corev1.Secret
g.Expect(k8sClient.Get(context.TODO(), types.NamespacedName{Name: "sops-year", Namespace: id}, &yearSecret)).To(Succeed())
g.Expect(string(yearSecret.Data["year"])).To(Equal("2017"))

var unencryptedSecret corev1.Secret
g.Expect(k8sClient.Get(context.TODO(), types.NamespacedName{Name: "unencrypted-sops-year", Namespace: id}, &unencryptedSecret)).To(Succeed())
g.Expect(string(unencryptedSecret.Data["year"])).To(Equal("2021"))

var year1Secret corev1.Secret
g.Expect(k8sClient.Get(context.TODO(), types.NamespacedName{Name: "sops-year1", Namespace: id}, &year1Secret)).To(Succeed())
g.Expect(string(year1Secret.Data["year"])).To(Equal("year1"))

var year2Secret corev1.Secret
g.Expect(k8sClient.Get(context.TODO(), types.NamespacedName{Name: "sops-year2", Namespace: id}, &year2Secret)).To(Succeed())
g.Expect(string(year2Secret.Data["year"])).To(Equal("year2"))

var year3Secret corev1.Secret
g.Expect(k8sClient.Get(context.TODO(), types.NamespacedName{Name: "sops-year3", Namespace: id}, &year3Secret)).To(Succeed())
g.Expect(string(year3Secret.Data["year"])).To(Equal("year3"))

var encodedSecret corev1.Secret
g.Expect(k8sClient.Get(context.TODO(), types.NamespacedName{Name: "sops-month", Namespace: id}, &encodedSecret)).To(Succeed())
g.Expect(string(encodedSecret.Data["month.yaml"])).To(Equal("month: May\n"))

var hcvaultSecret corev1.Secret
g.Expect(k8sClient.Get(context.TODO(), types.NamespacedName{Name: "sops-hcvault", Namespace: id}, &hcvaultSecret)).To(Succeed())
g.Expect(string(hcvaultSecret.Data["secret"])).To(Equal("my-sops-vault-secret\n"))
secretNames := []string{
"sops-algo-age",
"sops-algo-pgp",
"sops-algo-vault",
"sops-component",
"sops-envs-secret",
"sops-files-secret",
"sops-inside-secret",
"sops-remote-secret",
}
for _, name := range secretNames {
var secret corev1.Secret
g.Expect(k8sClient.Get(context.TODO(), types.NamespacedName{Name: name, Namespace: id}, &secret)).To(Succeed())
g.Expect(string(secret.Data["key"])).To(Equal("value"), fmt.Sprintf("failed on secret %s", name))
}

configMapNames := []string{
"sops-envs-configmap",
"sops-files-configmap",
"sops-remote-configmap",
}
for _, name := range configMapNames {
var configMap corev1.ConfigMap
g.Expect(k8sClient.Get(context.TODO(), types.NamespacedName{Name: name, Namespace: id}, &configMap)).To(Succeed())
g.Expect(string(configMap.Data["key"])).To(Equal("value"), fmt.Sprintf("failed on configmap %s", name))
}

var patchedSecret corev1.Secret
g.Expect(k8sClient.Get(context.TODO(), types.NamespacedName{Name: "sops-patches-secret", Namespace: id}, &patchedSecret)).To(Succeed())
g.Expect(string(patchedSecret.Data["key"])).To(Equal("merge1"))
g.Expect(string(patchedSecret.Data["merge2"])).To(Equal("merge2"))
})

t.Run("does not emit change events for identical secrets", func(t *testing.T) {
Expand Down
8 changes: 4 additions & 4 deletions internal/controller/kustomization_fuzzer_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -80,8 +80,8 @@ const vaultVersion = "1.13.2"
const defaultBinVersion = "1.24"

//go:embed testdata/crd/*.yaml
//go:embed testdata/sops/pgp.asc
//go:embed testdata/sops/age.txt
//go:embed testdata/sops/keys/pgp.asc
//go:embed testdata/sops/keys/age.txt
var testFiles embed.FS

// FuzzControllers implements a fuzzer that targets the Kustomize controller.
Expand Down Expand Up @@ -182,11 +182,11 @@ func Fuzz_Controllers(f *testing.F) {
if err != nil {
return err
}
pgpKey, err := testFiles.ReadFile("testdata/sops/pgp.asc")
pgpKey, err := testFiles.ReadFile("testdata/sops/keys/pgp.asc")
if err != nil {
return err
}
ageKey, err := testFiles.ReadFile("testdata/sops/age.txt")
ageKey, err := testFiles.ReadFile("testdata/sops/keys/age.txt")
if err != nil {
return err
}
Expand Down
37 changes: 28 additions & 9 deletions internal/controller/testdata/sops/.sops.yaml
Original file line number Diff line number Diff line change
@@ -1,11 +1,30 @@
stores:
json:
indent: 2
yaml:
indent: 2

# creation rules are evaluated sequentially, the first match wins
creation_rules:
# files using age
- path_regex: \.age.yaml$
encrypted_regex: ^(data|stringData)$
age: age1l44xcng8dqj32nlv6d930qvvrny05hglzcv9qpc7kxjc6902ma4qufys29
- path_regex: month.yaml$
pgp: 35C1A64CD7FC0AB6EB66756B2445463C3234ECE1
# fallback to PGP
- encrypted_regex: ^(data|stringData)$
pgp: 35C1A64CD7FC0AB6EB66756B2445463C3234ECE1
# Testing PGP
- path_regex: (inside|pgp)\.yaml$
encrypted_regex: &encrypted_regex ^(data|stringData)$
pgp: &pgp 35C1A64CD7FC0AB6EB66756B2445463C3234ECE1

- path_regex: json\.yaml$
encrypted_regex: ".*"
age: &age age1l44xcng8dqj32nlv6d930qvvrny05hglzcv9qpc7kxjc6902ma4qufys29

- path_regex: \.yaml$
encrypted_regex: *encrypted_regex
age: *age

- path_regex: \.(env|txt)$
age: *age

# Fallback
- key_groups:
- age:
- *age
- pgp:
- *pgp
26 changes: 26 additions & 0 deletions internal/controller/testdata/sops/algorithms/age.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
apiVersion: v1
kind: Secret
metadata:
name: age
stringData:
key: ENC[AES256_GCM,data:mHeXsmQ=,iv:vUMpILz3xchORqkzDFvgwENY7EqIHHGJdEF6C8xqbFE=,tag:IroV7hykADvD0IUaq6kikA==,type:str]
sops:
kms: []
gcp_kms: []
azure_kv: []
hc_vault: []
age:
- recipient: age1l44xcng8dqj32nlv6d930qvvrny05hglzcv9qpc7kxjc6902ma4qufys29
enc: |
-----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBZeHVSdjJoY3ZSQjJzbk1q
ZXFxMWJ5amkrN1VXeHI4QzQ5OHcwVGxDem1zCm8wQVEzNEUrOUhtRUFkVnFUY0tN
aFgwaHNrWmVWY1RGWXI2YlpYbUhYMGMKLS0tIDBFSXo3cjRCMngvTXpldzhMRlVp
TXk2d2ExSVZYNDVTV0xwVlZnQnpScG8KVpjffjtRTA7Z4Wf/l1VMLjcl16hOrRUv
LKiZDcq+nqKDUI7owZ+xNs2w5SrQjEWVhDXRSeSSRiJrK/bCYKzRxA==
-----END AGE ENCRYPTED FILE-----
lastmodified: "2024-11-12T13:33:42Z"
mac: ENC[AES256_GCM,data:vmrF+VgW3o8z4h/DOStCUNudz68yHEC8Mws+LPoKpM3Xc7GM0Z1CfX0TKwdLLjMuvyWa2Nx2NIxm0+MCbmR8+y2izn0hHPSWhNVCWSK+iW48M05vXhDCV0xNkqM7g0kLhQ3PiSrB69loQj8C590HIfEViEtyDCFUeynDgcC289Q=,iv:u5lhmtXMxyt+3Pw09wWvgBhmKLoOSpKNWUpu/LuCr3Y=,tag:Dg0HFdLgQltzPgnEmltAzQ==,type:str]
pgp: []
encrypted_regex: ^(data|stringData)$
version: 3.9.0
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
namePrefix: algo-
resources:
- age.yaml
- pgp.yaml
- vault.yaml
37 changes: 37 additions & 0 deletions internal/controller/testdata/sops/algorithms/pgp.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
apiVersion: v1
kind: Secret
metadata:
name: pgp
stringData:
key: ENC[AES256_GCM,data:EJey73Q=,iv:QRdpZJ6WYi3fWpKwjl8ZiV+Wwq9qtYTpcMQ0j0OEa44=,tag:d1WlcRpwEJg1lk3X3ILDmA==,type:str]
sops:
kms: []
gcp_kms: []
azure_kv: []
hc_vault: []
age: []
lastmodified: "2024-11-12T13:33:42Z"
mac: ENC[AES256_GCM,data:25ERLClNe3o33jEo109QtmVH/qzl+e0pMRR1RDyQ4QHrVqYfMIvgUeYDHAIJ5WDwQaueON8nne1KIo+fcPYVBdHvTYvnZiicCUPA5/fpgbyts0u5CdUs31bltI/blnUlU8VbJfIk2Zjlj93erLw23sdzdo/0xsdDTrf3bYiS2CI=,iv:vxrgdyqIKRWGBA+dgrGbjGn7tkXEqbADayIxuzNwxp0=,tag:qWesJqClsLpZHY9UR7ptLQ==,type:str]
pgp:
- created_at: "2024-11-12T13:33:42Z"
enc: |-
-----BEGIN PGP MESSAGE-----
hQIMA90SOJihaAjLARAAqSf7bnqHB0/gfh8CmweYr5cfUpH8aYg7B5QhsnD6nOok
x0UIPtaxtfEBvuDsM9M678Gj/hTEzMv0FmDYRt88NAXm1+63HHnz0/0O3xXQ/DR6
+1uEZruuyC23nyzjc1fefaqgZ1YJAnj5WCvcWaF12bXbIdFQpRhpVcoMMqWhQizF
5QJFXjU3cnzIVtvcpMDD63NTpk8+hSTYJr5ZFODSMbQr+EPHvKPMrIx3LLcihkkS
eyxvfLalj556f/3QVgGuOX6VX8lPIaUyIcmXyUkGsooEirOyhiZg2sk/QB6TYIa6
Nm62hmeeXP01wyY6tax7l3LpAuda6CJRVg+Je1OkIjiuPMIBzHgtfhGFks8vgeTP
xsHXKLKXlJAQyS4ewOItm9n9jc9Xdnwfli4HrGbHNzq7lgEyAOyZZtOifl4KqFbM
0c3kGiP3ezycRrQGudvbdIZqGfeD+gKrBv6cV49Wgt7Nb1WJUKLcPv4PNtSlYzSu
lGDM63bO+QBAKObc6MOvLnVXbFXrErLMqrexN9XFdjvvsmQAVr2z5phZk5fEk7kw
j8CqyTuy2Dm+ChJwNEeqIY3BNHkvvWMLx8Cr7ZY6bO1BvOdp01mBf+XD/apeBBUe
v2DT36mCehKZh5BHDYH7hKCNw+4PN2hzZd02zKMNzmARqLzQeseaTXti3Hyze23S
XAG1ddNzKXsgbTwLog5EN7DTIQKR+uCIgHuK0DclyWvTiUK7P6HGepTE7byJnnpl
jHtAVs8t+cYHBtY+gKFsstRGbJgAe8QfIt12/XMu9jcA/r8m7xdyNS5P9VZj
=gXAv
-----END PGP MESSAGE-----
fp: 35C1A64CD7FC0AB6EB66756B2445463C3234ECE1
encrypted_regex: ^(data|stringData)$
version: 3.9.0
6 changes: 6 additions & 0 deletions internal/controller/testdata/sops/algorithms/vault.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
apiVersion: v1
kind: Secret
metadata:
name: vault
stringData:
key: value
7 changes: 7 additions & 0 deletions internal/controller/testdata/sops/component/env.env
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
key=ENC[AES256_GCM,data:HfbmmMU=,iv:nWWqqIzzutZJBzu5PbaTPBsqvszaz2/+58mYOK7hj9Q=,tag:b+VcateAccwdb7x2dmYDrQ==,type:str]
sops_age__list_0__map_enc=-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBsc0Vyd25KTE1sYWM1akFH\nTUFBeHBmSmdGMnY3ZFJvazRZMUtPMFpscmhBCnVsL2Y0cUd1Nkx1Z0Q1OWpHOG0w\nNnhXSmxjbzR5NVE1NGpjR3d2SHN6SzgKLS0tIG5tdXpXK0U2SUlsQlcvY0ZvRWJB\nS2N6MS9QRVR4K2toMEg1eDR3a3ZtdzAKiliurqchsdfT4XbttES0ohnuTMNKlZy9\nefqbQO2lTLw8wUsNUunTpJBEAx9MFZ+LFHE/EZfHZqYlzxCPzfhufA==\n-----END AGE ENCRYPTED FILE-----\n
sops_age__list_0__map_recipient=age1l44xcng8dqj32nlv6d930qvvrny05hglzcv9qpc7kxjc6902ma4qufys29
sops_lastmodified=2024-11-12T13:33:42Z
sops_mac=ENC[AES256_GCM,data:kPn8FhXF7UcPbkA7gjfjfYljawfT67SQBsYbnaAgtcFAtMWTryTHSDAASp2RZiClZiWnKgOgT8NeFUC+hUvjlz/Vj3pQxl6zY+3CmlrbBiqYUwd8ksXjps8UTqcioWKc7xULLqV5GMUHpoWnDWkkt0F6F10uCL78P0JoKmIeCXM=,iv:/G3GIGXriXuoS9OhfEazEYgVBbo+XvouTGYEi5XVYqQ=,tag:80P9IXhwJzoqJ43eK2W+4g==,type:str]
sops_unencrypted_suffix=_unencrypted
sops_version=3.9.0
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
apiVersion: kustomize.config.k8s.io/v1alpha1
kind: Component
secretGenerator:
- name: sops-year3
envs:
- year3.env
generatorOptions:
disableNameSuffixHash: true
secretGenerator:
- name: component
envs:
- env.env
1 change: 0 additions & 1 deletion internal/controller/testdata/sops/day.txt

This file was deleted.

20 changes: 0 additions & 20 deletions internal/controller/testdata/sops/day.txt.encrypted

This file was deleted.

7 changes: 7 additions & 0 deletions internal/controller/testdata/sops/envs/env.env
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
key=ENC[AES256_GCM,data:3PTvx6o=,iv:74ni7B2QMB6aygdd3R7IEzNCwo1W+TpPWMJLfYCCG4U=,tag:mK2Tu7JWDdEmZUrXz3uRzw==,type:str]
sops_age__list_0__map_enc=-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSA5aDhVTW1IenNXQmptWnha\nMjd1UWN3dHp0QXRkSnhUSjBHVFdKSmdXYzNNClVWeXVGWndJQ1RpRUlJRy9yeHJY\nb1VhbnR2TlovSUg1MlpZdkhWdkVHTG8KLS0tIHVOSEhOVVV2cXRUQUs2Sk15eU1a\nRW92L1BWQnhNbStFekZjVVRDUFJtaWsK+wPkQAtZtTbh2WHik1ovX61ZJPpkmwuO\nnUYAn37tZELXX/alrOORRwoq+0oBQO5pZYsJBi0fvijfm9VqR/4jKg==\n-----END AGE ENCRYPTED FILE-----\n
sops_age__list_0__map_recipient=age1l44xcng8dqj32nlv6d930qvvrny05hglzcv9qpc7kxjc6902ma4qufys29
sops_lastmodified=2024-11-12T13:33:42Z
sops_mac=ENC[AES256_GCM,data:YQHMLRk85ozeuqIvNekLAVp2DFSj+VgDG2z70uQaeCA+uxFp3k/THlANAXx+GP1Oab923Q6nG5ItV9dcG1hTXpA/NRpbM02pfNe/iYnVL7AtcXqFg/jy2T4kkqx7cHAXJi9zd+ZrISIZCNWinLoFfaAo70+epsFumUmLUaDzUPQ=,iv:TdOIRoy6Wch1/x9GlEsmArA5g461ILJZUE7tIxi9G28=,tag:miip/H0SuHqvaoxGvzheIg==,type:str]
sops_unencrypted_suffix=_unencrypted
sops_version=3.9.0
Loading

0 comments on commit 49770ea

Please sign in to comment.