Skip to content

Commit

Permalink
Merge pull request #35 from n-e/short-secrets
Browse files Browse the repository at this point in the history
Allow omitting the project id in google secrets
  • Loading branch information
alexei-led committed Sep 6, 2023
2 parents 6b56dca + 3241b3f commit ff49794
Show file tree
Hide file tree
Showing 4 changed files with 92 additions and 3 deletions.
9 changes: 9 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,15 @@ MY_DB_PASSWORD=gcp:secretmanager:projects/$PROJECT_ID/secrets/mydbpassword/versi
MY_DB_PASSWORD=very-secret-password
```

#### Project auto-detection

If secret-manager is running in an environment where the Google metadata server is available, or the `-google-project` flag is set, the secret path may be omitted, and the current project is used.

```sh
MY_DB_PASSWORD=mydbpassword
MY_DB_PASSWORD=mydbpassword/versions/2
```

### Requirement

#### Container
Expand Down
6 changes: 5 additions & 1 deletion main.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,10 @@ func main() {
Usage: "supported secrets manager provider ['aws', 'google']",
Value: "aws",
},
&cli.StringFlag{
Name: "google-project",
Usage: "the google cloud project for secrets without a project prefix",
},
},
Commands: []*cli.Command{
{
Expand Down Expand Up @@ -129,7 +133,7 @@ func mainCmd(c *cli.Context) error {
if c.String("provider") == "aws" {
provider, err = aws.NewAwsSecretsProvider()
} else if c.String("provider") == "google" {
provider, err = google.NewGoogleSecretsProvider(ctx)
provider, err = google.NewGoogleSecretsProvider(ctx, c.String("google-project"))
}
if err != nil {
log.WithField("provider", c.String("provider")).WithError(err).Error("failed to initialize secrets provider")
Expand Down
34 changes: 32 additions & 2 deletions pkg/secrets/google/secrets.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,24 +2,39 @@ package google

import (
"context"
"fmt"
"regexp"
"strings"

"secrets-init/pkg/secrets" //nolint:gci

"cloud.google.com/go/compute/metadata"
secretmanager "cloud.google.com/go/secretmanager/apiv1"
"github.com/pkg/errors"
log "github.com/sirupsen/logrus"
secretspb "google.golang.org/genproto/googleapis/cloud/secretmanager/v1" //nolint:gci
)

// SecretsProvider Google Cloud secrets provider
type SecretsProvider struct {
sm SecretsManagerAPI
sm SecretsManagerAPI
projectID string
}

// NewGoogleSecretsProvider init Google Secrets Provider
func NewGoogleSecretsProvider(ctx context.Context) (secrets.Provider, error) {
func NewGoogleSecretsProvider(ctx context.Context, projectID string) (secrets.Provider, error) {
sp := SecretsProvider{}
var err error

if projectID != "" {
sp.projectID = projectID
} else {
sp.projectID, err = metadata.ProjectID()
if err != nil {
log.WithError(err).Infoln("The Google project cannot be detected, you won't be able to use the short secret version")
}
}

sp.sm, err = secretmanager.NewClient(ctx)
if err != nil {
return nil, errors.Wrap(err, "failed to initialize Google Cloud SDK")
Expand All @@ -33,14 +48,29 @@ func NewGoogleSecretsProvider(ctx context.Context) (secrets.Provider, error) {
//
// `gcp:secretmanager:projects/{PROJECT_ID}/secrets/{SECRET_NAME}`
// `gcp:secretmanager:projects/{PROJECT_ID}/secrets/{SECRET_NAME}/versions/{VERSION|latest}`
// `gcp:secretmanager:{SECRET_NAME}
// `gcp:secretmanager:{SECRET_NAME}/versions/{VERSION|latest}`
func (sp SecretsProvider) ResolveSecrets(ctx context.Context, vars []string) ([]string, error) {
envs := make([]string, 0, len(vars))

fullSecretRe := regexp.MustCompile("projects/[^/]+/secrets/[^/+](/version/[^/+])?")

for _, env := range vars {
kv := strings.Split(env, "=")
key, value := kv[0], kv[1]
if strings.HasPrefix(value, "gcp:secretmanager:") {
// construct valid secret name
name := strings.TrimPrefix(value, "gcp:secretmanager:")

isLong := fullSecretRe.MatchString(name)

if !isLong {
if sp.projectID == "" {
return vars, errors.Errorf("failed to get secret \"%s\" from Google Secret Manager (unknown project)", name)
}
name = fmt.Sprintf("projects/%s/secrets/%s", sp.projectID, name)
}

// if no version specified add latest
if !strings.Contains(name, "/versions/") {
name += "/versions/latest"
Expand Down
46 changes: 46 additions & 0 deletions pkg/secrets/google/secrets_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,29 @@ func TestSecretsProvider_ResolveSecrets(t *testing.T) {
return &sp
},
},
{
name: "get implicit (latest) version single secret from Secrets Manager with the shorthand syntax",
args: args{
ctx: context.TODO(),
vars: []string{
"test-secret=gcp:secretmanager:test-secret",
},
},
want: []string{
"test-secret=test-secret-value",
},
mockServiceProvider: func(ctx context.Context, mockSM *mocks.GoogleSecretsManagerAPI) secrets.Provider {
sp := SecretsProvider{sm: mockSM, projectID: "test-project-id"}
req := secretspb.AccessSecretVersionRequest{
Name: "projects/test-project-id/secrets/test-secret/versions/latest",
}
res := secretspb.AccessSecretVersionResponse{Payload: &secretspb.SecretPayload{
Data: []byte("test-secret-value"),
}}
mockSM.On("AccessSecretVersion", ctx, &req).Return(&res, nil)
return &sp
},
},
{
name: "get explicit single secret version from Secrets Manager",
args: args{
Expand All @@ -75,6 +98,29 @@ func TestSecretsProvider_ResolveSecrets(t *testing.T) {
return &sp
},
},
{
name: "get explicit single secret version from Secrets Manager with the shorthand syntax",
args: args{
ctx: context.TODO(),
vars: []string{
"test-secret=gcp:secretmanager:test-secret/versions/5",
},
},
want: []string{
"test-secret=test-secret-value",
},
mockServiceProvider: func(ctx context.Context, mockSM *mocks.GoogleSecretsManagerAPI) secrets.Provider {
sp := SecretsProvider{sm: mockSM, projectID: "test-project-id"}
req := secretspb.AccessSecretVersionRequest{
Name: "projects/test-project-id/secrets/test-secret/versions/5",
}
res := secretspb.AccessSecretVersionResponse{Payload: &secretspb.SecretPayload{
Data: []byte("test-secret-value"),
}}
mockSM.On("AccessSecretVersion", ctx, &req).Return(&res, nil)
return &sp
},
},
{
name: "get 2 secrets from Secrets Manager",
args: args{
Expand Down

0 comments on commit ff49794

Please sign in to comment.