Skip to content

Commit

Permalink
Merge pull request #644 from fluxcd/decryptor-detect-dockercfg
Browse files Browse the repository at this point in the history
decryptor: improve detection of in and out formats for Secret data fields
  • Loading branch information
hiddeco authored Apr 29, 2022
2 parents e0ba73f + 36df540 commit a139a02
Show file tree
Hide file tree
Showing 2 changed files with 137 additions and 10 deletions.
38 changes: 30 additions & 8 deletions controllers/kustomization_decryptor.go
Original file line number Diff line number Diff line change
Expand Up @@ -67,12 +67,16 @@ const (
// DecryptionAzureAuthFile is the name of the file containing the Azure
// credentials.
DecryptionAzureAuthFile = "sops.azure-kv"
)

var (
// maxEncryptedFileSize is the max allowed file size in bytes of an encrypted
// file.
maxEncryptedFileSize int64 = 5 << 20
// unsupportedFormat is used to signal no sopsFormatToMarkerBytes format was
// detected by detectFormatFromMarkerBytes.
unsupportedFormat = formats.Format(-1)
)

var (
// sopsFormatToString is the counterpart to
// https://github.com/mozilla/sops/blob/v3.7.2/cmd/sops/formats/formats.go#L16
sopsFormatToString = map[formats.Format]string{
Expand Down Expand Up @@ -334,9 +338,9 @@ func (d *KustomizeDecryptor) DecryptResource(res *resource.Resource) (*resource.
continue
}

if bytes.Contains(data, sopsFormatToMarkerBytes[formats.Yaml]) || bytes.Contains(data, sopsFormatToMarkerBytes[formats.Json]) {
outF := formats.FormatForPath(key)
out, err := d.SopsDecryptWithFormat(data, formats.Yaml, outF)
if inF := detectFormatFromMarkerBytes(data); inF != unsupportedFormat {
outF := formatForPath(key)
out, err := d.SopsDecryptWithFormat(data, inF, outF)
if err != nil {
return nil, fmt.Errorf("failed to decrypt and format '%s/%s' Secret field '%s': %w",
res.GetNamespace(), res.GetName(), key, err)
Expand Down Expand Up @@ -406,13 +410,13 @@ func (d *KustomizeDecryptor) decryptKustomizationEnvSources(visited map[string]s
} else {
filePath = key
}
if err := visitRef(filePath, formats.FormatForPath(key)); err != nil {
if err := visitRef(filePath, formatForPath(key)); err != nil {
return err
}
}
for _, envFile := range gen.EnvSources {
format := formats.FormatForPath(envFile)
if formats.FormatForPath(envFile) == formats.Binary {
format := formatForPath(envFile)
if format == formats.Binary {
// Default to dotenv
format = formats.Dotenv
}
Expand Down Expand Up @@ -731,3 +735,21 @@ func securePathErr(root string, err error) error {
}
return err
}

func formatForPath(path string) formats.Format {
switch {
case strings.HasSuffix(path, corev1.DockerConfigJsonKey):
return formats.Json
default:
return formats.FormatForPath(path)
}
}

func detectFormatFromMarkerBytes(b []byte) formats.Format {
for k, v := range sopsFormatToMarkerBytes {
if bytes.Contains(b, v) {
return k
}
}
return unsupportedFormat
}
109 changes: 107 additions & 2 deletions controllers/kustomization_decryptor_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -677,8 +677,8 @@ func TestKustomizeDecryptor_DecryptResource(t *testing.T) {
"apiVersion": "v1",
"kind": "Secret",
"metadata": map[string]interface{}{
"name": "secret",
"namespace": "test",
"name": name,
"namespace": namespace,
},
"data": data,
})
Expand Down Expand Up @@ -806,6 +806,59 @@ func TestKustomizeDecryptor_DecryptResource(t *testing.T) {
g.Expect(got.GetDataMap()).To(HaveKeyWithValue("key.yaml", base64.StdEncoding.EncodeToString(plainData)))
})

t.Run("SOPS-encrypted Docker config Secret", func(t *testing.T) {
g := NewWithT(t)

kus := kustomization.DeepCopy()
kus.Spec.Decryption = &kustomizev1.Decryption{
Provider: DecryptionProviderSOPS,
}

d, cleanup, err := NewTempDecryptor("", fake.NewClientBuilder().Build(), *kus)
g.Expect(err).ToNot(HaveOccurred())
t.Cleanup(cleanup)

ageID, err := extage.GenerateX25519Identity()
g.Expect(err).ToNot(HaveOccurred())
d.ageIdentities = append(d.ageIdentities, ageID)

plainData := []byte(`{
"auths": {
"my-registry.example:5000": {
"username": "tiger",
"password": "pass1234",
"email": "tiger@acme.example",
"auth": "dGlnZXI6cGFzczEyMzQ="
}
}
}`)
encData, err := d.sopsEncryptWithFormat(sops.Metadata{
KeyGroups: []sops.KeyGroup{
{&sopsage.MasterKey{Recipient: ageID.Recipient().String()}},
},
}, plainData, formats.Json, formats.Yaml)
g.Expect(err).ToNot(HaveOccurred())

secret := resourceFactory.FromMap(map[string]interface{}{
"apiVersion": "v1",
"kind": "Secret",
"metadata": map[string]interface{}{
"name": "secret",
"namespace": "test",
},
"type": corev1.SecretTypeDockerConfigJson,
"data": map[string]interface{}{
corev1.DockerConfigJsonKey: base64.StdEncoding.EncodeToString(encData),
},
})
g.Expect(isSOPSEncryptedResource(secret)).To(BeFalse())

got, err := d.DecryptResource(secret)
g.Expect(err).ToNot(HaveOccurred())
g.Expect(got).ToNot(BeNil())
g.Expect(got.GetDataMap()).To(HaveKeyWithValue(corev1.DockerConfigJsonKey, base64.StdEncoding.EncodeToString(plainData)))
})

t.Run("nil resource", func(t *testing.T) {
g := NewWithT(t)

Expand Down Expand Up @@ -1646,3 +1699,55 @@ func Test_secureAbsPath(t *testing.T) {
})
}
}

func Test_formatForPath(t *testing.T) {
tests := []struct {
name string
path string
want formats.Format
}{
{
name: "docker config",
path: corev1.DockerConfigJsonKey,
want: formats.Json,
},
{
name: "fallback",
path: "foo.yaml",
want: formats.Yaml,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
g := NewWithT(t)

g.Expect(formatForPath(tt.path)).To(Equal(tt.want))
})
}
}

func Test_detectFormatFromMarkerBytes(t *testing.T) {
tests := []struct {
name string
b []byte
want formats.Format
}{
{
name: "detects format",
b: bytes.Join([][]byte{[]byte("random other bytes"), sopsFormatToMarkerBytes[formats.Yaml], []byte("more random bytes")}, []byte(" ")),
want: formats.Yaml,
},
{
name: "returns unsupported format",
b: []byte("no marker bytes present"),
want: unsupportedFormat,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := detectFormatFromMarkerBytes(tt.b); got != tt.want {
t.Errorf("detectFormatFromMarkerBytes() = %v, want %v", got, tt.want)
}
})
}
}

0 comments on commit a139a02

Please sign in to comment.