Skip to content

Commit

Permalink
fix: Reworked file provider logic, made the code more cleaner, improv…
Browse files Browse the repository at this point in the history
…ed tests

Signed-off-by: Bence Csati <[email protected]>
  • Loading branch information
csatib02 committed Nov 24, 2023
1 parent fc8e949 commit 29cad09
Show file tree
Hide file tree
Showing 6 changed files with 218 additions and 184 deletions.
8 changes: 7 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,14 @@ require (
github.com/spf13/cast v1.5.1
)

require (
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)

require (
github.com/samber/lo v1.38.1 // indirect
github.com/stretchr/testify v1.8.4
golang.org/x/exp v0.0.0-20230522175609-2e198f4a06a1 // indirect
gopkg.in/yaml.v3 v3.0.1
)
6 changes: 6 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/frankban/quicktest v1.14.4 h1:g2rn0vABPOOXmZUj+vbmUp0lPoXEMuhTpIluN0XL9UY=
github.com/frankban/quicktest v1.14.4/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
Expand All @@ -6,6 +8,8 @@ github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8=
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
github.com/samber/lo v1.38.1 h1:j2XEAqXKb09Am4ebOg31SpvzUTTs6EN3VfgeLUhPdXM=
Expand All @@ -16,6 +20,8 @@ github.com/samber/slog-syslog v1.0.0 h1:4tf8sNv9+qTQ6Fj8+N6U1ZEtUbqbAIzd+q26/Neg
github.com/samber/slog-syslog v1.0.0/go.mod h1:jjupk+yHPVSuXuGhKleoClYc/HEaC+Ro5X4YYeBrt6g=
github.com/spf13/cast v1.5.1 h1:R+kOtfhWQE6TVQzY+4D7wJLBgkdVasCEFxSUBYBYIlA=
github.com/spf13/cast v1.5.1/go.mod h1:b9PdjNptOpzXr7Rq1q9gJML/2cdGQAo69NKzQ10KN48=
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
golang.org/x/exp v0.0.0-20230522175609-2e198f4a06a1 h1:k/i9J1pBpvlfR+9QsetwPyERsqu1GIbi967PQMq3Ivc=
golang.org/x/exp v0.0.0-20230522175609-2e198f4a06a1/go.mod h1:V1LtkGg67GoY2N1AnLN78QLrzxkLyJw7RJb1gzOOz9w=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
Expand Down
77 changes: 66 additions & 11 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ import (
func NewProvider(providerName string) (provider.Provider, error) {
switch providerName {
case file.ProviderName:
provider, err := file.NewProvider(os.Getenv("SECRETS_FILE_PATH"))
provider, err := file.NewProvider(os.DirFS("/secrets"))
if err != nil {
return nil, err
}
Expand All @@ -51,6 +51,59 @@ func NewProvider(providerName string) (provider.Provider, error) {
}
}

func CreateMapOfEnvs() map[string]string {
environ := make(map[string]string, len(os.Environ()))
for _, env := range os.Environ() {
split := strings.SplitN(env, "=", 2)
name := split[0]
value := split[1]
environ[name] = value
}

return environ
}

func ExtractPathsFromEnvs(envs map[string]string) []string {
var secretPaths []string

for _, path := range envs {
if strings.HasPrefix(path, "file:") {
path = strings.TrimPrefix(path, "file://")
secretPaths = append(secretPaths, path)
}
}

return secretPaths
}

func CreateEnvsFromLoadedSecrets(envs map[string]string, secrets []string) ([]string, error) {
// Reverse the map so we can match
// the environment variable key to the secret
// by using the secret path
reversedEnvs := make(map[string]string)
for envKey, path := range envs {
if strings.HasPrefix(path, "file:") {
path = strings.TrimPrefix(path, "file://")
reversedEnvs[path] = envKey
}
}

var secretsEnv []string
for _, secret := range secrets {
split := strings.SplitN(secret, "|", 2)
secretPath := split[0]

secretValue := split[1]
secretKey, ok := reversedEnvs[secretPath]
if !ok {
return nil, fmt.Errorf("failed to find environment variable key for secret path: %s", secretPath)
}
secretsEnv = append(secretsEnv, fmt.Sprintf("%s=%s", secretKey, secretValue))
}

return secretsEnv, nil
}

func main() {
var logger *slog.Logger
{
Expand Down Expand Up @@ -136,22 +189,24 @@ func main() {
os.Exit(1)
}

environ := make(map[string]string, len(os.Environ()))
for _, env := range os.Environ() {
split := strings.SplitN(env, "=", 2)
name := split[0]
value := split[1]
environ[name] = value
}
environ := CreateMapOfEnvs()
paths := ExtractPathsFromEnvs(environ)

ctx := context.Background()
envs, err := provider.LoadSecrets(ctx, environ)
secrets, err := provider.LoadSecrets(ctx, paths)
if err != nil {
logger.Error(fmt.Errorf("failed to load secrets from provider: %w", err).Error())

os.Exit(1)
}

secretsEnv, err := CreateEnvsFromLoadedSecrets(environ, secrets)
if err != nil {
logger.Error(fmt.Errorf("failed to create environment variables from loaded secrets: %w", err).Error())

os.Exit(1)
}

sigs := make(chan os.Signal, 1)

if delayExec > 0 {
Expand All @@ -164,7 +219,7 @@ func main() {
if daemonMode {
logger.Info("in daemon mode...")
cmd := exec.Command(binary, entrypointCmd[1:]...)
cmd.Env = append(os.Environ(), envs...)
cmd.Env = append(os.Environ(), secretsEnv...)
cmd.Stdin = os.Stdin
cmd.Stderr = os.Stderr
cmd.Stdout = os.Stdout
Expand Down Expand Up @@ -213,7 +268,7 @@ func main() {

os.Exit(cmd.ProcessState.ExitCode())
}
err = syscall.Exec(binary, entrypointCmd, envs)
err = syscall.Exec(binary, entrypointCmd, secretsEnv)
if err != nil {
logger.Error(fmt.Errorf("failed to exec process: %w", err).Error(), slog.String("entrypoint", fmt.Sprint(entrypointCmd)))

Expand Down
78 changes: 48 additions & 30 deletions provider/file/file.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,62 +17,80 @@ package file
import (
"context"
"fmt"
"os"
"strings"

"gopkg.in/yaml.v3"
"io/fs"

"github.com/bank-vaults/secret-init/provider"
)

const ProviderName = "file"

type Provider struct {
secretsFilePath string
fs fs.FS
}

func NewProvider(secretsFilePath string) (provider.Provider, error) {
return &Provider{secretsFilePath: secretsFilePath}, nil
}
func NewProvider(fs fs.FS) (provider.Provider, error) {
if fs == nil {
return nil, fmt.Errorf("file system is nil")
}

func (provider *Provider) LoadSecrets(_ context.Context, envs map[string]string) ([]string, error) {
// extract secrets from the file to a map
secretsMap, err := provider.getSecretsFromFile()
isEmpty, err := isFileSystemEmpty(fs)
if err != nil {

return nil, fmt.Errorf("failed to get secrets from file: %w", err)
return nil, fmt.Errorf("failed to check if file system is empty: %w", err)
}
if isEmpty {
return nil, fmt.Errorf("file system is empty")
}

return &Provider{fs: fs}, nil
}

func (provider *Provider) LoadSecrets(_ context.Context, paths []string) ([]string, error) {
var secrets []string
for envKey, envValue := range envs {
if strings.HasPrefix(envValue, "file:") {
// Check if the requested secret is in the loaded secret map
envValue = strings.TrimPrefix(envValue, "file:")
secret, ok := secretsMap[envValue]
if !ok {

return nil, fmt.Errorf("secret %s not found", envKey)
}
secrets = append(secrets, fmt.Sprintf("%s=%s", envKey, secret))

for i, path := range paths {
secret, err := provider.getSecretFromFile(path)
if err != nil {
return nil, fmt.Errorf("failed to get secret from file: %w", err)
}
// Add the secret path with a "|" separator character
// to the secrets slice along with the secret
// so later we can match it to the environment key
secrets = append(secrets, paths[i]+"|"+secret)
}

return secrets, nil
}

func (provider *Provider) getSecretsFromFile() (map[string]string, error) {
data, err := os.ReadFile(provider.secretsFilePath)
func isFileSystemEmpty(fsys fs.FS) (bool, error) {
dir, err := fs.ReadDir(fsys, ".")
fmt.Println(dir, err)
if err != nil {
return false, err
}

return nil, fmt.Errorf("failed to read secrets file: %w", err)
for _, entry := range dir {
if entry.IsDir() || entry.Type().IsRegular() {
return false, nil
}
}

secretsMap := make(map[string]string)
err = yaml.Unmarshal(data, &secretsMap)
return true, nil
}

func (provider *Provider) getSecretFromFile(path string) (string, error) {
content, err := provider.readFile(path)
if err != nil {
return "", err
}

return string(content), nil
}

return nil, fmt.Errorf("failed to unmarshal YAML: %w", err)
func (provider *Provider) readFile(path string) ([]byte, error) {
content, err := fs.ReadFile(provider.fs, path)
if err != nil {
return nil, fmt.Errorf("failed to read file: %w", err)
}

return secretsMap, nil
return content, nil
}
Loading

0 comments on commit 29cad09

Please sign in to comment.