|
Packit |
63bb0d |
package stscreds
|
|
Packit |
63bb0d |
|
|
Packit |
63bb0d |
import (
|
|
Packit |
63bb0d |
"fmt"
|
|
Packit |
63bb0d |
"io/ioutil"
|
|
Packit |
63bb0d |
"strconv"
|
|
Packit |
63bb0d |
"time"
|
|
Packit |
63bb0d |
|
|
Packit |
63bb0d |
"github.com/aws/aws-sdk-go/aws"
|
|
Packit |
63bb0d |
"github.com/aws/aws-sdk-go/aws/awserr"
|
|
Packit |
63bb0d |
"github.com/aws/aws-sdk-go/aws/client"
|
|
Packit |
63bb0d |
"github.com/aws/aws-sdk-go/aws/credentials"
|
|
Packit |
63bb0d |
"github.com/aws/aws-sdk-go/service/sts"
|
|
Packit |
63bb0d |
"github.com/aws/aws-sdk-go/service/sts/stsiface"
|
|
Packit |
63bb0d |
)
|
|
Packit |
63bb0d |
|
|
Packit |
63bb0d |
const (
|
|
Packit |
63bb0d |
// ErrCodeWebIdentity will be used as an error code when constructing
|
|
Packit |
63bb0d |
// a new error to be returned during session creation or retrieval.
|
|
Packit |
63bb0d |
ErrCodeWebIdentity = "WebIdentityErr"
|
|
Packit |
63bb0d |
|
|
Packit |
63bb0d |
// WebIdentityProviderName is the web identity provider name
|
|
Packit |
63bb0d |
WebIdentityProviderName = "WebIdentityCredentials"
|
|
Packit |
63bb0d |
)
|
|
Packit |
63bb0d |
|
|
Packit |
63bb0d |
// now is used to return a time.Time object representing
|
|
Packit |
63bb0d |
// the current time. This can be used to easily test and
|
|
Packit |
63bb0d |
// compare test values.
|
|
Packit |
63bb0d |
var now = time.Now
|
|
Packit |
63bb0d |
|
|
Packit |
63bb0d |
// WebIdentityRoleProvider is used to retrieve credentials using
|
|
Packit |
63bb0d |
// an OIDC token.
|
|
Packit |
63bb0d |
type WebIdentityRoleProvider struct {
|
|
Packit |
63bb0d |
credentials.Expiry
|
|
Packit |
63bb0d |
|
|
Packit |
63bb0d |
client stsiface.STSAPI
|
|
Packit |
63bb0d |
ExpiryWindow time.Duration
|
|
Packit |
63bb0d |
|
|
Packit |
63bb0d |
tokenFilePath string
|
|
Packit |
63bb0d |
roleARN string
|
|
Packit |
63bb0d |
roleSessionName string
|
|
Packit |
63bb0d |
}
|
|
Packit |
63bb0d |
|
|
Packit |
63bb0d |
// NewWebIdentityCredentials will return a new set of credentials with a given
|
|
Packit |
63bb0d |
// configuration, role arn, and token file path.
|
|
Packit |
63bb0d |
func NewWebIdentityCredentials(c client.ConfigProvider, roleARN, roleSessionName, path string) *credentials.Credentials {
|
|
Packit |
63bb0d |
svc := sts.New(c)
|
|
Packit |
63bb0d |
p := NewWebIdentityRoleProvider(svc, roleARN, roleSessionName, path)
|
|
Packit |
63bb0d |
return credentials.NewCredentials(p)
|
|
Packit |
63bb0d |
}
|
|
Packit |
63bb0d |
|
|
Packit |
63bb0d |
// NewWebIdentityRoleProvider will return a new WebIdentityRoleProvider with the
|
|
Packit |
63bb0d |
// provided stsiface.STSAPI
|
|
Packit |
63bb0d |
func NewWebIdentityRoleProvider(svc stsiface.STSAPI, roleARN, roleSessionName, path string) *WebIdentityRoleProvider {
|
|
Packit |
63bb0d |
return &WebIdentityRoleProvider{
|
|
Packit |
63bb0d |
client: svc,
|
|
Packit |
63bb0d |
tokenFilePath: path,
|
|
Packit |
63bb0d |
roleARN: roleARN,
|
|
Packit |
63bb0d |
roleSessionName: roleSessionName,
|
|
Packit |
63bb0d |
}
|
|
Packit |
63bb0d |
}
|
|
Packit |
63bb0d |
|
|
Packit |
63bb0d |
// Retrieve will attempt to assume a role from a token which is located at
|
|
Packit |
63bb0d |
// 'WebIdentityTokenFilePath' specified destination and if that is empty an
|
|
Packit |
63bb0d |
// error will be returned.
|
|
Packit |
63bb0d |
func (p *WebIdentityRoleProvider) Retrieve() (credentials.Value, error) {
|
|
Packit |
63bb0d |
b, err := ioutil.ReadFile(p.tokenFilePath)
|
|
Packit |
63bb0d |
if err != nil {
|
|
Packit |
63bb0d |
errMsg := fmt.Sprintf("unable to read file at %s", p.tokenFilePath)
|
|
Packit |
63bb0d |
return credentials.Value{}, awserr.New(ErrCodeWebIdentity, errMsg, err)
|
|
Packit |
63bb0d |
}
|
|
Packit |
63bb0d |
|
|
Packit |
63bb0d |
sessionName := p.roleSessionName
|
|
Packit |
63bb0d |
if len(sessionName) == 0 {
|
|
Packit |
63bb0d |
// session name is used to uniquely identify a session. This simply
|
|
Packit |
63bb0d |
// uses unix time in nanoseconds to uniquely identify sessions.
|
|
Packit |
63bb0d |
sessionName = strconv.FormatInt(now().UnixNano(), 10)
|
|
Packit |
63bb0d |
}
|
|
Packit |
63bb0d |
req, resp := p.client.AssumeRoleWithWebIdentityRequest(&sts.AssumeRoleWithWebIdentityInput{
|
|
Packit |
63bb0d |
RoleArn: &p.roleARN,
|
|
Packit |
63bb0d |
RoleSessionName: &sessionName,
|
|
Packit |
63bb0d |
WebIdentityToken: aws.String(string(b)),
|
|
Packit |
63bb0d |
})
|
|
Packit |
63bb0d |
// InvalidIdentityToken error is a temporary error that can occur
|
|
Packit |
63bb0d |
// when assuming an Role with a JWT web identity token.
|
|
Packit |
63bb0d |
req.RetryErrorCodes = append(req.RetryErrorCodes, sts.ErrCodeInvalidIdentityTokenException)
|
|
Packit |
63bb0d |
if err := req.Send(); err != nil {
|
|
Packit |
63bb0d |
return credentials.Value{}, awserr.New(ErrCodeWebIdentity, "failed to retrieve credentials", err)
|
|
Packit |
63bb0d |
}
|
|
Packit |
63bb0d |
|
|
Packit |
63bb0d |
p.SetExpiration(aws.TimeValue(resp.Credentials.Expiration), p.ExpiryWindow)
|
|
Packit |
63bb0d |
|
|
Packit |
63bb0d |
value := credentials.Value{
|
|
Packit |
63bb0d |
AccessKeyID: aws.StringValue(resp.Credentials.AccessKeyId),
|
|
Packit |
63bb0d |
SecretAccessKey: aws.StringValue(resp.Credentials.SecretAccessKey),
|
|
Packit |
63bb0d |
SessionToken: aws.StringValue(resp.Credentials.SessionToken),
|
|
Packit |
63bb0d |
ProviderName: WebIdentityProviderName,
|
|
Packit |
63bb0d |
}
|
|
Packit |
63bb0d |
return value, nil
|
|
Packit |
63bb0d |
}
|