mirror of
https://github.com/quay/quay.git
synced 2026-01-26 06:21:37 +03:00
storage: Modify the STS S3 implementation of the storage backend to use Web Identity Tokens when available (PROJQUAY-8692) (#3715)
Backport the Quay STS token file implementation from https://github.com/quay/quay/pull/3670 --------- Co-authored-by: Mathieu Bouchard <mathieu.bouchard@mcn.gouv.qc.ca> Co-authored-by: Mathieu Bouchard <83231959+bouchardmathieu-qc@users.noreply.github.com>
This commit is contained in:
committed by
GitHub
parent
0a4e248ecb
commit
48691d648f
8
build.sh
8
build.sh
@@ -18,7 +18,7 @@ git checkout $SHA
|
||||
|
||||
REPO=quay.io/quay/quay:$SHA
|
||||
|
||||
# Use buildah or podman or docker
|
||||
# Use buildah, podman or docker
|
||||
if [ -x /usr/bin/buildah ]; then
|
||||
BUILDER="/usr/bin/buildah bud"
|
||||
elif [ -x /usr/bin/podman ]; then
|
||||
@@ -26,7 +26,11 @@ elif [ -x /usr/bin/podman ]; then
|
||||
elif [ -x /usr/bin/docker ] ; then
|
||||
BUILDER="/usr/bin/docker build"
|
||||
fi
|
||||
echo $BUILDER
|
||||
if [[ -z "$BUILDER" ]]; then
|
||||
echo 'Unable to find buildah, podman or docker' >&2
|
||||
exit 1
|
||||
fi
|
||||
echo $BUILDER
|
||||
|
||||
$BUILDER -t $REPO .
|
||||
echo $REPO
|
||||
|
||||
@@ -379,6 +379,13 @@ func NewDistributedStorageArgs(storageArgs map[string]interface{}) (*shared.Dist
|
||||
}
|
||||
}
|
||||
|
||||
if value, ok := storageArgs["sts_web_token_filen"]; ok {
|
||||
newDistributedStorageArgs.STSWebIdentityTokenFile, ok = value.(string)
|
||||
if !ok {
|
||||
return newDistributedStorageArgs, errors.New("sts_web_token_file must be a string")
|
||||
}
|
||||
}
|
||||
|
||||
return newDistributedStorageArgs, nil
|
||||
}
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
"net/url"
|
||||
"strconv"
|
||||
"time"
|
||||
"os"
|
||||
|
||||
"github.com/Azure/azure-storage-blob-go/azblob"
|
||||
"github.com/aws/aws-sdk-go/aws"
|
||||
@@ -162,30 +163,73 @@ func ValidateStorage(opts Options, storageName string, storageType string, args
|
||||
}
|
||||
|
||||
roleArn := args.STSRoleArn
|
||||
sess := session.Must(session.NewSession(&aws.Config{
|
||||
Credentials: awscredentials.NewStaticCredentials(args.STSUserAccessKey, args.STSUserSecretKey, ""),
|
||||
}))
|
||||
svc := sts.New(sess)
|
||||
if roleArn == "" {
|
||||
roleArn = os.Getenv("AWS_ROLE_ARN")
|
||||
}
|
||||
roleToAssumeArn := roleArn
|
||||
durationSeconds := int64(3600)
|
||||
assumeRoleInput := &sts.AssumeRoleInput{
|
||||
RoleArn: aws.String(roleToAssumeArn),
|
||||
RoleSessionName: aws.String("quay"),
|
||||
DurationSeconds: aws.Int64(durationSeconds),
|
||||
}
|
||||
assumeRoleOutput, err := svc.AssumeRole(assumeRoleInput)
|
||||
if err != nil {
|
||||
errors = append(errors, ValidationError{
|
||||
Tags: []string{"DISTRIBUTED_STORAGE_CONFIG"},
|
||||
FieldGroup: fgName,
|
||||
Message: "Could not fetch credentials from STS. Error: " + err.Error(),
|
||||
})
|
||||
break
|
||||
|
||||
webIdentityTokenFile := args.STSWebIdentityTokenFile
|
||||
// Only check the Web Identity Token File variable if no other credentials are present in the config
|
||||
if args.STSUserAccessKey == "" && args.STSUserSecretKey == "" && args.STSWebIdentityTokenFile == "" {
|
||||
webIdentityTokenFile = os.Getenv("AWS_WEB_IDENTITY_TOKEN_FILE")
|
||||
}
|
||||
|
||||
accessKey := *assumeRoleOutput.Credentials.AccessKeyId
|
||||
secretKey := *assumeRoleOutput.Credentials.SecretAccessKey
|
||||
token = *assumeRoleOutput.Credentials.SessionToken
|
||||
var credentials *sts.Credentials
|
||||
// Prefer using web tokens to authenticate and fallback to access and secret keys
|
||||
if webIdentityTokenFile != "" {
|
||||
sess := session.Must(session.NewSession())
|
||||
svc := sts.New(sess)
|
||||
webIdentityToken, err := os.ReadFile(webIdentityTokenFile)
|
||||
if err != nil {
|
||||
errors = append(errors, ValidationError{
|
||||
Tags: []string{"DISTRIBUTED_STORAGE_CONFIG"},
|
||||
FieldGroup: fgName,
|
||||
Message: "Could not read the STS Web Identity Token File, Error: " + err.Error(),
|
||||
})
|
||||
break
|
||||
}
|
||||
assumeRoleInput := &sts.AssumeRoleWithWebIdentityInput{
|
||||
RoleArn: aws.String(roleToAssumeArn),
|
||||
RoleSessionName: aws.String("quay"),
|
||||
DurationSeconds: aws.Int64(durationSeconds),
|
||||
WebIdentityToken: aws.String(string(webIdentityToken)),
|
||||
}
|
||||
assumeRoleOutput, err := svc.AssumeRoleWithWebIdentity(assumeRoleInput)
|
||||
if err != nil {
|
||||
errors = append(errors, ValidationError{
|
||||
Tags: []string{"DISTRIBUTED_STORAGE_CONFIG"},
|
||||
FieldGroup: fgName,
|
||||
Message: "Could not fetch credentials from STS with Web Identity Token. Error: " + err.Error(),
|
||||
})
|
||||
break
|
||||
}
|
||||
credentials = assumeRoleOutput.Credentials
|
||||
} else {
|
||||
sess := session.Must(session.NewSession(&aws.Config{
|
||||
Credentials: awscredentials.NewStaticCredentials(args.STSUserAccessKey, args.STSUserSecretKey, ""),
|
||||
}))
|
||||
svc := sts.New(sess)
|
||||
assumeRoleInput := &sts.AssumeRoleInput{
|
||||
RoleArn: aws.String(roleToAssumeArn),
|
||||
RoleSessionName: aws.String("quay"),
|
||||
DurationSeconds: aws.Int64(durationSeconds),
|
||||
}
|
||||
assumeRoleOutput, err := svc.AssumeRole(assumeRoleInput)
|
||||
if err != nil {
|
||||
errors = append(errors, ValidationError{
|
||||
Tags: []string{"DISTRIBUTED_STORAGE_CONFIG"},
|
||||
FieldGroup: fgName,
|
||||
Message: "Could not fetch credentials from STS. Error: " + err.Error(),
|
||||
})
|
||||
break
|
||||
}
|
||||
credentials = assumeRoleOutput.Credentials
|
||||
}
|
||||
|
||||
accessKey := *credentials.AccessKeyId
|
||||
secretKey := *credentials.SecretAccessKey
|
||||
token = *credentials.SessionToken
|
||||
bucketName = args.S3Bucket
|
||||
isSecure = true
|
||||
|
||||
|
||||
@@ -59,7 +59,8 @@ type DistributedStorageArgs struct {
|
||||
Providers map[string]interface{} `default:"" validate:"" json:"providers,omitempty" yaml:"providers,omitempty"`
|
||||
StorageConfig map[string]interface{} `default:"" validate:"" json:"storage_config,omitempty" yaml:"storage_config,omitempty"`
|
||||
// Args for STSS3Storage
|
||||
STSUserAccessKey string `default:"" validate:"" json:"sts_user_access_key,omitempty" yaml:"sts_user_access_key,omitempty"`
|
||||
STSUserSecretKey string `default:"" validate:"" json:"sts_user_secret_key,omitempty" yaml:"sts_user_secret_key,omitempty"`
|
||||
STSRoleArn string `default:"" validate:"" json:"sts_role_arn,omitempty" yaml:"sts_role_arn,omitempty"`
|
||||
STSUserAccessKey string `default:"" validate:"" json:"sts_user_access_key,omitempty" yaml:"sts_user_access_key,omitempty"`
|
||||
STSUserSecretKey string `default:"" validate:"" json:"sts_user_secret_key,omitempty" yaml:"sts_user_secret_key,omitempty"`
|
||||
STSRoleArn string `default:"" validate:"" json:"sts_role_arn,omitempty" yaml:"sts_role_arn,omitempty"`
|
||||
STSWebIdentityTokenFile string `default:"" validate:"" json:"sts_web_identity_token_file,omitempty" yaml:"sts_web_identity_token_file,omitempty"`
|
||||
}
|
||||
|
||||
@@ -1245,28 +1245,39 @@ class STSS3Storage(S3Storage):
|
||||
maximum_chunk_size_gb=None,
|
||||
signature_version="s3v4",
|
||||
):
|
||||
sts_client = boto3.client(
|
||||
"sts", aws_access_key_id=sts_user_access_key, aws_secret_access_key=sts_user_secret_key
|
||||
)
|
||||
assumed_role = sts_client.assume_role(RoleArn=sts_role_arn, RoleSessionName="quay")
|
||||
credentials = assumed_role["Credentials"]
|
||||
deferred_refreshable_credentials = DeferredRefreshableCredentials(
|
||||
refresh_using=create_assume_role_refresher(
|
||||
sts_client, {"RoleArn": sts_role_arn, "RoleSessionName": "quay"}
|
||||
),
|
||||
method="sts-assume-role",
|
||||
)
|
||||
if sts_user_access_key == "" or sts_user_secret_key == "":
|
||||
sts_client = boto3.client("sts")
|
||||
else:
|
||||
sts_client = boto3.client(
|
||||
"sts",
|
||||
aws_access_key_id=sts_user_access_key,
|
||||
aws_secret_access_key=sts_user_secret_key,
|
||||
)
|
||||
|
||||
# !! NOTE !! connect_kwargs here initializes the S3Storage Class not the s3 connection (mis leading re-use of the name)
|
||||
connect_kwargs = {
|
||||
"s3_access_key": credentials["AccessKeyId"],
|
||||
"s3_secret_key": credentials["SecretAccessKey"],
|
||||
"aws_session_token": credentials["SessionToken"],
|
||||
"s3_region": s3_region,
|
||||
"endpoint_url": endpoint_url,
|
||||
"maximum_chunk_size_gb": maximum_chunk_size_gb,
|
||||
"deferred_refreshable_credentials": deferred_refreshable_credentials,
|
||||
"signature_version": signature_version,
|
||||
}
|
||||
if sts_role_arn is not None:
|
||||
assumed_role = sts_client.assume_role(RoleArn=sts_role_arn, RoleSessionName="quay")
|
||||
credentials = assumed_role["Credentials"]
|
||||
deferred_refreshable_credentials = DeferredRefreshableCredentials(
|
||||
refresh_using=create_assume_role_refresher(
|
||||
sts_client, {"RoleArn": sts_role_arn, "RoleSessionName": "quay"}
|
||||
),
|
||||
method="sts-assume-role",
|
||||
)
|
||||
|
||||
connect_kwargs.update(
|
||||
{
|
||||
"s3_access_key": credentials["AccessKeyId"],
|
||||
"s3_secret_key": credentials["SecretAccessKey"],
|
||||
"aws_session_token": credentials["SessionToken"],
|
||||
"deferred_refreshable_credentials": deferred_refreshable_credentials,
|
||||
}
|
||||
)
|
||||
|
||||
super().__init__(context, storage_path, s3_bucket, **connect_kwargs)
|
||||
|
||||
Reference in New Issue
Block a user