1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
|
package stscreds
import (
"context"
"fmt"
"io/ioutil"
"strconv"
"time"
"github.com/aws/aws-sdk-go-v2/aws"
"github.com/aws/aws-sdk-go-v2/aws/retry"
"github.com/aws/aws-sdk-go-v2/internal/sdk"
"github.com/aws/aws-sdk-go-v2/service/sts"
"github.com/aws/aws-sdk-go-v2/service/sts/types"
)
var invalidIdentityTokenExceptionCode = (&types.InvalidIdentityTokenException{}).ErrorCode()
const (
// WebIdentityProviderName is the web identity provider name
WebIdentityProviderName = "WebIdentityCredentials"
)
// AssumeRoleWithWebIdentityAPIClient is a client capable of the STS AssumeRoleWithWebIdentity operation.
type AssumeRoleWithWebIdentityAPIClient interface {
AssumeRoleWithWebIdentity(ctx context.Context, params *sts.AssumeRoleWithWebIdentityInput, optFns ...func(*sts.Options)) (*sts.AssumeRoleWithWebIdentityOutput, error)
}
// WebIdentityRoleProvider is used to retrieve credentials using
// an OIDC token.
type WebIdentityRoleProvider struct {
options WebIdentityRoleOptions
}
// WebIdentityRoleOptions is a structure of configurable options for WebIdentityRoleProvider
type WebIdentityRoleOptions struct {
// Client implementation of the AssumeRoleWithWebIdentity operation. Required
Client AssumeRoleWithWebIdentityAPIClient
// JWT Token Provider. Required
TokenRetriever IdentityTokenRetriever
// IAM Role ARN to assume. Required
RoleARN string
// Session name, if you wish to uniquely identify this session.
RoleSessionName string
// Expiry duration of the STS credentials. STS will assign a default expiry
// duration if this value is unset. This is different from the Duration
// option of AssumeRoleProvider, which automatically assigns 15 minutes if
// Duration is unset.
//
// See the STS AssumeRoleWithWebIdentity API reference guide for more
// information on defaults.
// https://docs.aws.amazon.com/STS/latest/APIReference/API_AssumeRoleWithWebIdentity.html
Duration time.Duration
// An IAM policy in JSON format that you want to use as an inline session policy.
Policy *string
// The Amazon Resource Names (ARNs) of the IAM managed policies that you
// want to use as managed session policies. The policies must exist in the
// same account as the role.
PolicyARNs []types.PolicyDescriptorType
}
// IdentityTokenRetriever is an interface for retrieving a JWT
type IdentityTokenRetriever interface {
GetIdentityToken() ([]byte, error)
}
// IdentityTokenFile is for retrieving an identity token from the given file name
type IdentityTokenFile string
// GetIdentityToken retrieves the JWT token from the file and returns the contents as a []byte
func (j IdentityTokenFile) GetIdentityToken() ([]byte, error) {
b, err := ioutil.ReadFile(string(j))
if err != nil {
return nil, fmt.Errorf("unable to read file at %s: %v", string(j), err)
}
return b, nil
}
// NewWebIdentityRoleProvider will return a new WebIdentityRoleProvider with the
// provided stsiface.ClientAPI
func NewWebIdentityRoleProvider(client AssumeRoleWithWebIdentityAPIClient, roleARN string, tokenRetriever IdentityTokenRetriever, optFns ...func(*WebIdentityRoleOptions)) *WebIdentityRoleProvider {
o := WebIdentityRoleOptions{
Client: client,
RoleARN: roleARN,
TokenRetriever: tokenRetriever,
}
for _, fn := range optFns {
fn(&o)
}
return &WebIdentityRoleProvider{options: o}
}
// Retrieve will attempt to assume a role from a token which is located at
// 'WebIdentityTokenFilePath' specified destination and if that is empty an
// error will be returned.
func (p *WebIdentityRoleProvider) Retrieve(ctx context.Context) (aws.Credentials, error) {
b, err := p.options.TokenRetriever.GetIdentityToken()
if err != nil {
return aws.Credentials{}, fmt.Errorf("failed to retrieve jwt from provide source, %w", err)
}
sessionName := p.options.RoleSessionName
if len(sessionName) == 0 {
// session name is used to uniquely identify a session. This simply
// uses unix time in nanoseconds to uniquely identify sessions.
sessionName = strconv.FormatInt(sdk.NowTime().UnixNano(), 10)
}
input := &sts.AssumeRoleWithWebIdentityInput{
PolicyArns: p.options.PolicyARNs,
RoleArn: &p.options.RoleARN,
RoleSessionName: &sessionName,
WebIdentityToken: aws.String(string(b)),
}
if p.options.Duration != 0 {
// If set use the value, otherwise STS will assign a default expiration duration.
input.DurationSeconds = aws.Int32(int32(p.options.Duration / time.Second))
}
if p.options.Policy != nil {
input.Policy = p.options.Policy
}
resp, err := p.options.Client.AssumeRoleWithWebIdentity(ctx, input, func(options *sts.Options) {
options.Retryer = retry.AddWithErrorCodes(options.Retryer, invalidIdentityTokenExceptionCode)
})
if err != nil {
return aws.Credentials{}, fmt.Errorf("failed to retrieve credentials, %w", err)
}
// InvalidIdentityToken error is a temporary error that can occur
// when assuming an Role with a JWT web identity token.
value := aws.Credentials{
AccessKeyID: aws.ToString(resp.Credentials.AccessKeyId),
SecretAccessKey: aws.ToString(resp.Credentials.SecretAccessKey),
SessionToken: aws.ToString(resp.Credentials.SessionToken),
Source: WebIdentityProviderName,
CanExpire: true,
Expires: *resp.Credentials.Expiration,
}
return value, nil
}
|