Skip to content

Commit 58f0f9f

Browse files
Merge pull request #71 from rahulkaukuntla/rahul/use-v2-sdk-with-api-auth-aws
migrating `api/auth/aws` from `aws-sdk-go` to `aws-sdk-go-v2`
1 parent 3d50fe1 commit 58f0f9f

4 files changed

Lines changed: 148 additions & 182 deletions

File tree

api/auth/aws/aws.go

Lines changed: 64 additions & 80 deletions
Original file line numberDiff line numberDiff line change
@@ -5,16 +5,20 @@ package aws
55

66
import (
77
"context"
8+
"crypto/sha256"
89
"encoding/base64"
10+
"encoding/hex"
11+
"encoding/json"
912
"fmt"
10-
"os"
13+
"io"
14+
"net/http"
1115
"strings"
16+
"time"
1217

13-
"github.com/aws/aws-sdk-go/aws/credentials"
14-
"github.com/aws/aws-sdk-go/aws/ec2metadata"
15-
"github.com/aws/aws-sdk-go/aws/session"
16-
"github.com/hashicorp/go-hclog"
17-
"github.com/hashicorp/go-secure-stdlib/awsutil"
18+
"github.com/aws/aws-sdk-go-v2/aws"
19+
v4 "github.com/aws/aws-sdk-go-v2/aws/signer/v4"
20+
"github.com/aws/aws-sdk-go-v2/config"
21+
"github.com/aws/aws-sdk-go-v2/feature/ec2/imds"
1822
"github.com/hashicorp/go-uuid"
1923
"github.com/hashicorp/vault/api"
2024
)
@@ -32,7 +36,7 @@ type AWSAuth struct {
3236
signatureType string
3337
region string
3438
iamServerIDHeaderValue string
35-
creds *credentials.Credentials
39+
creds aws.CredentialsProvider
3640
nonce string
3741
}
3842

@@ -95,102 +99,82 @@ func (a *AWSAuth) Login(ctx context.Context, client *api.Client) (*api.Secret, e
9599
loginData := make(map[string]interface{})
96100
switch a.authType {
97101
case ec2Type:
98-
sess, err := session.NewSession()
102+
cfg, err := config.LoadDefaultConfig(ctx, config.WithRegion(a.region))
99103
if err != nil {
100-
return nil, fmt.Errorf("error creating session to probe EC2 metadata: %w", err)
104+
return nil, fmt.Errorf("error loading AWS config: %w", err)
101105
}
102-
metadataSvc := ec2metadata.New(sess)
103-
if !metadataSvc.Available() {
104-
return nil, fmt.Errorf("metadata service not available")
106+
metadataSvc := imds.NewFromConfig(cfg)
107+
108+
var path string
109+
switch a.signatureType {
110+
case pkcs7Type:
111+
path = "/instance-identity/pkcs7"
112+
case identityType:
113+
path = "/instance-identity/document"
114+
case rsa2048Type:
115+
path = "/instance-identity/rsa2048"
116+
default:
117+
return nil, fmt.Errorf("unknown signature type: %s", a.signatureType)
105118
}
106119

107-
if a.signatureType == pkcs7Type {
108-
// fetch PKCS #7 signature
109-
resp, err := metadataSvc.GetDynamicData("/instance-identity/pkcs7")
110-
if err != nil {
111-
return nil, fmt.Errorf("unable to get PKCS 7 data from metadata service: %w", err)
112-
}
113-
pkcs7 := strings.TrimSpace(resp)
114-
loginData["pkcs7"] = pkcs7
115-
} else if a.signatureType == identityType {
116-
// fetch signature from identity document
117-
doc, err := metadataSvc.GetDynamicData("/instance-identity/document")
118-
if err != nil {
119-
return nil, fmt.Errorf("error requesting instance identity doc: %w", err)
120-
}
121-
loginData["identity"] = base64.StdEncoding.EncodeToString([]byte(doc))
122-
123-
signature, err := metadataSvc.GetDynamicData("/instance-identity/signature")
124-
if err != nil {
125-
return nil, fmt.Errorf("error requesting signature: %w", err)
126-
}
127-
loginData["signature"] = signature
128-
} else if a.signatureType == rsa2048Type {
129-
// fetch RSA 2048 signature, which is also a PKCS#7 signature
130-
resp, err := metadataSvc.GetDynamicData("/instance-identity/rsa2048")
131-
if err != nil {
132-
return nil, fmt.Errorf("unable to get PKCS 7 data from metadata service: %w", err)
133-
}
134-
pkcs7 := strings.TrimSpace(resp)
135-
loginData["pkcs7"] = pkcs7
136-
} else {
137-
return nil, fmt.Errorf("unknown signature type: %s", a.signatureType)
120+
resp, err := metadataSvc.GetDynamicData(ctx, &imds.GetDynamicDataInput{Path: path})
121+
if err != nil {
122+
return nil, fmt.Errorf("unable to get identity data: %w", err)
123+
}
124+
defer resp.Content.Close()
125+
body, err := io.ReadAll(resp.Content)
126+
if err != nil {
127+
return nil, fmt.Errorf("error reading identity data: %w", err)
138128
}
129+
pkcs7 := strings.TrimSpace(string(body))
130+
loginData["pkcs7"] = pkcs7
139131

140-
// Add the reauthentication value, if we have one
141132
if a.nonce == "" {
142-
uid, err := uuid.GenerateUUID()
133+
uuid, err := uuid.GenerateUUID()
143134
if err != nil {
144-
return nil, fmt.Errorf("error generating uuid for reauthentication value: %w", err)
135+
return nil, fmt.Errorf("error generating uuid: %w", err)
145136
}
146-
a.nonce = uid
137+
a.nonce = uuid
147138
}
148139
loginData["nonce"] = a.nonce
149140
case iamType:
150-
logger := hclog.Default()
151141
if a.creds == nil {
152-
credsConfig := awsutil.CredentialsConfig{
153-
AccessKey: os.Getenv("AWS_ACCESS_KEY_ID"),
154-
SecretKey: os.Getenv("AWS_SECRET_ACCESS_KEY"),
155-
SessionToken: os.Getenv("AWS_SESSION_TOKEN"),
156-
Logger: logger,
157-
}
158-
159-
// the env vars above will take precedence if they are set, as
160-
// they will be added to the ChainProvider stack first
161-
var hasCredsFile bool
162-
credsFilePath := os.Getenv("AWS_SHARED_CREDENTIALS_FILE")
163-
if credsFilePath != "" {
164-
hasCredsFile = true
165-
credsConfig.Filename = credsFilePath
166-
}
167-
168-
creds, err := credsConfig.GenerateCredentialChain(awsutil.WithSharedCredentials(hasCredsFile))
142+
cfg, err := config.LoadDefaultConfig(ctx, config.WithRegion(a.region))
169143
if err != nil {
170-
return nil, err
171-
}
172-
if creds == nil {
173-
return nil, fmt.Errorf("could not compile valid credential providers from static config, environment, shared, or instance metadata")
144+
return nil, fmt.Errorf("unable to load AWS config: %w", err)
174145
}
146+
a.creds = cfg.Credentials
147+
}
175148

176-
_, err = creds.Get()
177-
if err != nil {
178-
return nil, fmt.Errorf("failed to retrieve credentials from credential chain: %w", err)
179-
}
149+
credsVal, err := a.creds.Retrieve(ctx)
150+
if err != nil {
151+
return nil, fmt.Errorf("failed to retrieve credentials: %w", err)
152+
}
180153

181-
a.creds = creds
154+
const iamBody = "Action=GetCallerIdentity&Version=2011-06-15"
155+
req, err := http.NewRequest("POST", "https://sts.amazonaws.com/", strings.NewReader(iamBody))
156+
if err != nil {
157+
return nil, fmt.Errorf("failed to construct STS request: %w", err)
182158
}
159+
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
183160

184-
data, err := awsutil.GenerateLoginData(a.creds, a.iamServerIDHeaderValue, a.region, logger)
161+
hash := sha256.Sum256([]byte(iamBody))
162+
payloadHash := hex.EncodeToString(hash[:])
163+
164+
signer := v4.NewSigner()
165+
err = signer.SignHTTP(ctx, credsVal, req, payloadHash, "sts", a.region, time.Now().UTC())
185166
if err != nil {
186-
return nil, fmt.Errorf("unable to generate login data for AWS auth endpoint: %w", err)
167+
return nil, fmt.Errorf("failed to sign STS request: %w", err)
187168
}
188-
loginData = data
169+
170+
headersData, _ := json.Marshal(req.Header)
171+
172+
loginData["iam_http_request_method"] = "POST"
173+
loginData["iam_request_url"] = base64.StdEncoding.EncodeToString([]byte(req.URL.String()))
174+
loginData["iam_request_body"] = base64.StdEncoding.EncodeToString([]byte(iamBody))
175+
loginData["iam_request_headers"] = base64.StdEncoding.EncodeToString(headersData)
189176
}
190177

191-
// Add role if we have one. If not, Vault will infer the role name based
192-
// on the IAM friendly name (iam auth type) or EC2 instance's
193-
// AMI ID (ec2 auth type).
194178
if a.roleName != "" {
195179
loginData["role"] = a.roleName
196180
}

api/auth/aws/go.mod

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,24 @@ go 1.23.0
55
toolchain go1.23.7
66

77
require (
8-
github.com/aws/aws-sdk-go v1.55.7
9-
github.com/hashicorp/go-hclog v1.6.3
10-
github.com/hashicorp/go-secure-stdlib/awsutil v0.3.0
8+
github.com/aws/aws-sdk-go-v2 v1.26.1
9+
github.com/aws/aws-sdk-go-v2/config v1.26.1
10+
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.14.10
1111
github.com/hashicorp/go-uuid v1.0.2
1212
github.com/hashicorp/vault/api v1.20.0
1313
)
1414

1515
require (
16+
github.com/aws/aws-sdk-go-v2/credentials v1.16.12 // indirect
17+
github.com/aws/aws-sdk-go-v2/internal/configsources v1.2.9 // indirect
18+
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.5.9 // indirect
19+
github.com/aws/aws-sdk-go-v2/internal/ini v1.7.2 // indirect
20+
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.10.4 // indirect
21+
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.10.9 // indirect
22+
github.com/aws/aws-sdk-go-v2/service/sso v1.18.5 // indirect
23+
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.21.5 // indirect
24+
github.com/aws/aws-sdk-go-v2/service/sts v1.26.5 // indirect
25+
github.com/aws/smithy-go v1.20.2 // indirect
1626
github.com/cenkalti/backoff/v4 v4.3.0 // indirect
1727
github.com/fatih/color v1.18.0 // indirect
1828
github.com/go-jose/go-jose/v4 v4.1.1 // indirect
@@ -25,16 +35,12 @@ require (
2535
github.com/hashicorp/go-secure-stdlib/strutil v0.1.2 // indirect
2636
github.com/hashicorp/go-sockaddr v1.0.7 // indirect
2737
github.com/hashicorp/hcl v1.0.1-vault-7 // indirect
28-
github.com/jmespath/go-jmespath v0.4.0 // indirect
2938
github.com/mattn/go-colorable v0.1.14 // indirect
30-
github.com/mattn/go-isatty v0.0.20 // indirect
3139
github.com/mitchellh/go-homedir v1.1.0 // indirect
3240
github.com/mitchellh/mapstructure v1.5.0 // indirect
33-
github.com/pkg/errors v0.9.1 // indirect
3441
github.com/ryanuber/go-glob v1.0.0 // indirect
3542
golang.org/x/crypto v0.40.0 // indirect
3643
golang.org/x/net v0.42.0 // indirect
37-
golang.org/x/sys v0.34.0 // indirect
3844
golang.org/x/text v0.27.0 // indirect
3945
golang.org/x/time v0.12.0 // indirect
4046
)

0 commit comments

Comments
 (0)