Skip to content

Commit

Permalink
migrate lib/srv/discovery/fetchers eks to aws sdk v2
Browse files Browse the repository at this point in the history
  • Loading branch information
creack committed Dec 28, 2024
1 parent e0aac84 commit 8c5273e
Show file tree
Hide file tree
Showing 5 changed files with 217 additions and 169 deletions.
47 changes: 28 additions & 19 deletions lib/kube/utils/eks_token_signed.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,44 +19,53 @@
package utils

import (
"context"
"encoding/base64"
"time"

"github.com/aws/aws-sdk-go/service/sts"
"github.com/aws/aws-sdk-go/service/sts/stsiface"
v4 "github.com/aws/aws-sdk-go-v2/aws/signer/v4"
"github.com/aws/aws-sdk-go-v2/service/sts"
"github.com/aws/smithy-go/middleware"
smithyhttp "github.com/aws/smithy-go/transport/http"
"github.com/gravitational/trace"
"github.com/jonboulle/clockwork"
)

// STSPresignClient is the subset of the STS presign client we need to generate EKS tokens.
type STSPresignClient interface {
PresignGetCallerIdentity(ctx context.Context, params *sts.GetCallerIdentityInput, optFns ...func(*sts.PresignOptions)) (*v4.PresignedHTTPRequest, error)
}

// GenAWSEKSToken creates an AWS token to access EKS clusters.
// Logic from https://github.com/aws/aws-cli/blob/6c0d168f0b44136fc6175c57c090d4b115437ad1/awscli/customizations/eks/get_token.py#L211-L229
func GenAWSEKSToken(stsClient stsiface.STSAPI, clusterID string, clock clockwork.Clock) (string, time.Time, error) {
func GenAWSEKSToken(ctx context.Context, stsClient STSPresignClient, clusterID string, clock clockwork.Clock) (string, time.Time, error) {
const (
// The sts GetCallerIdentity request is valid for 15 minutes regardless of this parameters value after it has been
// signed.
requestPresignParam = 60
// The actual token expiration (presigned STS urls are valid for 15 minutes after timestamp in x-amz-date).
presignedURLExpiration = 15 * time.Minute
v1Prefix = "k8s-aws-v1."
clusterIDHeader = "x-k8s-aws-id"
)

// generate an sts:GetCallerIdentity request and add our custom cluster ID header
request, _ := stsClient.GetCallerIdentityRequest(&sts.GetCallerIdentityInput{})
request.HTTPRequest.Header.Add(clusterIDHeader, clusterID)

// Sign the request. The expires parameter (sets the x-amz-expires header) is
// currently ignored by STS, and the token expires 15 minutes after the x-amz-date
// timestamp regardless. We set it to 60 seconds for backwards compatibility (the
// parameter is a required argument to Presign(), and authenticators 0.3.0 and older are expecting a value between
// 0 and 60 on the server side).
// https://github.com/aws/aws-sdk-go/issues/2167
presignedURLString, err := request.Presign(requestPresignParam)
presignedReq, err := sts.NewPresignClient(nil).PresignGetCallerIdentity(ctx, nil, func(po *sts.PresignOptions) {
po.ClientOptions = append(po.ClientOptions, func(o *sts.Options) {
o.APIOptions = append(o.APIOptions, func(stack *middleware.Stack) error {
return stack.Finalize.Add(middleware.FinalizeMiddlewareFunc("ClusterIDHeaderMW", func(
ctx context.Context, input middleware.FinalizeInput, next middleware.FinalizeHandler,
) (middleware.FinalizeOutput, middleware.Metadata, error) {
req, ok := input.Request.(*smithyhttp.Request)
if ok {
req.Header.Add(clusterIDHeader, clusterID)
}
return next.HandleFinalize(ctx, input)
}), middleware.After)
})
})
})
if err != nil {
return "", time.Time{}, trace.Wrap(err)
}

// Set token expiration to 1 minute before the presigned URL expires for some cushion
// Set token expiration to 1 minute before the presigned URL expires for some cushion.
tokenExpiration := clock.Now().Add(presignedURLExpiration - 1*time.Minute)
return v1Prefix + base64.RawURLEncoding.EncodeToString([]byte(presignedURLString)), tokenExpiration, nil
return v1Prefix + base64.RawURLEncoding.EncodeToString([]byte(presignedReq.URL)), tokenExpiration, nil
}
9 changes: 4 additions & 5 deletions lib/srv/discovery/common/kubernetes.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@ import (
"strings"

"github.com/aws/aws-sdk-go-v2/aws/arn"
"github.com/aws/aws-sdk-go/aws"
"github.com/gravitational/trace"

"github.com/gravitational/teleport/api/types"
Expand All @@ -40,7 +39,7 @@ func setAWSKubeName(meta types.Metadata, firstNamePart string, extraNameParts ..
}

// NewKubeClusterFromAWSEKS creates a kube_cluster resource from an EKS cluster.
func NewKubeClusterFromAWSEKS(clusterName, clusterArn string, tags map[string]*string) (types.KubeCluster, error) {
func NewKubeClusterFromAWSEKS(clusterName, clusterArn string, tags map[string]string) (types.KubeCluster, error) {
parsedARN, err := arn.Parse(clusterArn)
if err != nil {
return nil, trace.Wrap(err)
Expand All @@ -64,7 +63,7 @@ func NewKubeClusterFromAWSEKS(clusterName, clusterArn string, tags map[string]*s
}

// labelsFromAWSKubeClusterTags creates kube cluster labels.
func labelsFromAWSKubeClusterTags(tags map[string]*string, parsedARN arn.ARN) map[string]string {
func labelsFromAWSKubeClusterTags(tags map[string]string, parsedARN arn.ARN) map[string]string {
labels := awsEKSTagsToLabels(tags)
labels[types.CloudLabel] = types.CloudAWS
labels[types.DiscoveryLabelRegion] = parsedARN.Region
Expand All @@ -74,11 +73,11 @@ func labelsFromAWSKubeClusterTags(tags map[string]*string, parsedARN arn.ARN) ma
}

// awsEKSTagsToLabels converts AWS tags to a labels map.
func awsEKSTagsToLabels(tags map[string]*string) map[string]string {
func awsEKSTagsToLabels(tags map[string]string) map[string]string {
labels := make(map[string]string)
for key, val := range tags {
if types.IsValidLabelKey(key) {
labels[key] = aws.StringValue(val)
labels[key] = val
} else {
slog.DebugContext(context.Background(), "Skipping EKS tag that is not a valid label key", "tag", key)
}
Expand Down
6 changes: 6 additions & 0 deletions lib/srv/discovery/discovery.go
Original file line number Diff line number Diff line change
Expand Up @@ -111,10 +111,16 @@ type gcpInstaller interface {
Run(ctx context.Context, req server.GCPRunRequest) error
}

type eksClientGetter func(ctx context.Context, region string, opts ...awsconfig.OptionsFn) (eksClient, error)

type eksClient interface{}

// Config provides configuration for the discovery server.
type Config struct {
// CloudClients is an interface for retrieving cloud clients.
CloudClients cloud.Clients
// GetEKSClient gets an AWS EKS client for the given region.
GetEKSClient eksClientGetter
// GetEC2Client gets an AWS EC2 client for the given region.
GetEC2Client server.EC2ClientGetter
// GetSSMClient gets an AWS SSM client for the given region.
Expand Down
Loading

0 comments on commit 8c5273e

Please sign in to comment.