vendor: update buildkit to master@31c870e82a48

Signed-off-by: Justin Chadwell <me@jedevc.com>
This commit is contained in:
Justin Chadwell
2023-05-15 18:32:31 +01:00
parent 167cd16acb
commit e61a8cf637
269 changed files with 25798 additions and 3371 deletions

View File

@ -1,3 +1,169 @@
# v1.13.16 (2023-03-10)
* **Dependency Update**: Updated to the latest SDK module versions
# v1.13.15 (2023-02-22)
* **Dependency Update**: Updated to the latest SDK module versions
# v1.13.14 (2023-02-20)
* **Dependency Update**: Updated to the latest SDK module versions
# v1.13.13 (2023-02-15)
* **Dependency Update**: Updated to the latest SDK module versions
# v1.13.12 (2023-02-03)
* **Dependency Update**: Updated to the latest SDK module versions
# v1.13.11 (2023-02-01)
* No change notes available for this release.
# v1.13.10 (2023-01-25)
* **Dependency Update**: Updated to the latest SDK module versions
# v1.13.9 (2023-01-23)
* **Dependency Update**: Updated to the latest SDK module versions
# v1.13.8 (2023-01-05)
* **Dependency Update**: Updated to the latest SDK module versions
# v1.13.7 (2022-12-20)
* **Dependency Update**: Updated to the latest SDK module versions
# v1.13.6 (2022-12-19)
* **Dependency Update**: Updated to the latest SDK module versions
# v1.13.5 (2022-12-15)
* **Bug Fix**: Unify logic between shared config and in finding home directory
* **Dependency Update**: Updated to the latest SDK module versions
# v1.13.4 (2022-12-02)
* **Dependency Update**: Updated to the latest SDK module versions
# v1.13.3 (2022-11-22)
* **Dependency Update**: Updated to the latest SDK module versions
# v1.13.2 (2022-11-17)
* **Dependency Update**: Updated to the latest SDK module versions
# v1.13.1 (2022-11-16)
* **Dependency Update**: Updated to the latest SDK module versions
# v1.13.0 (2022-11-11)
* **Announcement**: When using the SSOTokenProvider, a previous implementation incorrectly compensated for invalid SSOTokenProvider configurations in the shared profile. This has been fixed via PR #1903 and tracked in issue #1846
* **Feature**: Adds token refresh support (via SSOTokenProvider) when using the SSOCredentialProvider
# v1.12.24 (2022-11-10)
* **Dependency Update**: Updated to the latest SDK module versions
# v1.12.23 (2022-10-24)
* **Dependency Update**: Updated to the latest SDK module versions
# v1.12.22 (2022-10-21)
* **Dependency Update**: Updated to the latest SDK module versions
# v1.12.21 (2022-09-30)
* **Dependency Update**: Updated to the latest SDK module versions
# v1.12.20 (2022-09-20)
* **Dependency Update**: Updated to the latest SDK module versions
# v1.12.19 (2022-09-14)
* **Dependency Update**: Updated to the latest SDK module versions
# v1.12.18 (2022-09-02)
* **Dependency Update**: Updated to the latest SDK module versions
# v1.12.17 (2022-08-31)
* **Dependency Update**: Updated to the latest SDK module versions
# v1.12.16 (2022-08-30)
* **Dependency Update**: Updated to the latest SDK module versions
# v1.12.15 (2022-08-29)
* **Dependency Update**: Updated to the latest SDK module versions
# v1.12.14 (2022-08-15)
* **Dependency Update**: Updated to the latest SDK module versions
# v1.12.13 (2022-08-11)
* **Dependency Update**: Updated to the latest SDK module versions
# v1.12.12 (2022-08-09)
* **Dependency Update**: Updated to the latest SDK module versions
# v1.12.11 (2022-08-08)
* **Dependency Update**: Updated to the latest SDK module versions
# v1.12.10 (2022-08-01)
* **Dependency Update**: Updated to the latest SDK module versions
# v1.12.9 (2022-07-11)
* **Dependency Update**: Updated to the latest SDK module versions
# v1.12.8 (2022-07-05)
* **Dependency Update**: Updated to the latest SDK module versions
# v1.12.7 (2022-06-29)
* **Dependency Update**: Updated to the latest SDK module versions
# v1.12.6 (2022-06-16)
* **Dependency Update**: Updated to the latest SDK module versions
# v1.12.5 (2022-06-07)
* **Dependency Update**: Updated to the latest SDK module versions
# v1.12.4 (2022-05-26)
* **Dependency Update**: Updated to the latest SDK module versions
# v1.12.3 (2022-05-25)
* **Dependency Update**: Updated to the latest SDK module versions
# v1.12.2 (2022-05-17)
* **Dependency Update**: Updated to the latest SDK module versions
# v1.12.1 (2022-05-16)
* **Dependency Update**: Updated to the latest SDK module versions
# v1.12.0 (2022-04-25)
* **Feature**: Adds Duration and Policy options that can be used when creating stscreds.WebIdentityRoleProvider credentials provider.

View File

@ -1,27 +1,27 @@
// Package ec2rolecreds provides the credentials provider implementation for
// retrieving AWS credentials from Amazon EC2 Instance Roles via Amazon EC2 IMDS.
//
// Concurrency and caching
// # Concurrency and caching
//
// The Provider is not safe to be used concurrently, and does not provide any
// caching of credentials retrieved. You should wrap the Provider with a
// `aws.CredentialsCache` to provide concurrency safety, and caching of
// credentials.
//
// Loading credentials with the SDK's AWS Config
// # Loading credentials with the SDK's AWS Config
//
// The EC2 Instance role credentials provider will automatically be the resolved
// credential provider int he credential chain if no other credential provider is
// credential provider in the credential chain if no other credential provider is
// resolved first.
//
// To explicitly instruct the SDK's credentials resolving to use the EC2 Instance
// role for credentials, you specify a `credentials_source` property in the config
// profile the SDK will load.
//
// [default]
// credential_source = Ec2InstanceMetadata
// [default]
// credential_source = Ec2InstanceMetadata
//
// Loading credentials with the Provider directly
// # Loading credentials with the Provider directly
//
// Another way to use the EC2 Instance role credentials provider is to create it
// directly and assign it as the credentials provider for an API client.
@ -30,28 +30,28 @@
// it with the CredentialsCache before assigning the provider to the Amazon S3 API
// client's Credentials option.
//
// provider := imds.New(imds.Options{})
// provider := imds.New(imds.Options{})
//
// // Create the service client value configured for credentials.
// svc := s3.New(s3.Options{
// Credentials: aws.NewCredentialsCache(provider),
// })
// // Create the service client value configured for credentials.
// svc := s3.New(s3.Options{
// Credentials: aws.NewCredentialsCache(provider),
// })
//
// If you need more control, you can set the configuration options on the
// credentials provider using the imds.Options type to configure the EC2 IMDS
// API Client and ExpiryWindow of the retrieved credentials.
//
// provider := imds.New(imds.Options{
// // See imds.Options type's documentation for more options available.
// Client: imds.New(Options{
// HTTPClient: customHTTPClient,
// }),
// provider := imds.New(imds.Options{
// // See imds.Options type's documentation for more options available.
// Client: imds.New(Options{
// HTTPClient: customHTTPClient,
// }),
//
// // Modify how soon credentials expire prior to their original expiry time.
// ExpiryWindow: 5 * time.Minute,
// })
// // Modify how soon credentials expire prior to their original expiry time.
// ExpiryWindow: 5 * time.Minute,
// })
//
// EC2 IMDS API Client
// # EC2 IMDS API Client
//
// See the github.com/aws/aws-sdk-go-v2/feature/ec2/imds module for more details on
// configuring the client, and options available.

View File

@ -33,9 +33,9 @@ type GetMetadataAPIClient interface {
//
// The New function must be used to create the with a custom EC2 IMDS client.
//
// p := &ec2rolecreds.New(func(o *ec2rolecreds.Options{
// o.Client = imds.New(imds.Options{/* custom options */})
// })
// p := &ec2rolecreds.New(func(o *ec2rolecreds.Options{
// o.Client = imds.New(imds.Options{/* custom options */})
// })
type Provider struct {
options Options
}

View File

@ -7,26 +7,29 @@
//
// Static credentials will never expire once they have been retrieved. The format
// of the static credentials response:
// {
// "AccessKeyId" : "MUA...",
// "SecretAccessKey" : "/7PC5om....",
// }
//
// {
// "AccessKeyId" : "MUA...",
// "SecretAccessKey" : "/7PC5om....",
// }
//
// Refreshable credentials will expire within the "ExpiryWindow" of the Expiration
// value in the response. The format of the refreshable credentials response:
// {
// "AccessKeyId" : "MUA...",
// "SecretAccessKey" : "/7PC5om....",
// "Token" : "AQoDY....=",
// "Expiration" : "2016-02-25T06:03:31Z"
// }
//
// {
// "AccessKeyId" : "MUA...",
// "SecretAccessKey" : "/7PC5om....",
// "Token" : "AQoDY....=",
// "Expiration" : "2016-02-25T06:03:31Z"
// }
//
// Errors should be returned in the following format and only returned with 400
// or 500 HTTP status codes.
// {
// "code": "ErrorCode",
// "message": "Helpful error message."
// }
//
// {
// "code": "ErrorCode",
// "message": "Helpful error message."
// }
package endpointcreds
import (

View File

@ -3,4 +3,4 @@
package credentials
// goModuleVersion is the tagged release for this module
const goModuleVersion = "1.12.0"
const goModuleVersion = "1.13.16"

View File

@ -7,14 +7,14 @@
// option, you should make sure that the config file is as locked down as possible
// using security best practices for your operating system.
//
// Concurrency and caching
// # Concurrency and caching
//
// The Provider is not safe to be used concurrently, and does not provide any
// caching of credentials retrieved. You should wrap the Provider with a
// `aws.CredentialsCache` to provide concurrency safety, and caching of
// credentials.
//
// Loading credentials with the SDKs AWS Config
// # Loading credentials with the SDKs AWS Config
//
// You can use credentials from a AWS shared config `credential_process` in a
// variety of ways.
@ -24,20 +24,20 @@
// called. You also need to set the AWS_SDK_LOAD_CONFIG environment variable
// (e.g., `export AWS_SDK_LOAD_CONFIG=1`) to use the shared config file.
//
// [default]
// credential_process = /command/to/call
// [default]
// credential_process = /command/to/call
//
// Loading configuration using external will use the credential process to
// retrieve credentials. NOTE: If there are credentials in the profile you are
// using, the credential process will not be used.
//
// // Initialize a session to load credentials.
// cfg, _ := config.LoadDefaultConfig(context.TODO())
// // Initialize a session to load credentials.
// cfg, _ := config.LoadDefaultConfig(context.TODO())
//
// // Create S3 service client to use the credentials.
// svc := s3.NewFromConfig(cfg)
// // Create S3 service client to use the credentials.
// svc := s3.NewFromConfig(cfg)
//
// Loading credentials with the Provider directly
// # Loading credentials with the Provider directly
//
// Another way to use the credentials process provider is by using the
// `NewProvider` constructor to create the provider and providing a it with a
@ -47,46 +47,46 @@
// it with the CredentialsCache before assigning the provider to the Amazon S3 API
// client's Credentials option.
//
// // Create credentials using the Provider.
// provider := processcreds.NewProvider("/path/to/command")
// // Create credentials using the Provider.
// provider := processcreds.NewProvider("/path/to/command")
//
// // Create the service client value configured for credentials.
// svc := s3.New(s3.Options{
// Credentials: aws.NewCredentialsCache(provider),
// })
// // Create the service client value configured for credentials.
// svc := s3.New(s3.Options{
// Credentials: aws.NewCredentialsCache(provider),
// })
//
// If you need more control, you can set any configurable options in the
// credentials using one or more option functions.
//
// provider := processcreds.NewProvider("/path/to/command",
// func(o *processcreds.Options) {
// // Override the provider's default timeout
// o.Timeout = 2 * time.Minute
// })
// provider := processcreds.NewProvider("/path/to/command",
// func(o *processcreds.Options) {
// // Override the provider's default timeout
// o.Timeout = 2 * time.Minute
// })
//
// You can also use your own `exec.Cmd` value by satisfying a value that satisfies
// the `NewCommandBuilder` interface and use the `NewProviderCommand` constructor.
//
// // Create an exec.Cmd
// cmdBuilder := processcreds.NewCommandBuilderFunc(
// func(ctx context.Context) (*exec.Cmd, error) {
// cmd := exec.CommandContext(ctx,
// "customCLICommand",
// "-a", "argument",
// )
// cmd.Env = []string{
// "ENV_VAR_FOO=value",
// "ENV_VAR_BAR=other_value",
// }
// // Create an exec.Cmd
// cmdBuilder := processcreds.NewCommandBuilderFunc(
// func(ctx context.Context) (*exec.Cmd, error) {
// cmd := exec.CommandContext(ctx,
// "customCLICommand",
// "-a", "argument",
// )
// cmd.Env = []string{
// "ENV_VAR_FOO=value",
// "ENV_VAR_BAR=other_value",
// }
//
// return cmd, nil
// },
// )
// return cmd, nil
// },
// )
//
// // Create credentials using your exec.Cmd and custom timeout
// provider := processcreds.NewProviderCommand(cmdBuilder,
// func(opt *processcreds.Provider) {
// // optionally override the provider's default timeout
// opt.Timeout = 1 * time.Second
// })
// // Create credentials using your exec.Cmd and custom timeout
// provider := processcreds.NewProviderCommand(cmdBuilder,
// func(opt *processcreds.Provider) {
// // optionally override the provider's default timeout
// opt.Timeout = 1 * time.Second
// })
package processcreds

View File

@ -149,12 +149,24 @@ func NewProviderCommand(builder NewCommandBuilder, options ...func(*Options)) *P
return p
}
type credentialProcessResponse struct {
Version int
AccessKeyID string `json:"AccessKeyId"`
// A CredentialProcessResponse is the AWS credentials format that must be
// returned when executing an external credential_process.
type CredentialProcessResponse struct {
// As of this writing, the Version key must be set to 1. This might
// increment over time as the structure evolves.
Version int
// The access key ID that identifies the temporary security credentials.
AccessKeyID string `json:"AccessKeyId"`
// The secret access key that can be used to sign requests.
SecretAccessKey string
SessionToken string
Expiration *time.Time
// The token that users must pass to the service API to use the temporary credentials.
SessionToken string
// The date on which the current credentials expire.
Expiration *time.Time
}
// Retrieve executes the credential process command and returns the
@ -166,7 +178,7 @@ func (p *Provider) Retrieve(ctx context.Context) (aws.Credentials, error) {
}
// Serialize and validate response
resp := &credentialProcessResponse{}
resp := &CredentialProcessResponse{}
if err = json.Unmarshal(out, resp); err != nil {
return aws.Credentials{Source: ProviderName}, &ProviderError{
Err: fmt.Errorf("parse failed of process output: %s, error: %w", out, err),

View File

@ -1,63 +1,71 @@
// Package ssocreds provides a credential provider for retrieving temporary AWS credentials using an SSO access token.
// Package ssocreds provides a credential provider for retrieving temporary AWS
// credentials using an SSO access token.
//
// IMPORTANT: The provider in this package does not initiate or perform the AWS SSO login flow. The SDK provider
// expects that you have already performed the SSO login flow using AWS CLI using the "aws sso login" command, or by
// some other mechanism. The provider must find a valid non-expired access token for the AWS SSO user portal URL in
// ~/.aws/sso/cache. If a cached token is not found, it is expired, or the file is malformed an error will be returned.
// IMPORTANT: The provider in this package does not initiate or perform the AWS
// SSO login flow. The SDK provider expects that you have already performed the
// SSO login flow using AWS CLI using the "aws sso login" command, or by some
// other mechanism. The provider must find a valid non-expired access token for
// the AWS SSO user portal URL in ~/.aws/sso/cache. If a cached token is not
// found, it is expired, or the file is malformed an error will be returned.
//
// Loading AWS SSO credentials with the AWS shared configuration file
// # Loading AWS SSO credentials with the AWS shared configuration file
//
// You can use configure AWS SSO credentials from the AWS shared configuration file by
// providing the specifying the required keys in the profile:
//
// sso_account_id
// sso_region
// sso_role_name
// sso_start_url
// sso_account_id
// sso_region
// sso_role_name
// sso_start_url
//
// For example, the following defines a profile "devsso" and specifies the AWS SSO parameters that defines the target
// account, role, sign-on portal, and the region where the user portal is located. Note: all SSO arguments must be
// For example, the following defines a profile "devsso" and specifies the AWS
// SSO parameters that defines the target account, role, sign-on portal, and
// the region where the user portal is located. Note: all SSO arguments must be
// provided, or an error will be returned.
//
// [profile devsso]
// sso_start_url = https://my-sso-portal.awsapps.com/start
// sso_role_name = SSOReadOnlyRole
// sso_region = us-east-1
// sso_account_id = 123456789012
// [profile devsso]
// sso_start_url = https://my-sso-portal.awsapps.com/start
// sso_role_name = SSOReadOnlyRole
// sso_region = us-east-1
// sso_account_id = 123456789012
//
// Using the config module, you can load the AWS SDK shared configuration, and specify that this profile be used to
// retrieve credentials. For example:
// Using the config module, you can load the AWS SDK shared configuration, and
// specify that this profile be used to retrieve credentials. For example:
//
// config, err := config.LoadDefaultConfig(context.TODO(), config.WithSharedConfigProfile("devsso"))
// if err != nil {
// return err
// }
// config, err := config.LoadDefaultConfig(context.TODO(), config.WithSharedConfigProfile("devsso"))
// if err != nil {
// return err
// }
//
// Programmatically loading AWS SSO credentials directly
// # Programmatically loading AWS SSO credentials directly
//
// You can programmatically construct the AWS SSO Provider in your application, and provide the necessary information
// to load and retrieve temporary credentials using an access token from ~/.aws/sso/cache.
// You can programmatically construct the AWS SSO Provider in your application,
// and provide the necessary information to load and retrieve temporary
// credentials using an access token from ~/.aws/sso/cache.
//
// client := sso.NewFromConfig(cfg)
// client := sso.NewFromConfig(cfg)
//
// var provider aws.CredentialsProvider
// provider = ssocreds.New(client, "123456789012", "SSOReadOnlyRole", "us-east-1", "https://my-sso-portal.awsapps.com/start")
// var provider aws.CredentialsProvider
// provider = ssocreds.New(client, "123456789012", "SSOReadOnlyRole", "us-east-1", "https://my-sso-portal.awsapps.com/start")
//
// // Wrap the provider with aws.CredentialsCache to cache the credentials until their expire time
// provider = aws.NewCredentialsCache(provider)
// // Wrap the provider with aws.CredentialsCache to cache the credentials until their expire time
// provider = aws.NewCredentialsCache(provider)
//
// credentials, err := provider.Retrieve(context.TODO())
// if err != nil {
// return err
// }
// credentials, err := provider.Retrieve(context.TODO())
// if err != nil {
// return err
// }
//
// It is important that you wrap the Provider with aws.CredentialsCache if you are programmatically constructing the
// provider directly. This prevents your application from accessing the cached access token and requesting new
// It is important that you wrap the Provider with aws.CredentialsCache if you
// are programmatically constructing the provider directly. This prevents your
// application from accessing the cached access token and requesting new
// credentials each time the credentials are used.
//
// Additional Resources
// # Additional Resources
//
// Configuring the AWS CLI to use AWS Single Sign-On: https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-sso.html
// Configuring the AWS CLI to use AWS Single Sign-On:
// https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-sso.html
//
// AWS Single Sign-On User Guide: https://docs.aws.amazon.com/singlesignon/latest/userguide/what-is.html
// AWS Single Sign-On User Guide:
// https://docs.aws.amazon.com/singlesignon/latest/userguide/what-is.html
package ssocreds

View File

@ -1,10 +0,0 @@
//go:build !windows
// +build !windows
package ssocreds
import "os"
func getHomeDirectory() string {
return os.Getenv("HOME")
}

View File

@ -1,7 +0,0 @@
package ssocreds
import "os"
func getHomeDirectory() string {
return os.Getenv("USERPROFILE")
}

View File

@ -1,184 +0,0 @@
package ssocreds
import (
"context"
"crypto/sha1"
"encoding/hex"
"encoding/json"
"fmt"
"io/ioutil"
"path/filepath"
"strings"
"time"
"github.com/aws/aws-sdk-go-v2/aws"
"github.com/aws/aws-sdk-go-v2/internal/sdk"
"github.com/aws/aws-sdk-go-v2/service/sso"
)
// ProviderName is the name of the provider used to specify the source of credentials.
const ProviderName = "SSOProvider"
var defaultCacheLocation func() string
func defaultCacheLocationImpl() string {
return filepath.Join(getHomeDirectory(), ".aws", "sso", "cache")
}
func init() {
defaultCacheLocation = defaultCacheLocationImpl
}
// GetRoleCredentialsAPIClient is a API client that implements the GetRoleCredentials operation.
type GetRoleCredentialsAPIClient interface {
GetRoleCredentials(ctx context.Context, params *sso.GetRoleCredentialsInput, optFns ...func(*sso.Options)) (*sso.GetRoleCredentialsOutput, error)
}
// Options is the Provider options structure.
type Options struct {
// The Client which is configured for the AWS Region where the AWS SSO user portal is located.
Client GetRoleCredentialsAPIClient
// The AWS account that is assigned to the user.
AccountID string
// The role name that is assigned to the user.
RoleName string
// The URL that points to the organization's AWS Single Sign-On (AWS SSO) user portal.
StartURL string
}
// Provider is an AWS credential provider that retrieves temporary AWS credentials by exchanging an SSO login token.
type Provider struct {
options Options
}
// New returns a new AWS Single Sign-On (AWS SSO) credential provider. The provided client is expected to be configured
// for the AWS Region where the AWS SSO user portal is located.
func New(client GetRoleCredentialsAPIClient, accountID, roleName, startURL string, optFns ...func(options *Options)) *Provider {
options := Options{
Client: client,
AccountID: accountID,
RoleName: roleName,
StartURL: startURL,
}
for _, fn := range optFns {
fn(&options)
}
return &Provider{
options: options,
}
}
// Retrieve retrieves temporary AWS credentials from the configured Amazon Single Sign-On (AWS SSO) user portal
// by exchanging the accessToken present in ~/.aws/sso/cache.
func (p *Provider) Retrieve(ctx context.Context) (aws.Credentials, error) {
tokenFile, err := loadTokenFile(p.options.StartURL)
if err != nil {
return aws.Credentials{}, err
}
output, err := p.options.Client.GetRoleCredentials(ctx, &sso.GetRoleCredentialsInput{
AccessToken: &tokenFile.AccessToken,
AccountId: &p.options.AccountID,
RoleName: &p.options.RoleName,
})
if err != nil {
return aws.Credentials{}, err
}
return aws.Credentials{
AccessKeyID: aws.ToString(output.RoleCredentials.AccessKeyId),
SecretAccessKey: aws.ToString(output.RoleCredentials.SecretAccessKey),
SessionToken: aws.ToString(output.RoleCredentials.SessionToken),
Expires: time.Unix(0, output.RoleCredentials.Expiration*int64(time.Millisecond)).UTC(),
CanExpire: true,
Source: ProviderName,
}, nil
}
func getCacheFileName(url string) (string, error) {
hash := sha1.New()
_, err := hash.Write([]byte(url))
if err != nil {
return "", err
}
return strings.ToLower(hex.EncodeToString(hash.Sum(nil))) + ".json", nil
}
type rfc3339 time.Time
func (r *rfc3339) UnmarshalJSON(bytes []byte) error {
var value string
if err := json.Unmarshal(bytes, &value); err != nil {
return err
}
parse, err := time.Parse(time.RFC3339, value)
if err != nil {
return fmt.Errorf("expected RFC3339 timestamp: %w", err)
}
*r = rfc3339(parse)
return nil
}
type token struct {
AccessToken string `json:"accessToken"`
ExpiresAt rfc3339 `json:"expiresAt"`
Region string `json:"region,omitempty"`
StartURL string `json:"startUrl,omitempty"`
}
func (t token) Expired() bool {
return sdk.NowTime().Round(0).After(time.Time(t.ExpiresAt))
}
// InvalidTokenError is the error type that is returned if loaded token has expired or is otherwise invalid.
// To refresh the SSO session run aws sso login with the corresponding profile.
type InvalidTokenError struct {
Err error
}
func (i *InvalidTokenError) Unwrap() error {
return i.Err
}
func (i *InvalidTokenError) Error() string {
const msg = "the SSO session has expired or is invalid"
if i.Err == nil {
return msg
}
return msg + ": " + i.Err.Error()
}
func loadTokenFile(startURL string) (t token, err error) {
key, err := getCacheFileName(startURL)
if err != nil {
return token{}, &InvalidTokenError{Err: err}
}
fileBytes, err := ioutil.ReadFile(filepath.Join(defaultCacheLocation(), key))
if err != nil {
return token{}, &InvalidTokenError{Err: err}
}
if err := json.Unmarshal(fileBytes, &t); err != nil {
return token{}, &InvalidTokenError{Err: err}
}
if len(t.AccessToken) == 0 {
return token{}, &InvalidTokenError{}
}
if t.Expired() {
return token{}, &InvalidTokenError{Err: fmt.Errorf("access token is expired")}
}
return t, nil
}

View File

@ -0,0 +1,233 @@
package ssocreds
import (
"crypto/sha1"
"encoding/hex"
"encoding/json"
"fmt"
"io/ioutil"
"os"
"path/filepath"
"strconv"
"strings"
"time"
"github.com/aws/aws-sdk-go-v2/internal/sdk"
"github.com/aws/aws-sdk-go-v2/internal/shareddefaults"
)
var osUserHomeDur = shareddefaults.UserHomeDir
// StandardCachedTokenFilepath returns the filepath for the cached SSO token file, or
// error if unable get derive the path. Key that will be used to compute a SHA1
// value that is hex encoded.
//
// Derives the filepath using the Key as:
//
// ~/.aws/sso/cache/<sha1-hex-encoded-key>.json
func StandardCachedTokenFilepath(key string) (string, error) {
homeDir := osUserHomeDur()
if len(homeDir) == 0 {
return "", fmt.Errorf("unable to get USER's home directory for cached token")
}
hash := sha1.New()
if _, err := hash.Write([]byte(key)); err != nil {
return "", fmt.Errorf("unable to compute cached token filepath key SHA1 hash, %w", err)
}
cacheFilename := strings.ToLower(hex.EncodeToString(hash.Sum(nil))) + ".json"
return filepath.Join(homeDir, ".aws", "sso", "cache", cacheFilename), nil
}
type tokenKnownFields struct {
AccessToken string `json:"accessToken,omitempty"`
ExpiresAt *rfc3339 `json:"expiresAt,omitempty"`
RefreshToken string `json:"refreshToken,omitempty"`
ClientID string `json:"clientId,omitempty"`
ClientSecret string `json:"clientSecret,omitempty"`
}
type token struct {
tokenKnownFields
UnknownFields map[string]interface{} `json:"-"`
}
func (t token) MarshalJSON() ([]byte, error) {
fields := map[string]interface{}{}
setTokenFieldString(fields, "accessToken", t.AccessToken)
setTokenFieldRFC3339(fields, "expiresAt", t.ExpiresAt)
setTokenFieldString(fields, "refreshToken", t.RefreshToken)
setTokenFieldString(fields, "clientId", t.ClientID)
setTokenFieldString(fields, "clientSecret", t.ClientSecret)
for k, v := range t.UnknownFields {
if _, ok := fields[k]; ok {
return nil, fmt.Errorf("unknown token field %v, duplicates known field", k)
}
fields[k] = v
}
return json.Marshal(fields)
}
func setTokenFieldString(fields map[string]interface{}, key, value string) {
if value == "" {
return
}
fields[key] = value
}
func setTokenFieldRFC3339(fields map[string]interface{}, key string, value *rfc3339) {
if value == nil {
return
}
fields[key] = value
}
func (t *token) UnmarshalJSON(b []byte) error {
var fields map[string]interface{}
if err := json.Unmarshal(b, &fields); err != nil {
return nil
}
t.UnknownFields = map[string]interface{}{}
for k, v := range fields {
var err error
switch k {
case "accessToken":
err = getTokenFieldString(v, &t.AccessToken)
case "expiresAt":
err = getTokenFieldRFC3339(v, &t.ExpiresAt)
case "refreshToken":
err = getTokenFieldString(v, &t.RefreshToken)
case "clientId":
err = getTokenFieldString(v, &t.ClientID)
case "clientSecret":
err = getTokenFieldString(v, &t.ClientSecret)
default:
t.UnknownFields[k] = v
}
if err != nil {
return fmt.Errorf("field %q, %w", k, err)
}
}
return nil
}
func getTokenFieldString(v interface{}, value *string) error {
var ok bool
*value, ok = v.(string)
if !ok {
return fmt.Errorf("expect value to be string, got %T", v)
}
return nil
}
func getTokenFieldRFC3339(v interface{}, value **rfc3339) error {
var stringValue string
if err := getTokenFieldString(v, &stringValue); err != nil {
return err
}
timeValue, err := parseRFC3339(stringValue)
if err != nil {
return err
}
*value = &timeValue
return nil
}
func loadCachedToken(filename string) (token, error) {
fileBytes, err := ioutil.ReadFile(filename)
if err != nil {
return token{}, fmt.Errorf("failed to read cached SSO token file, %w", err)
}
var t token
if err := json.Unmarshal(fileBytes, &t); err != nil {
return token{}, fmt.Errorf("failed to parse cached SSO token file, %w", err)
}
if len(t.AccessToken) == 0 || t.ExpiresAt == nil || time.Time(*t.ExpiresAt).IsZero() {
return token{}, fmt.Errorf(
"cached SSO token must contain accessToken and expiresAt fields")
}
return t, nil
}
func storeCachedToken(filename string, t token, fileMode os.FileMode) (err error) {
tmpFilename := filename + ".tmp-" + strconv.FormatInt(sdk.NowTime().UnixNano(), 10)
if err := writeCacheFile(tmpFilename, fileMode, t); err != nil {
return err
}
if err := os.Rename(tmpFilename, filename); err != nil {
return fmt.Errorf("failed to replace old cached SSO token file, %w", err)
}
return nil
}
func writeCacheFile(filename string, fileMode os.FileMode, t token) (err error) {
var f *os.File
f, err = os.OpenFile(filename, os.O_CREATE|os.O_TRUNC|os.O_RDWR, fileMode)
if err != nil {
return fmt.Errorf("failed to create cached SSO token file %w", err)
}
defer func() {
closeErr := f.Close()
if err == nil && closeErr != nil {
err = fmt.Errorf("failed to close cached SSO token file, %w", closeErr)
}
}()
encoder := json.NewEncoder(f)
if err = encoder.Encode(t); err != nil {
return fmt.Errorf("failed to serialize cached SSO token, %w", err)
}
return nil
}
type rfc3339 time.Time
func parseRFC3339(v string) (rfc3339, error) {
parsed, err := time.Parse(time.RFC3339, v)
if err != nil {
return rfc3339{}, fmt.Errorf("expected RFC3339 timestamp: %w", err)
}
return rfc3339(parsed), nil
}
func (r *rfc3339) UnmarshalJSON(bytes []byte) (err error) {
var value string
// Use JSON unmarshal to unescape the quoted value making use of JSON's
// unquoting rules.
if err = json.Unmarshal(bytes, &value); err != nil {
return err
}
*r, err = parseRFC3339(value)
return nil
}
func (r *rfc3339) MarshalJSON() ([]byte, error) {
value := time.Time(*r).Format(time.RFC3339)
// Use JSON unmarshal to unescape the quoted value making use of JSON's
// quoting rules.
return json.Marshal(value)
}

View File

@ -0,0 +1,152 @@
package ssocreds
import (
"context"
"time"
"github.com/aws/aws-sdk-go-v2/aws"
"github.com/aws/aws-sdk-go-v2/internal/sdk"
"github.com/aws/aws-sdk-go-v2/service/sso"
)
// ProviderName is the name of the provider used to specify the source of
// credentials.
const ProviderName = "SSOProvider"
// GetRoleCredentialsAPIClient is a API client that implements the
// GetRoleCredentials operation.
type GetRoleCredentialsAPIClient interface {
GetRoleCredentials(context.Context, *sso.GetRoleCredentialsInput, ...func(*sso.Options)) (
*sso.GetRoleCredentialsOutput, error,
)
}
// Options is the Provider options structure.
type Options struct {
// The Client which is configured for the AWS Region where the AWS SSO user
// portal is located.
Client GetRoleCredentialsAPIClient
// The AWS account that is assigned to the user.
AccountID string
// The role name that is assigned to the user.
RoleName string
// The URL that points to the organization's AWS Single Sign-On (AWS SSO)
// user portal.
StartURL string
// The filepath the cached token will be retrieved from. If unset Provider will
// use the startURL to determine the filepath at.
//
// ~/.aws/sso/cache/<sha1-hex-encoded-startURL>.json
//
// If custom cached token filepath is used, the Provider's startUrl
// parameter will be ignored.
CachedTokenFilepath string
// Used by the SSOCredentialProvider if a token configuration
// profile is used in the shared config
SSOTokenProvider *SSOTokenProvider
}
// Provider is an AWS credential provider that retrieves temporary AWS
// credentials by exchanging an SSO login token.
type Provider struct {
options Options
cachedTokenFilepath string
}
// New returns a new AWS Single Sign-On (AWS SSO) credential provider. The
// provided client is expected to be configured for the AWS Region where the
// AWS SSO user portal is located.
func New(client GetRoleCredentialsAPIClient, accountID, roleName, startURL string, optFns ...func(options *Options)) *Provider {
options := Options{
Client: client,
AccountID: accountID,
RoleName: roleName,
StartURL: startURL,
}
for _, fn := range optFns {
fn(&options)
}
return &Provider{
options: options,
cachedTokenFilepath: options.CachedTokenFilepath,
}
}
// Retrieve retrieves temporary AWS credentials from the configured Amazon
// Single Sign-On (AWS SSO) user portal by exchanging the accessToken present
// in ~/.aws/sso/cache. However, if a token provider configuration exists
// in the shared config, then we ought to use the token provider rather then
// direct access on the cached token.
func (p *Provider) Retrieve(ctx context.Context) (aws.Credentials, error) {
var accessToken *string
if p.options.SSOTokenProvider != nil {
token, err := p.options.SSOTokenProvider.RetrieveBearerToken(ctx)
if err != nil {
return aws.Credentials{}, err
}
accessToken = &token.Value
} else {
if p.cachedTokenFilepath == "" {
cachedTokenFilepath, err := StandardCachedTokenFilepath(p.options.StartURL)
if err != nil {
return aws.Credentials{}, &InvalidTokenError{Err: err}
}
p.cachedTokenFilepath = cachedTokenFilepath
}
tokenFile, err := loadCachedToken(p.cachedTokenFilepath)
if err != nil {
return aws.Credentials{}, &InvalidTokenError{Err: err}
}
if tokenFile.ExpiresAt == nil || sdk.NowTime().After(time.Time(*tokenFile.ExpiresAt)) {
return aws.Credentials{}, &InvalidTokenError{}
}
accessToken = &tokenFile.AccessToken
}
output, err := p.options.Client.GetRoleCredentials(ctx, &sso.GetRoleCredentialsInput{
AccessToken: accessToken,
AccountId: &p.options.AccountID,
RoleName: &p.options.RoleName,
})
if err != nil {
return aws.Credentials{}, err
}
return aws.Credentials{
AccessKeyID: aws.ToString(output.RoleCredentials.AccessKeyId),
SecretAccessKey: aws.ToString(output.RoleCredentials.SecretAccessKey),
SessionToken: aws.ToString(output.RoleCredentials.SessionToken),
CanExpire: true,
Expires: time.Unix(0, output.RoleCredentials.Expiration*int64(time.Millisecond)).UTC(),
Source: ProviderName,
}, nil
}
// InvalidTokenError is the error type that is returned if loaded token has
// expired or is otherwise invalid. To refresh the SSO session run AWS SSO
// login with the corresponding profile.
type InvalidTokenError struct {
Err error
}
func (i *InvalidTokenError) Unwrap() error {
return i.Err
}
func (i *InvalidTokenError) Error() string {
const msg = "the SSO session has expired or is invalid"
if i.Err == nil {
return msg
}
return msg + ": " + i.Err.Error()
}

View File

@ -0,0 +1,147 @@
package ssocreds
import (
"context"
"fmt"
"os"
"time"
"github.com/aws/aws-sdk-go-v2/aws"
"github.com/aws/aws-sdk-go-v2/internal/sdk"
"github.com/aws/aws-sdk-go-v2/service/ssooidc"
"github.com/aws/smithy-go/auth/bearer"
)
// CreateTokenAPIClient provides the interface for the SSOTokenProvider's API
// client for calling CreateToken operation to refresh the SSO token.
type CreateTokenAPIClient interface {
CreateToken(context.Context, *ssooidc.CreateTokenInput, ...func(*ssooidc.Options)) (
*ssooidc.CreateTokenOutput, error,
)
}
// SSOTokenProviderOptions provides the options for configuring the
// SSOTokenProvider.
type SSOTokenProviderOptions struct {
// Client that can be overridden
Client CreateTokenAPIClient
// The set of API Client options to be applied when invoking the
// CreateToken operation.
ClientOptions []func(*ssooidc.Options)
// The path the file containing the cached SSO token will be read from.
// Initialized the NewSSOTokenProvider's cachedTokenFilepath parameter.
CachedTokenFilepath string
}
// SSOTokenProvider provides an utility for refreshing SSO AccessTokens for
// Bearer Authentication. The SSOTokenProvider can only be used to refresh
// already cached SSO Tokens. This utility cannot perform the initial SSO
// create token.
//
// The SSOTokenProvider is not safe to use concurrently. It must be wrapped in
// a utility such as smithy-go's auth/bearer#TokenCache. The SDK's
// config.LoadDefaultConfig will automatically wrap the SSOTokenProvider with
// the smithy-go TokenCache, if the external configuration loaded configured
// for an SSO session.
//
// The initial SSO create token should be preformed with the AWS CLI before the
// Go application using the SSOTokenProvider will need to retrieve the SSO
// token. If the AWS CLI has not created the token cache file, this provider
// will return an error when attempting to retrieve the cached token.
//
// This provider will attempt to refresh the cached SSO token periodically if
// needed when RetrieveBearerToken is called.
//
// A utility such as the AWS CLI must be used to initially create the SSO
// session and cached token file.
// https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-sso.html
type SSOTokenProvider struct {
options SSOTokenProviderOptions
}
var _ bearer.TokenProvider = (*SSOTokenProvider)(nil)
// NewSSOTokenProvider returns an initialized SSOTokenProvider that will
// periodically refresh the SSO token cached stored in the cachedTokenFilepath.
// The cachedTokenFilepath file's content will be rewritten by the token
// provider when the token is refreshed.
//
// The client must be configured for the AWS region the SSO token was created for.
func NewSSOTokenProvider(client CreateTokenAPIClient, cachedTokenFilepath string, optFns ...func(o *SSOTokenProviderOptions)) *SSOTokenProvider {
options := SSOTokenProviderOptions{
Client: client,
CachedTokenFilepath: cachedTokenFilepath,
}
for _, fn := range optFns {
fn(&options)
}
provider := &SSOTokenProvider{
options: options,
}
return provider
}
// RetrieveBearerToken returns the SSO token stored in the cachedTokenFilepath
// the SSOTokenProvider was created with. If the token has expired
// RetrieveBearerToken will attempt to refresh it. If the token cannot be
// refreshed or is not present an error will be returned.
//
// A utility such as the AWS CLI must be used to initially create the SSO
// session and cached token file. https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-sso.html
func (p SSOTokenProvider) RetrieveBearerToken(ctx context.Context) (bearer.Token, error) {
cachedToken, err := loadCachedToken(p.options.CachedTokenFilepath)
if err != nil {
return bearer.Token{}, err
}
if cachedToken.ExpiresAt != nil && sdk.NowTime().After(time.Time(*cachedToken.ExpiresAt)) {
cachedToken, err = p.refreshToken(ctx, cachedToken)
if err != nil {
return bearer.Token{}, fmt.Errorf("refresh cached SSO token failed, %w", err)
}
}
expiresAt := aws.ToTime((*time.Time)(cachedToken.ExpiresAt))
return bearer.Token{
Value: cachedToken.AccessToken,
CanExpire: !expiresAt.IsZero(),
Expires: expiresAt,
}, nil
}
func (p SSOTokenProvider) refreshToken(ctx context.Context, cachedToken token) (token, error) {
if cachedToken.ClientSecret == "" || cachedToken.ClientID == "" || cachedToken.RefreshToken == "" {
return token{}, fmt.Errorf("cached SSO token is expired, or not present, and cannot be refreshed")
}
createResult, err := p.options.Client.CreateToken(ctx, &ssooidc.CreateTokenInput{
ClientId: &cachedToken.ClientID,
ClientSecret: &cachedToken.ClientSecret,
RefreshToken: &cachedToken.RefreshToken,
GrantType: aws.String("refresh_token"),
}, p.options.ClientOptions...)
if err != nil {
return token{}, fmt.Errorf("unable to refresh SSO token, %w", err)
}
expiresAt := sdk.NowTime().Add(time.Duration(createResult.ExpiresIn) * time.Second)
cachedToken.AccessToken = aws.ToString(createResult.AccessToken)
cachedToken.ExpiresAt = (*rfc3339)(&expiresAt)
cachedToken.RefreshToken = aws.ToString(createResult.RefreshToken)
fileInfo, err := os.Stat(p.options.CachedTokenFilepath)
if err != nil {
return token{}, fmt.Errorf("failed to stat cached SSO token file %w", err)
}
if err = storeCachedToken(p.options.CachedTokenFilepath, cachedToken, fileInfo.Mode()); err != nil {
return token{}, fmt.Errorf("unable to cache refreshed SSO token, %w", err)
}
return cachedToken, nil
}

View File

@ -8,31 +8,31 @@
// ensure synchronous usage of the AssumeRoleProvider if the value is shared
// between multiple Credentials or service clients.
//
// Assume Role
// # Assume Role
//
// To assume an IAM role using STS with the SDK you can create a new Credentials
// with the SDKs's stscreds package.
//
// // Initial credentials loaded from SDK's default credential chain. Such as
// // the environment, shared credentials (~/.aws/credentials), or EC2 Instance
// // Role. These credentials will be used to to make the STS Assume Role API.
// cfg, err := config.LoadDefaultConfig(context.TODO())
// if err != nil {
// panic(err)
// }
// // Initial credentials loaded from SDK's default credential chain. Such as
// // the environment, shared credentials (~/.aws/credentials), or EC2 Instance
// // Role. These credentials will be used to to make the STS Assume Role API.
// cfg, err := config.LoadDefaultConfig(context.TODO())
// if err != nil {
// panic(err)
// }
//
// // Create the credentials from AssumeRoleProvider to assume the role
// // referenced by the "myRoleARN" ARN.
// stsSvc := sts.NewFromConfig(cfg)
// creds := stscreds.NewAssumeRoleProvider(stsSvc, "myRoleArn")
// // Create the credentials from AssumeRoleProvider to assume the role
// // referenced by the "myRoleARN" ARN.
// stsSvc := sts.NewFromConfig(cfg)
// creds := stscreds.NewAssumeRoleProvider(stsSvc, "myRoleArn")
//
// cfg.Credentials = aws.NewCredentialsCache(creds)
// cfg.Credentials = aws.NewCredentialsCache(creds)
//
// // Create service client value configured for credentials
// // from assumed role.
// svc := s3.NewFromConfig(cfg)
// // Create service client value configured for credentials
// // from assumed role.
// svc := s3.NewFromConfig(cfg)
//
// Assume Role with custom MFA Token provider
// # Assume Role with custom MFA Token provider
//
// To assume an IAM role with a MFA token you can either specify a custom MFA
// token provider or use the SDK's built in StdinTokenProvider that will prompt
@ -43,29 +43,29 @@
// With a custom token provider, the provider is responsible for refreshing the
// token code when called.
//
// cfg, err := config.LoadDefaultConfig(context.TODO())
// if err != nil {
// panic(err)
// }
// cfg, err := config.LoadDefaultConfig(context.TODO())
// if err != nil {
// panic(err)
// }
//
// staticTokenProvider := func() (string, error) {
// return someTokenCode, nil
// }
// staticTokenProvider := func() (string, error) {
// return someTokenCode, nil
// }
//
// // Create the credentials from AssumeRoleProvider to assume the role
// // referenced by the "myRoleARN" ARN using the MFA token code provided.
// creds := stscreds.NewAssumeRoleProvider(sts.NewFromConfig(cfg), "myRoleArn", func(o *stscreds.AssumeRoleOptions) {
// o.SerialNumber = aws.String("myTokenSerialNumber")
// o.TokenProvider = staticTokenProvider
// })
// // Create the credentials from AssumeRoleProvider to assume the role
// // referenced by the "myRoleARN" ARN using the MFA token code provided.
// creds := stscreds.NewAssumeRoleProvider(sts.NewFromConfig(cfg), "myRoleArn", func(o *stscreds.AssumeRoleOptions) {
// o.SerialNumber = aws.String("myTokenSerialNumber")
// o.TokenProvider = staticTokenProvider
// })
//
// cfg.Credentials = aws.NewCredentialsCache(creds)
// cfg.Credentials = aws.NewCredentialsCache(creds)
//
// // Create service client value configured for credentials
// // from assumed role.
// svc := s3.NewFromConfig(cfg)
// // Create service client value configured for credentials
// // from assumed role.
// svc := s3.NewFromConfig(cfg)
//
// Assume Role with MFA Token Provider
// # Assume Role with MFA Token Provider
//
// To assume an IAM role with MFA for longer running tasks where the credentials
// may need to be refreshed setting the TokenProvider field of AssumeRoleProvider
@ -80,23 +80,23 @@
// have undesirable results as the StdinTokenProvider will not be synchronized. A
// single Credentials with an AssumeRoleProvider can be shared safely.
//
// cfg, err := config.LoadDefaultConfig(context.TODO())
// if err != nil {
// panic(err)
// }
// cfg, err := config.LoadDefaultConfig(context.TODO())
// if err != nil {
// panic(err)
// }
//
// // Create the credentials from AssumeRoleProvider to assume the role
// // referenced by the "myRoleARN" ARN using the MFA token code provided.
// creds := stscreds.NewAssumeRoleProvider(sts.NewFromConfig(cfg), "myRoleArn", func(o *stscreds.AssumeRoleOptions) {
// o.SerialNumber = aws.String("myTokenSerialNumber")
// o.TokenProvider = stscreds.StdinTokenProvider
// })
// // Create the credentials from AssumeRoleProvider to assume the role
// // referenced by the "myRoleARN" ARN using the MFA token code provided.
// creds := stscreds.NewAssumeRoleProvider(sts.NewFromConfig(cfg), "myRoleArn", func(o *stscreds.AssumeRoleOptions) {
// o.SerialNumber = aws.String("myTokenSerialNumber")
// o.TokenProvider = stscreds.StdinTokenProvider
// })
//
// cfg.Credentials = aws.NewCredentialsCache(creds)
// cfg.Credentials = aws.NewCredentialsCache(creds)
//
// // Create service client value configured for credentials
// // from assumed role.
// svc := s3.NewFromConfig(cfg)
// // Create service client value configured for credentials
// // from assumed role.
// svc := s3.NewFromConfig(cfg)
package stscreds
import (