Skip to content

Commit 566b812

Browse files
authored
feat: Provide external shell command to retrieve AWS credentials (#37)
* feat: Provide external shell command to retrieve AWS credentials * resolve bkneis' comment(s)
1 parent c3bc5aa commit 566b812

File tree

4 files changed

+50
-4
lines changed

4 files changed

+50
-4
lines changed

cmd/init.go

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ package cmd
33
import (
44
"context"
55

6-
AwsConfig "github.com/aws/aws-sdk-go-v2/config"
76
"github.com/loft-sh/devpod-provider-aws/pkg/aws"
87
"github.com/loft-sh/devpod-provider-aws/pkg/options"
98
"github.com/loft-sh/devpod/pkg/log"
@@ -43,7 +42,7 @@ func (cmd *InitCmd) Run(
4342
return err
4443
}
4544

46-
cfg, err := AwsConfig.LoadDefaultConfig(ctx)
45+
cfg, err := aws.NewAWSConfig(ctx, logs, config)
4746
if err != nil {
4847
return err
4948
}

hack/provider/provider.yaml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,10 @@ optionGroups:
3434
- INJECT_GIT_CREDENTIALS
3535
name: "Agent options"
3636
defaultVisible: false
37+
- options:
38+
- CUSTOM_AWS_CREDENTIAL_COMMAND
39+
name: "Credential handling options"
40+
defaultVisible: true
3741
options:
3842
AWS_REGION:
3943
suggestions:
@@ -272,6 +276,9 @@ options:
272276
AGENT_PATH:
273277
description: The path where to inject the DevPod agent to.
274278
default: /var/lib/toolbox/devpod
279+
CUSTOM_AWS_CREDENTIAL_COMMAND:
280+
description: "Shell command which is executed to get the AWS credentials. The command must return a json containing the keys `AccessKeyID` (required), `SecretAccessKey` (required) and `SessionToken` (optional)."
281+
default: ""
275282
agent:
276283
path: ${AGENT_PATH}
277284
inactivityTimeout: ${INACTIVITY_TIMEOUT}

pkg/aws/aws.go

Lines changed: 38 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,15 @@
11
package aws
22

33
import (
4+
"bytes"
45
"context"
56
"encoding/base64"
7+
"encoding/json"
68
"fmt"
9+
"github.com/aws/aws-sdk-go-v2/credentials"
10+
"github.com/sirupsen/logrus"
711
"net/http"
12+
"os/exec"
813
"regexp"
914
"sort"
1015
"strings"
@@ -46,7 +51,7 @@ func NewProvider(ctx context.Context, logs log.Logger) (*AwsProvider, error) {
4651
return nil, err
4752
}
4853

49-
cfg, err := awsConfig.LoadDefaultConfig(ctx)
54+
cfg, err := NewAWSConfig(ctx, logs, config)
5055
if err != nil {
5156
return nil, err
5257
}
@@ -80,6 +85,38 @@ func NewProvider(ctx context.Context, logs log.Logger) (*AwsProvider, error) {
8085
return provider, nil
8186
}
8287

88+
func NewAWSConfig(ctx context.Context, logs log.Logger, options *options.Options) (aws.Config, error) {
89+
var opts []func(*awsConfig.LoadOptions) error
90+
if options.CustomCredentialCommand != "" {
91+
var output bytes.Buffer
92+
cmd := exec.Command("sh", "-c", options.CustomCredentialCommand)
93+
cmd.Stdout = &output
94+
cmd.Stderr = logs.Writer(logrus.ErrorLevel, true)
95+
if err := cmd.Run(); err != nil {
96+
return aws.Config{}, fmt.Errorf("run command %q: %w", options.CustomCredentialCommand, err)
97+
}
98+
99+
// parse the JSON output to an aws.Credentials object
100+
var creds aws.Credentials
101+
if err := json.Unmarshal(output.Bytes(), &creds); err != nil {
102+
return aws.Config{}, fmt.Errorf("parse AWS credential JSON output %q: %w", output.Bytes(), err)
103+
}
104+
105+
if creds.AccessKeyID == "" || creds.SecretAccessKey == "" {
106+
return aws.Config{}, fmt.Errorf("missing access key id or secret access key in JSON output %q", output.Bytes())
107+
}
108+
109+
// we managed to parse credentials from the external source. Let's use them through a credentials provider
110+
opts = append(opts, awsConfig.WithCredentialsProvider(credentials.StaticCredentialsProvider{Value: creds}))
111+
}
112+
113+
cfg, err := awsConfig.LoadDefaultConfig(ctx, opts...)
114+
if err != nil {
115+
return aws.Config{}, err
116+
}
117+
return cfg, nil
118+
}
119+
83120
type AwsProvider struct {
84121
Config *options.Options
85122
AwsConfig aws.Config

pkg/options/options.go

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ var (
2424
AWS_KMS_KEY_ARN_FOR_SESSION_MANAGER = "AWS_KMS_KEY_ARN_FOR_SESSION_MANAGER"
2525
AWS_USE_ROUTE53 = "AWS_USE_ROUTE53"
2626
AWS_ROUTE53_ZONE_NAME = "AWS_ROUTE53_ZONE_NAME"
27+
CUSTOM_AWS_CREDENTIAL_COMMAND = "CUSTOM_AWS_CREDENTIAL_COMMAND"
2728
)
2829

2930
type Options struct {
@@ -46,12 +47,14 @@ type Options struct {
4647
KmsKeyARNForSessionManager string
4748
UseRoute53Hostnames bool
4849
Route53ZoneName string
50+
CustomCredentialCommand string
4951
}
5052

5153
func FromEnv(init bool) (*Options, error) {
5254
retOptions := &Options{}
5355

5456
var err error
57+
retOptions.CustomCredentialCommand = os.Getenv(CUSTOM_AWS_CREDENTIAL_COMMAND)
5558

5659
retOptions.MachineType, err = fromEnvOrError(AWS_INSTANCE_TYPE)
5760
if err != nil {
@@ -84,7 +87,7 @@ func FromEnv(init bool) (*Options, error) {
8487
retOptions.UseRoute53Hostnames = os.Getenv(AWS_USE_ROUTE53) == "true"
8588
retOptions.Route53ZoneName = os.Getenv(AWS_ROUTE53_ZONE_NAME)
8689

87-
// Return eraly if we're just doing init
90+
// Return early if we're just doing init
8891
if init {
8992
return retOptions, nil
9093
}

0 commit comments

Comments
 (0)