Skip to content

Commit 09c4398

Browse files
authored
Merge pull request #54 from buildkite/keithduncan/add-additional-region-fallback
Add additional region configuration and fallback
2 parents aa0812c + 5cbfe01 commit 09c4398

File tree

5 files changed

+74
-78
lines changed

5 files changed

+74
-78
lines changed

git-credential-s3-secrets

Lines changed: 7 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -52,44 +52,12 @@ parse_url() {
5252
done
5353
}
5454

55-
s3_bucket_region() {
56-
local bucket="$1"
57-
58-
local guess_region="${AWS_DEFAULT_REGION:-}"
59-
if [ -z "${guess_region}" ]
60-
then
61-
# This plug-in may not be executing in an AWS VPC or have access to the IDMS
62-
# Fail fast with the --connect-timeout flag
63-
local token=$(curl -X PUT -H "X-aws-ec2-metadata-token-ttl-seconds: 60" --fail --silent --show-error --location --connect-timeout 5 "http://169.254.169.254/latest/api/token")
64-
if [ -n "${token}" ]
65-
then
66-
guess_region=$(curl -H "X-aws-ec2-metadata-token: $token" --fail --silent --show-error --location "http://169.254.169.254/latest/meta-data/placement/region")
67-
fi
68-
fi
69-
if [ -z "${guess_region}" ]
70-
then
71-
guess_region="us-east-1"
72-
fi
73-
74-
# Buckets in us-east-1 have a LocationConstraint of null
75-
# https://docs.aws.amazon.com/cli/latest/reference/s3api/get-bucket-location.html
76-
local bucket_region="$(aws s3api get-bucket-location --bucket "${bucket}" --region "${guess_region}" --output text --query "LocationConstraint || 'us-east-1'")"
77-
78-
echo "${bucket_region}"
79-
}
80-
8155
s3_download() {
8256
local bucket="$1"
83-
local key="$2"
84-
85-
local bucket_region="$(s3_bucket_region "${bucket}")"
86-
if [ -z "${bucket_region}" ]
87-
then
88-
echo "Could not determine the bucket region for ${bucket}" >&2
89-
exit 2
90-
fi
57+
local region="$2"
58+
local key="$3"
9159

92-
local aws_s3_args=("--quiet" "--region=${bucket_region}")
60+
local aws_s3_args=("--quiet" "--region=${region}")
9361

9462
if [[ "${BUILDKITE_USE_KMS:-true}" =~ ^(true|1)$ ]] ; then
9563
aws_s3_args+=("--sse" "aws:kms")
@@ -103,14 +71,15 @@ s3_download() {
10371
}
10472

10573
bucket="$1"
106-
key="$2"
107-
action="${3:-get}"
74+
region="$2"
75+
key="$3"
76+
action="${4:-get}"
10877

10978
# we only support get and we don't parse the stdin params
11079
if [ "$action" == "get" ] ; then
11180

11281
# read git-credentials, which is a list of uris
113-
s3_download "$bucket" "$key" | while read -r uri ; do
82+
s3_download "$bucket" "$region" "$key" | while read -r uri ; do
11483
if ! parse_url "$uri" ; then
11584
echo "Failed to parse uri $uri" >&2
11685
exit 1

s3secrets-helper/env/env.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
package env
2+
3+
const (
4+
EnvBucket = "BUILDKITE_PLUGIN_S3_SECRETS_BUCKET"
5+
EnvRegion = "BUILDKITE_PLUGIN_S3_SECRETS_REGION"
6+
EnvPrefix = "BUILDKITE_PLUGIN_S3_SECRETS_BUCKET_PREFIX"
7+
EnvPipeline = "BUILDKITE_PIPELINE_SLUG"
8+
EnvRepo = "BUILDKITE_REPO"
9+
EnvCredHelper = "BUILDKITE_PLUGIN_S3_SECRETS_CREDHELPER"
10+
)

s3secrets-helper/main.go

Lines changed: 12 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -5,19 +5,12 @@ import (
55
"log"
66
"os"
77

8+
"github.com/buildkite/elastic-ci-stack-s3-secrets-hooks/s3secrets-helper/v2/env"
89
"github.com/buildkite/elastic-ci-stack-s3-secrets-hooks/s3secrets-helper/v2/s3"
910
"github.com/buildkite/elastic-ci-stack-s3-secrets-hooks/s3secrets-helper/v2/secrets"
1011
"github.com/buildkite/elastic-ci-stack-s3-secrets-hooks/s3secrets-helper/v2/sshagent"
1112
)
1213

13-
const (
14-
envBucket = "BUILDKITE_PLUGIN_S3_SECRETS_BUCKET"
15-
envPrefix = "BUILDKITE_PLUGIN_S3_SECRETS_BUCKET_PREFIX"
16-
envPipeline = "BUILDKITE_PIPELINE_SLUG"
17-
envRepo = "BUILDKITE_REPO"
18-
envCredHelper = "BUILDKITE_PLUGIN_S3_SECRETS_CREDHELPER"
19-
)
20-
2114
func main() {
2215
log := log.New(os.Stderr, "", log.Lmsgprefix)
2316
if err := mainWithError(log); err != nil {
@@ -26,33 +19,36 @@ func main() {
2619
}
2720

2821
func mainWithError(log *log.Logger) error {
29-
bucket := os.Getenv(envBucket)
22+
bucket := os.Getenv(env.EnvBucket)
3023
if bucket == "" {
3124
return nil
3225
}
3326

34-
prefix := os.Getenv(envPrefix)
27+
// May be empty string
28+
regionHint := os.Getenv(env.EnvRegion)
29+
30+
prefix := os.Getenv(env.EnvPrefix)
3531
if prefix == "" {
36-
prefix = os.Getenv(envPipeline)
32+
prefix = os.Getenv(env.EnvPipeline)
3733
}
3834
if prefix == "" {
39-
return fmt.Errorf("%s or %s required", envPrefix, envPipeline)
35+
return fmt.Errorf("One of the %s or %s environment variables is required, set one to configure the bucket key prefix that is scanned for secrets.", env.EnvPrefix, env.EnvPipeline)
4036
}
4137

42-
client, err := s3.New(log, bucket)
38+
client, err := s3.New(log, bucket, regionHint)
4339
if err != nil {
4440
return err
4541
}
4642

4743
agent := &sshagent.Agent{}
4844

49-
credHelper := os.Getenv(envCredHelper)
45+
credHelper := os.Getenv(env.EnvCredHelper)
5046
if credHelper == "" {
51-
return fmt.Errorf("%s required", envCredHelper)
47+
return fmt.Errorf("The %s environment variable is required, set it to the path of the git-credential-s3-secrets script.", env.EnvCredHelper)
5248
}
5349

5450
return secrets.Run(secrets.Config{
55-
Repo: os.Getenv(envRepo),
51+
Repo: os.Getenv(env.EnvRepo),
5652
Bucket: bucket,
5753
Prefix: prefix,
5854
Client: client,

s3secrets-helper/s3/s3.go

Lines changed: 43 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -8,21 +8,24 @@ import (
88
"io/ioutil"
99
"os"
1010

11+
"github.com/aws/aws-sdk-go-v2/aws"
1112
"github.com/aws/aws-sdk-go-v2/config"
1213
"github.com/aws/aws-sdk-go-v2/feature/ec2/imds"
1314
"github.com/aws/aws-sdk-go-v2/feature/s3/manager"
1415
"github.com/aws/aws-sdk-go-v2/service/s3"
1516
"github.com/aws/aws-sdk-go-v2/service/s3/types"
1617
"github.com/aws/smithy-go"
18+
"github.com/buildkite/elastic-ci-stack-s3-secrets-hooks/s3secrets-helper/v2/env"
1719
"github.com/buildkite/elastic-ci-stack-s3-secrets-hooks/s3secrets-helper/v2/sentinel"
1820
)
1921

2022
type Client struct {
2123
s3 *s3.Client
2224
bucket string
25+
region string
2326
}
2427

25-
func getRegion(ctx context.Context) (string, error) {
28+
func getCurrentRegion(ctx context.Context) (string, error) {
2629
if region := os.Getenv("AWS_DEFAULT_REGION"); len(region) > 0 {
2730
return region, nil
2831
}
@@ -37,45 +40,62 @@ func getRegion(ctx context.Context) (string, error) {
3740
return "", errors.New("Unknown current region")
3841
}
3942

40-
func New(log *log.Logger, bucket string) (*Client, error) {
43+
func New(log *log.Logger, bucket string, regionHint string) (*Client, error) {
4144
ctx := context.Background()
4245

43-
// Using the current region (or a guess) find where the bucket lives
46+
var awsConfig aws.Config
47+
var err error
4448

45-
region, err := getRegion(ctx)
46-
if err != nil {
47-
// Ignore error and fallback to us-east-1 for bucket lookup
48-
region = "us-east-1"
49-
}
49+
if regionHint != "" {
50+
// If there is a region hint provided, we use it unconditionally
51+
awsConfig, err = config.LoadDefaultConfig(ctx,
52+
config.WithRegion(regionHint),
53+
)
54+
if err != nil {
55+
return nil, fmt.Errorf("Could not load the AWS SDK config (%v)", err)
56+
}
57+
} else {
58+
// Otherwise, use the current region (or a guess) to dynamically find
59+
// where the bucket lives.
60+
region, err := getCurrentRegion(ctx)
61+
if err != nil {
62+
// Ignore error and fallback to us-east-1 for bucket lookup
63+
region = "us-east-1"
64+
}
5065

51-
config, err := config.LoadDefaultConfig(ctx,
52-
config.WithRegion(region),
53-
)
54-
if err != nil {
55-
return nil, fmt.Errorf("Could not load the AWS SDK config (%v)", err)
56-
}
66+
awsConfig, err = config.LoadDefaultConfig(ctx,
67+
config.WithRegion(region),
68+
)
69+
if err != nil {
70+
return nil, fmt.Errorf("Could not load the AWS SDK config (%v)", err)
71+
}
5772

58-
log.Printf("Discovered current region as %q\n", config.Region)
73+
log.Printf("Discovered current region as %q\n", awsConfig.Region)
5974

60-
bucketRegion, err := manager.GetBucketRegion(ctx, s3.NewFromConfig(config), bucket)
61-
if err != nil {
62-
return nil, fmt.Errorf("Could not discover the region for bucket %q: (%v)", bucket, err)
75+
bucketRegion, err := manager.GetBucketRegion(ctx, s3.NewFromConfig(awsConfig), bucket)
76+
if err == nil && bucketRegion != "" {
77+
log.Printf("Discovered bucket region as %q\n", bucketRegion)
78+
awsConfig.Region = bucketRegion
79+
} else {
80+
log.Printf("Could not discover region for bucket %q. Using the %q region as a fallback, if this is not correct configure a bucket region using the %q environment variable. (%v)\n", bucket, awsConfig.Region, env.EnvRegion, err)
81+
}
6382
}
6483

65-
log.Printf("Discovered bucket region as %q\n", bucketRegion)
66-
67-
config.Region = bucketRegion
68-
6984
return &Client{
70-
s3: s3.NewFromConfig(config),
85+
s3: s3.NewFromConfig(awsConfig),
7186
bucket: bucket,
87+
region: awsConfig.Region,
7288
}, nil
7389
}
7490

7591
func (c *Client) Bucket() (string) {
7692
return c.bucket
7793
}
7894

95+
func (c *Client) Region() (string) {
96+
return c.region
97+
}
98+
7999
// Get downloads an object from S3.
80100
// Intended for small files; object is fully read into memory.
81101
// sentinel.ErrNotFound and sentinel.ErrForbidden are returned for those cases.

s3secrets-helper/secrets/secrets.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import (
1313
// Client represents interaction with AWS S3
1414
type Client interface {
1515
Bucket() (string)
16+
Region() (string)
1617
Get(key string) ([]byte, error)
1718
BucketExists() (bool, error)
1819
}
@@ -209,7 +210,7 @@ func handleGitCredentials(conf Config, results <-chan getResult) error {
209210
// Replace spaces ' ' in the helper path with an escaped space '\ '
210211
escapedCredentialHelper := strings.ReplaceAll(conf.GitCredentialHelper, " ", "\\ ")
211212

212-
helper := fmt.Sprintf("credential.helper=%s %s %s", escapedCredentialHelper, r.bucket, r.key)
213+
helper := fmt.Sprintf("credential.helper=%s %s %s %s", escapedCredentialHelper, r.bucket, conf.Client.Region(), r.key)
213214

214215
helpers = append(helpers, helper)
215216
}

0 commit comments

Comments
 (0)