Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

sops: various improvements and tests #607

Merged
merged 15 commits into from
Apr 5, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
91 changes: 37 additions & 54 deletions controllers/kustomization_decryptor.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,9 @@ import (
"encoding/base64"
"fmt"
"os"
"os/exec"
"path/filepath"
"strings"

securejoin "github.com/cyphar/filepath-securejoin"
"go.mozilla.org/sops/v3"
"go.mozilla.org/sops/v3/aes"
"go.mozilla.org/sops/v3/cmd/sops/common"
Expand All @@ -42,8 +40,10 @@ import (
"sigs.k8s.io/yaml"

kustomizev1 "github.com/fluxcd/kustomize-controller/api/v1beta2"
"github.com/fluxcd/kustomize-controller/internal/sops/age"
"github.com/fluxcd/kustomize-controller/internal/sops/azkv"
intkeyservice "github.com/fluxcd/kustomize-controller/internal/sops/keyservice"
"github.com/fluxcd/kustomize-controller/internal/sops/pgp"
)

const (
Expand All @@ -58,30 +58,30 @@ const (
type KustomizeDecryptor struct {
client.Client

kustomization kustomizev1.Kustomization
homeDir string
ageIdentities []string
vaultToken string
azureAADConfig *azkv.AADConfig
kustomization kustomizev1.Kustomization
gnuPGHome pgp.GnuPGHome
ageIdentities age.ParsedIdentities
vaultToken string
azureToken *azkv.Token
}

func NewDecryptor(kubeClient client.Client,
kustomization kustomizev1.Kustomization, homeDir string) *KustomizeDecryptor {
kustomization kustomizev1.Kustomization, gnuPGHome string) *KustomizeDecryptor {
return &KustomizeDecryptor{
Client: kubeClient,
kustomization: kustomization,
homeDir: homeDir,
gnuPGHome: pgp.GnuPGHome(gnuPGHome),
}
}

func NewTempDecryptor(kubeClient client.Client,
kustomization kustomizev1.Kustomization) (*KustomizeDecryptor, func(), error) {
tmpDir, err := os.MkdirTemp("", fmt.Sprintf("decryptor-%s-", kustomization.Name))
gnuPGHome, err := pgp.NewGnuPGHome()
if err != nil {
return nil, nil, fmt.Errorf("tmp dir error: %w", err)
return nil, nil, fmt.Errorf("cannot create decryptor: %w", err)
}
cleanup := func() { os.RemoveAll(tmpDir) }
return NewDecryptor(kubeClient, kustomization, tmpDir), cleanup, nil
cleanup := func() { os.RemoveAll(gnuPGHome.String()) }
return NewDecryptor(kubeClient, kustomization, gnuPGHome.String()), cleanup, nil
}

func (kd *KustomizeDecryptor) Decrypt(res *resource.Resource) (*resource.Resource, error) {
Expand Down Expand Up @@ -116,7 +116,7 @@ func (kd *KustomizeDecryptor) Decrypt(res *resource.Resource) (*resource.Resourc

data, err := base64.StdEncoding.DecodeString(value)
if err != nil {
fmt.Println("Base64 Decode: %w", err)
return nil, fmt.Errorf("Base64 Decode: %w", err)
}

if bytes.Contains(data, []byte("sops")) && bytes.Contains(data, []byte("ENC[")) {
Expand Down Expand Up @@ -151,68 +151,42 @@ func (kd *KustomizeDecryptor) ImportKeys(ctx context.Context) error {
return fmt.Errorf("decryption secret error: %w", err)
}

tmpDir, err := os.MkdirTemp("", kd.kustomization.Name)
if err != nil {
return fmt.Errorf("tmp dir error: %w", err)
}
defer os.RemoveAll(tmpDir)

var ageIdentities []string
var vaultToken string
var err error
for name, value := range secret.Data {
switch filepath.Ext(name) {
case ".asc":
keyPath, err := securejoin.SecureJoin(tmpDir, name)
if err != nil {
return err
}
if err := os.WriteFile(keyPath, value, os.ModePerm); err != nil {
return fmt.Errorf("unable to write key to storage: %w", err)
}
if err := kd.gpgImport(keyPath); err != nil {
return err
if err = kd.gnuPGHome.Import(value); err != nil {
return fmt.Errorf("failed to import '%s' data from Secret '%s': %w", name, secretName, err)
}
case ".agekey":
ageIdentities = append(ageIdentities, string(value))
if err = kd.ageIdentities.Import(string(value)); err != nil {
return fmt.Errorf("failed to import '%s' data from Secret '%s': %w", name, secretName, err)
}
case filepath.Ext(DecryptionVaultTokenFileName):
// Make sure we have the absolute name
if name == DecryptionVaultTokenFileName {
token := string(value)
token = strings.Trim(strings.TrimSpace(token), "\n")
vaultToken = token
kd.vaultToken = token
}
case filepath.Ext(DecryptionAzureAuthFile):
// Make sure we have the absolute name
if name == DecryptionAzureAuthFile {
azureConf := azkv.AADConfig{}
if err = azkv.LoadAADConfigFromBytes(value, &azureConf); err != nil {
return err
conf := azkv.AADConfig{}
if err = azkv.LoadAADConfigFromBytes(value, &conf); err != nil {
return fmt.Errorf("failed to import '%s' data from Secret '%s': %w", name, secretName, err)
}
if kd.azureToken, err = azkv.TokenFromAADConfig(conf); err != nil {
return fmt.Errorf("failed to import '%s' data from Secret '%s': %w", name, secretName, err)
}
kd.azureAADConfig = &azureConf
}
}
}

kd.ageIdentities = ageIdentities
kd.vaultToken = vaultToken
}

return nil
}

func (kd *KustomizeDecryptor) gpgImport(path string) error {
args := []string{"--batch", "--import", path}
if kd.homeDir != "" {
args = append([]string{"--homedir", kd.homeDir}, args...)
}
cmd := exec.Command("gpg", args...)
out, err := cmd.CombinedOutput()
if err != nil {
return fmt.Errorf("gpg import error: %s", string(out))
}
return nil
}

func (kd *KustomizeDecryptor) decryptDotEnvFiles(dirpath string) error {
kustomizePath := filepath.Join(dirpath, konfig.DefaultKustomizationFileName())
ksData, err := os.ReadFile(kustomizePath)
Expand Down Expand Up @@ -284,9 +258,18 @@ func (kd KustomizeDecryptor) DataWithFormat(data []byte, inputFormat, outputForm
return nil, fmt.Errorf("LoadEncryptedFile: %w", err)
}

serverOpts := []intkeyservice.ServerOption{
intkeyservice.WithGnuPGHome(kd.gnuPGHome),
intkeyservice.WithVaultToken(kd.vaultToken),
intkeyservice.WithAgeIdentities(kd.ageIdentities),
}
if kd.azureToken != nil {
serverOpts = append(serverOpts, intkeyservice.WithAzureToken{Token: kd.azureToken})
}

metadataKey, err := tree.Metadata.GetDataKeyWithKeyServices(
[]keyservice.KeyServiceClient{
intkeyservice.NewLocalClient(intkeyservice.NewServer(false, kd.homeDir, kd.vaultToken, kd.ageIdentities, kd.azureAADConfig)),
intkeyservice.NewLocalClient(intkeyservice.NewServer(serverOpts...)),
},
)
if err != nil {
Expand Down
1 change: 0 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ require (
github.com/Azure/azure-sdk-for-go/sdk/azcore v0.22.0
github.com/Azure/azure-sdk-for-go/sdk/azidentity v0.13.2
github.com/Azure/azure-sdk-for-go/sdk/keyvault/azkeys v0.4.0
github.com/ProtonMail/go-crypto v0.0.0-20220113124808-70ae35bab23f
github.com/cyphar/filepath-securejoin v0.2.3
github.com/dimchansky/utfbom v1.1.1
github.com/drone/envsubst v1.0.3
Expand Down
3 changes: 0 additions & 3 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -121,8 +121,6 @@ github.com/NYTimes/gziphandler v1.1.1/go.mod h1:n/CVRwUEOgIxrgPvAQhUUr9oeUtvrhMo
github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5 h1:TngWCqHvy9oXAN6lEVMRuU21PR1EtLVZJmdB18Gu3Rw=
github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5/go.mod h1:lmUJ/7eu/Q8D7ML55dXQrVaamCz2vxCfdQBasLZfHKk=
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
github.com/ProtonMail/go-crypto v0.0.0-20220113124808-70ae35bab23f h1:J2FzIrXN82q5uyUraeJpLIm7U6PffRwje2ORho5yIik=
github.com/ProtonMail/go-crypto v0.0.0-20220113124808-70ae35bab23f/go.mod h1:z4/9nQmJSSwwds7ejkxaJwO37dru3geImFUdJlaLzQo=
github.com/PuerkitoBio/purell v1.1.1 h1:WEQqlqaGbrPkxLJWfBwQmfEAE1Z7ONdDLqrN38tNFfI=
github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 h1:d+Bc7a5rLufV/sSk/8dngufqelfh6jnri85riMAaF/M=
Expand Down Expand Up @@ -917,7 +915,6 @@ golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8U
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20210817164053-32db794688a5/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519 h1:7I4JAnoQBe7ZtJcBaYHi5UtiO8tQHbUSXxL+pnGRANg=
Expand Down
Loading