Skip to content

Commit 3650278

Browse files
authored
Merge branch 'master' into exit-early
2 parents 7d6d805 + ff49794 commit 3650278

File tree

5 files changed

+99
-7
lines changed

5 files changed

+99
-7
lines changed

README.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,15 @@ MY_DB_PASSWORD=gcp:secretmanager:projects/$PROJECT_ID/secrets/mydbpassword/versi
7070
MY_DB_PASSWORD=very-secret-password
7171
```
7272

73+
#### Project auto-detection
74+
75+
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.
76+
77+
```sh
78+
MY_DB_PASSWORD=mydbpassword
79+
MY_DB_PASSWORD=mydbpassword/versions/2
80+
```
81+
7382
### Requirement
7483

7584
#### Container

main.go

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,9 @@ func main() {
5353
Name: "exit-early",
5454
Usage: "exit when a provider fails or a secret is not found",
5555
EnvVars: []string{"EXIT_EARLY"},
56+
&cli.StringFlag{
57+
Name: "google-project",
58+
Usage: "the google cloud project for secrets without a project prefix",
5659
},
5760
},
5861
Commands: []*cli.Command{
@@ -134,7 +137,7 @@ func mainCmd(c *cli.Context) error {
134137
if c.String("provider") == "aws" {
135138
provider, err = aws.NewAwsSecretsProvider()
136139
} else if c.String("provider") == "google" {
137-
provider, err = google.NewGoogleSecretsProvider(ctx)
140+
provider, err = google.NewGoogleSecretsProvider(ctx, c.String("google-project"))
138141
}
139142
if err != nil {
140143
log.WithField("provider", c.String("provider")).WithError(err).Error("failed to initialize secrets provider")
@@ -242,8 +245,12 @@ func run(ctx context.Context, provider secrets.Provider, exitEarly bool, command
242245
// Goroutine for signals forwarding
243246
go func() {
244247
for sig := range sigs {
245-
// ignore SIGCHLD signals since these are only useful for secrets-init
246-
if sig != syscall.SIGCHLD {
248+
// ignore:
249+
// - SIGCHLD signals, since these are only useful for secrets-init
250+
// - SIGURG signals, since they are used internally by the secrets-init
251+
// go runtime (see https://github.com/golang/go/issues/37942) and are of
252+
// no interest to the child process
253+
if sig != syscall.SIGCHLD && sig != syscall.SIGURG {
247254
// forward signal to the main process and its children
248255
e := syscall.Kill(-cmd.Process.Pid, sig.(syscall.Signal))
249256
if e != nil {

pkg/secrets/aws/secrets.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ func (sp *SecretsProvider) ResolveSecrets(_ context.Context, vars []string) ([]s
5151
for _, env := range vars {
5252
kv := strings.Split(env, "=")
5353
key, value := kv[0], kv[1]
54-
if strings.HasPrefix(value, "arn:aws:secretsmanager") {
54+
if strings.HasPrefix(value, "arn:aws:secretsmanager") || strings.HasPrefix(value, "arn:aws-cn:secretsmanager") {
5555
// get secret value
5656
secret, err := sp.sm.GetSecretValue(&secretsmanager.GetSecretValueInput{SecretId: &value})
5757
if err != nil {
@@ -71,7 +71,7 @@ func (sp *SecretsProvider) ResolveSecrets(_ context.Context, vars []string) ([]s
7171
} else {
7272
env = key + "=" + *secret.SecretString
7373
}
74-
} else if strings.HasPrefix(value, "arn:aws:ssm") && strings.Contains(value, ":parameter/") {
74+
} else if (strings.HasPrefix(value, "arn:aws:ssm") || strings.HasPrefix(value, "arn:aws-cn:ssm")) && strings.Contains(value, ":parameter/") {
7575
tokens := strings.Split(value, ":")
7676
// valid parameter ARN arn:aws:ssm:REGION:ACCOUNT:parameter/PATH
7777
// or arn:aws:ssm:REGION:ACCOUNT:parameter/PATH:VERSION

pkg/secrets/google/secrets.go

Lines changed: 32 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,24 +2,39 @@ package google
22

33
import (
44
"context"
5+
"fmt"
6+
"regexp"
57
"strings"
68

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

11+
"cloud.google.com/go/compute/metadata"
912
secretmanager "cloud.google.com/go/secretmanager/apiv1"
1013
"github.com/pkg/errors"
14+
log "github.com/sirupsen/logrus"
1115
secretspb "google.golang.org/genproto/googleapis/cloud/secretmanager/v1" //nolint:gci
1216
)
1317

1418
// SecretsProvider Google Cloud secrets provider
1519
type SecretsProvider struct {
16-
sm SecretsManagerAPI
20+
sm SecretsManagerAPI
21+
projectID string
1722
}
1823

1924
// NewGoogleSecretsProvider init Google Secrets Provider
20-
func NewGoogleSecretsProvider(ctx context.Context) (secrets.Provider, error) {
25+
func NewGoogleSecretsProvider(ctx context.Context, projectID string) (secrets.Provider, error) {
2126
sp := SecretsProvider{}
2227
var err error
28+
29+
if projectID != "" {
30+
sp.projectID = projectID
31+
} else {
32+
sp.projectID, err = metadata.ProjectID()
33+
if err != nil {
34+
log.WithError(err).Infoln("The Google project cannot be detected, you won't be able to use the short secret version")
35+
}
36+
}
37+
2338
sp.sm, err = secretmanager.NewClient(ctx)
2439
if err != nil {
2540
return nil, errors.Wrap(err, "failed to initialize Google Cloud SDK")
@@ -33,14 +48,29 @@ func NewGoogleSecretsProvider(ctx context.Context) (secrets.Provider, error) {
3348
//
3449
// `gcp:secretmanager:projects/{PROJECT_ID}/secrets/{SECRET_NAME}`
3550
// `gcp:secretmanager:projects/{PROJECT_ID}/secrets/{SECRET_NAME}/versions/{VERSION|latest}`
51+
// `gcp:secretmanager:{SECRET_NAME}
52+
// `gcp:secretmanager:{SECRET_NAME}/versions/{VERSION|latest}`
3653
func (sp SecretsProvider) ResolveSecrets(ctx context.Context, vars []string) ([]string, error) {
3754
envs := make([]string, 0, len(vars))
55+
56+
fullSecretRe := regexp.MustCompile("projects/[^/]+/secrets/[^/+](/version/[^/+])?")
57+
3858
for _, env := range vars {
3959
kv := strings.Split(env, "=")
4060
key, value := kv[0], kv[1]
4161
if strings.HasPrefix(value, "gcp:secretmanager:") {
4262
// construct valid secret name
4363
name := strings.TrimPrefix(value, "gcp:secretmanager:")
64+
65+
isLong := fullSecretRe.MatchString(name)
66+
67+
if !isLong {
68+
if sp.projectID == "" {
69+
return vars, errors.Errorf("failed to get secret \"%s\" from Google Secret Manager (unknown project)", name)
70+
}
71+
name = fmt.Sprintf("projects/%s/secrets/%s", sp.projectID, name)
72+
}
73+
4474
// if no version specified add latest
4575
if !strings.Contains(name, "/versions/") {
4676
name += "/versions/latest"

pkg/secrets/google/secrets_test.go

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,29 @@ func TestSecretsProvider_ResolveSecrets(t *testing.T) {
5252
return &sp
5353
},
5454
},
55+
{
56+
name: "get implicit (latest) version single secret from Secrets Manager with the shorthand syntax",
57+
args: args{
58+
ctx: context.TODO(),
59+
vars: []string{
60+
"test-secret=gcp:secretmanager:test-secret",
61+
},
62+
},
63+
want: []string{
64+
"test-secret=test-secret-value",
65+
},
66+
mockServiceProvider: func(ctx context.Context, mockSM *mocks.GoogleSecretsManagerAPI) secrets.Provider {
67+
sp := SecretsProvider{sm: mockSM, projectID: "test-project-id"}
68+
req := secretspb.AccessSecretVersionRequest{
69+
Name: "projects/test-project-id/secrets/test-secret/versions/latest",
70+
}
71+
res := secretspb.AccessSecretVersionResponse{Payload: &secretspb.SecretPayload{
72+
Data: []byte("test-secret-value"),
73+
}}
74+
mockSM.On("AccessSecretVersion", ctx, &req).Return(&res, nil)
75+
return &sp
76+
},
77+
},
5578
{
5679
name: "get explicit single secret version from Secrets Manager",
5780
args: args{
@@ -75,6 +98,29 @@ func TestSecretsProvider_ResolveSecrets(t *testing.T) {
7598
return &sp
7699
},
77100
},
101+
{
102+
name: "get explicit single secret version from Secrets Manager with the shorthand syntax",
103+
args: args{
104+
ctx: context.TODO(),
105+
vars: []string{
106+
"test-secret=gcp:secretmanager:test-secret/versions/5",
107+
},
108+
},
109+
want: []string{
110+
"test-secret=test-secret-value",
111+
},
112+
mockServiceProvider: func(ctx context.Context, mockSM *mocks.GoogleSecretsManagerAPI) secrets.Provider {
113+
sp := SecretsProvider{sm: mockSM, projectID: "test-project-id"}
114+
req := secretspb.AccessSecretVersionRequest{
115+
Name: "projects/test-project-id/secrets/test-secret/versions/5",
116+
}
117+
res := secretspb.AccessSecretVersionResponse{Payload: &secretspb.SecretPayload{
118+
Data: []byte("test-secret-value"),
119+
}}
120+
mockSM.On("AccessSecretVersion", ctx, &req).Return(&res, nil)
121+
return &sp
122+
},
123+
},
78124
{
79125
name: "get 2 secrets from Secrets Manager",
80126
args: args{

0 commit comments

Comments
 (0)