Skip to content
Open
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
80 changes: 80 additions & 0 deletions cconfig/tree.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
package cconfig

import (
secretmanager "cloud.google.com/go/secretmanager/apiv1"
"cloud.google.com/go/secretmanager/apiv1/secretmanagerpb"
"context"
"hash/crc32"
"html/template"
"os"
"path/filepath"
Expand Down Expand Up @@ -113,6 +117,12 @@ func loadTree(fp, overrides string, disableKeyOverrides bool) (*toml.Tree, error
})
}
}

err = loadSecrets(tree)
if err != nil {
return nil, cerrors.New(err, "failed to load secrets", nil)
}

return tree, nil
}

Expand Down Expand Up @@ -181,3 +191,73 @@ func mergeTrees(base, override *toml.Tree, disableKeyOverrides bool) (*toml.Tree

return base, nil
}

func loadSecrets(tree *toml.Tree) error {
for _, key := range tree.Keys() {
switch keyVal := tree.Get(key).(type) {
case *toml.Tree:
err := loadSecrets(keyVal)
if err != nil {
return cerrors.New(err, "failed to load secrets for key", map[string]interface{}{
"key": key,
})
}

case string:
if strings.HasPrefix(keyVal, "google-secret-manager:") {
secretName := strings.TrimPrefix(keyVal, "google-secret-manager:")
secretVal, err := accessGoogleSecretVersion(secretName)
if err != nil {
return cerrors.New(err, "failed to get secret", map[string]interface{}{
"secretName": secretName,
})
}

tree.Set(key, secretVal)
}
}
}
return nil
}

// accessGoogleSecretVersion accesses the payload for the given secret version if one
// exists. The version can be a version number as a string (e.g. "5") or an
// alias (e.g. "latest").
func accessGoogleSecretVersion(name string) (string, error) {
// name := "projects/my-project/secrets/my-secret/versions/5"
// name := "projects/my-project/secrets/my-secret/versions/latest"

// Create the client.
ctx := context.Background()
client, err := secretmanager.NewClient(ctx)
if err != nil {
return "", cerrors.New(err, "create secretmanager", nil)
}
defer client.Close()

// Build the request.
req := &secretmanagerpb.AccessSecretVersionRequest{
Name: name,
}

// Call the API.
result, err := client.AccessSecretVersion(ctx, req)
if err != nil {
return "", cerrors.New(err, "failed to access secret version", map[string]interface{}{
"name": name,
})
}

// Verify the data checksum.
crc32c := crc32.MakeTable(crc32.Castagnoli)
checksum := int64(crc32.Checksum(result.Payload.Data, crc32c))
if checksum != *result.Payload.DataCrc32C {
return "", cerrors.New(err, "Data corruption detected.", map[string]interface{}{
"name": name,
"computed_checksum": checksum,
"data_checksum": *result.Payload.DataCrc32C,
})
}

return string(result.Payload.Data), nil
}
9 changes: 5 additions & 4 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,19 @@ module github.com/gocopper/copper
go 1.16

require (
cloud.google.com/go/secretmanager v1.11.4
github.com/Masterminds/sprig/v3 v3.2.3
github.com/asaskevich/govalidator v0.0.0-20180720115003-f9ffefc3facf
github.com/google/uuid v1.1.2
github.com/google/uuid v1.4.0
github.com/google/wire v0.5.0
github.com/gorilla/context v1.1.1 // indirect
github.com/gorilla/mux v1.6.2
github.com/jmoiron/sqlx v1.3.5
github.com/lib/pq v1.10.2 // indirect
github.com/mattn/go-sqlite3 v1.14.12
github.com/mattn/go-sqlite3 v1.14.15
github.com/pelletier/go-toml v1.9.3
github.com/pkg/errors v0.9.1 // indirect
github.com/rubenv/sql-migrate v1.1.2
github.com/stretchr/testify v1.7.0
github.com/stretchr/testify v1.8.4
go.uber.org/zap v1.21.0
google.golang.org/genproto v0.0.0-20231212172506-995d672761c0 // indirect
)
Loading