-
Notifications
You must be signed in to change notification settings - Fork 378
AWS IAM: lakefs IDP interface #8994
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 12 commits
a9e4edd
4dad5ff
125fecf
7859d81
f477495
ef90ede
9b20fb1
c3080fd
1cb02ab
ab76f8d
2d1405d
fdd6822
e6e7d93
d541159
6fff5d4
6dc1fcf
64afc2e
dd6dcb8
588eed4
e148d23
ecbe333
d53953e
4a28fb6
0049782
02ed282
3774f6c
9975fd0
0358833
878b4d7
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
@@ -0,0 +1,196 @@ | ||||||
package awsiam | ||||||
|
||||||
import ( | ||||||
"context" | ||||||
"errors" | ||||||
"fmt" | ||||||
"net/http" | ||||||
"net/url" | ||||||
"strings" | ||||||
"time" | ||||||
|
||||||
"github.com/aws/aws-sdk-go-v2/aws" | ||||||
"github.com/aws/aws-sdk-go-v2/config" | ||||||
"github.com/aws/aws-sdk-go-v2/service/sts" | ||||||
"github.com/aws/smithy-go/middleware" | ||||||
smithyhttp "github.com/aws/smithy-go/transport/http" | ||||||
) | ||||||
|
||||||
const ( | ||||||
authVersion = "2011-06-15" | ||||||
authMethod = http.MethodPost | ||||||
authAction = "GetCallerIdentity" | ||||||
authAlgorithm = "AWS4-HMAC-SHA256" | ||||||
stsGlobalEndpoint = "sts.amazonaws.com" | ||||||
authActionKey = "Action" | ||||||
authVersionKey = "Version" | ||||||
authAlgorithmKey = "X-Amz-Algorithm" | ||||||
//nolint:gosec | ||||||
authCredentialKey = "X-Amz-Credential" | ||||||
authDateKey = "X-Amz-Date" | ||||||
authExpiresKey = "X-Amz-Expires" | ||||||
//nolint:gosec | ||||||
authSecurityTokenKey = "X-Amz-Security-Token" | ||||||
authSignedHeadersKey = "X-Amz-SignedHeaders" | ||||||
authSignatureKey = "X-Amz-Signature" | ||||||
datetimeFormat = "20060102T150405Z" | ||||||
credentialTimeFormat = "20060102" | ||||||
defaultSTSLoginExpire = 15 * time.Minute | ||||||
Isan-Rivkin marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||
) | ||||||
|
||||||
var ErrAWSCredentialsExpired = errors.New("AWS credentials expired") | ||||||
Isan-Rivkin marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||
var ErrRetrievingToken = errors.New("failed to retrieve token") | ||||||
|
||||||
type AWSIdentityTokenInfo struct { | ||||||
Method string `json:"method"` | ||||||
Host string `json:"host"` | ||||||
Region string `json:"region"` | ||||||
Action string `json:"action"` | ||||||
Date string `json:"date"` | ||||||
ExpirationDuration string `json:"expiration_duration"` | ||||||
AccessKeyID string `json:"access_key_id"` | ||||||
Signature string `json:"signature"` | ||||||
SignedHeaders []string `json:"signed_headers"` | ||||||
Version string `json:"version"` | ||||||
Algorithm string `json:"algorithm"` | ||||||
SecurityToken string `json:"security_token"` | ||||||
} | ||||||
type AWSProvider struct { | ||||||
Params IAMAuthParams | ||||||
} | ||||||
type IAMAuthParams struct { | ||||||
ProviderType string | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. unused remove |
||||||
TokenRequestHeaders map[string]string | ||||||
URLPresignTTL time.Duration | ||||||
TokenTTL time.Duration | ||||||
RefreshInterval time.Duration | ||||||
} | ||||||
|
||||||
func NewAWSProvider(params IAMAuthParams) *AWSProvider { | ||||||
return &AWSProvider{ | ||||||
Params: params, | ||||||
} | ||||||
} | ||||||
|
||||||
func (p *AWSProvider) NewRequest() (*AWSIdentityTokenInfo, error) { | ||||||
ctx := context.TODO() | ||||||
Isan-Rivkin marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||
cfg, err := GetConfig(ctx) | ||||||
if err != nil { | ||||||
return &AWSIdentityTokenInfo{}, err | ||||||
Isan-Rivkin marked this conversation as resolved.
Show resolved
Hide resolved
Isan-Rivkin marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||
} | ||||||
creds, err := GetCreds(ctx, cfg) | ||||||
if err != nil { | ||||||
return &AWSIdentityTokenInfo{}, err | ||||||
Isan-Rivkin marked this conversation as resolved.
Show resolved
Hide resolved
Isan-Rivkin marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||
} | ||||||
url, err := GetPresignedURL(ctx, &p.Params, cfg, creds) | ||||||
if err != nil { | ||||||
return &AWSIdentityTokenInfo{}, err | ||||||
} | ||||||
tokenInfo, err := NewIdentityTokenInfo(creds, url) | ||||||
if err != nil { | ||||||
return &AWSIdentityTokenInfo{}, err | ||||||
Isan-Rivkin marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||
} | ||||||
return tokenInfo, nil | ||||||
// tokenTTL := int(p.Params.TokenTTL.Seconds()) | ||||||
// externalLoginInfo := apigen.ExternalLoginInformation{ | ||||||
// IdentityRequest: map[string]interface{}{ | ||||||
// "identity_token": identityToken, | ||||||
// }, | ||||||
// TokenExpirationDuration: &tokenTTL, | ||||||
// } | ||||||
// res, err := p.Client.ExternalPrincipalLoginWithResponse(ctx, apigen.ExternalPrincipalLoginJSONRequestBody(externalLoginInfo)) | ||||||
// if err != nil { | ||||||
// return LoginResponse{}, err | ||||||
// } | ||||||
// err = helpers.ResponseAsError(res) | ||||||
// if err != nil { | ||||||
// return LoginResponse{}, err | ||||||
// } | ||||||
// return LoginResponse{Token: res.JSON200}, nil | ||||||
} | ||||||
|
||||||
func NewIdentityTokenInfo(creds *aws.Credentials, presignedURL string) (*AWSIdentityTokenInfo, error) { | ||||||
parsedURL, err := url.Parse(presignedURL) | ||||||
if err != nil { | ||||||
return nil, err | ||||||
} | ||||||
|
||||||
queryParams := parsedURL.Query() | ||||||
credentials := queryParams.Get(authCredentialKey) | ||||||
splitedCreds := strings.Split(credentials, "/") | ||||||
calculatedRegion := splitedCreds[2] | ||||||
Isan-Rivkin marked this conversation as resolved.
Show resolved
Hide resolved
Isan-Rivkin marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||
identityTokenInfo := AWSIdentityTokenInfo{ | ||||||
Isan-Rivkin marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||
Method: "POST", | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. use AuthMethod |
||||||
Host: parsedURL.Host, | ||||||
Region: calculatedRegion, | ||||||
Action: authAction, | ||||||
Date: queryParams.Get(authDateKey), | ||||||
ExpirationDuration: queryParams.Get(authExpiresKey), | ||||||
AccessKeyID: creds.AccessKeyID, | ||||||
Signature: queryParams.Get(authSignatureKey), | ||||||
SignedHeaders: strings.Split(queryParams.Get(authSignedHeadersKey), ";"), | ||||||
Version: queryParams.Get(authVersionKey), | ||||||
Algorithm: queryParams.Get(authAlgorithmKey), | ||||||
SecurityToken: queryParams.Get(authSecurityTokenKey), | ||||||
} | ||||||
return &identityTokenInfo, nil | ||||||
// marshaledIdentityTokenInfo, _ := json.Marshal(identityTokenInfo) | ||||||
// encodedIdentityTokenInfo := base64.StdEncoding.EncodeToString(marshaledIdentityTokenInfo) | ||||||
// return &identityTokenInfo, encodedIdentityTokenInfo, nil | ||||||
} | ||||||
|
||||||
func GetPresignedURL(ctx context.Context, params *IAMAuthParams, cfg *aws.Config, creds *aws.Credentials) (string, error) { | ||||||
stsClient := sts.NewFromConfig(*cfg) | ||||||
stsPresignClient := sts.NewPresignClient(stsClient, func(o *sts.PresignOptions) { | ||||||
o.ClientOptions = append(o.ClientOptions, func(opts *sts.Options) { | ||||||
opts.ClientLogMode = aws.LogSigning | ||||||
}) | ||||||
}) | ||||||
|
||||||
presign, err := stsPresignClient.PresignGetCallerIdentity(context.Background(), &sts.GetCallerIdentityInput{}, | ||||||
sts.WithPresignClientFromClientOptions(sts.WithAPIOptions(setHTTPHeaders(params.TokenRequestHeaders, params.URLPresignTTL))), | ||||||
) | ||||||
if err != nil { | ||||||
return "", err | ||||||
} | ||||||
return presign.URL, err | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
} | ||||||
|
||||||
func GetCreds(ctx context.Context, cfg *aws.Config) (*aws.Credentials, error) { | ||||||
creds, err := cfg.Credentials.Retrieve(ctx) | ||||||
if err != nil { | ||||||
return nil, err | ||||||
} | ||||||
if creds.Expired() { | ||||||
Isan-Rivkin marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||
return nil, ErrAWSCredentialsExpired | ||||||
} | ||||||
return &creds, err | ||||||
} | ||||||
func GetConfig(ctx context.Context) (*aws.Config, error) { | ||||||
cfg, err := config.LoadDefaultConfig(ctx) | ||||||
if err != nil { | ||||||
return nil, err | ||||||
} | ||||||
return &cfg, err | ||||||
} | ||||||
|
||||||
func setHTTPHeaders(requestHeaders map[string]string, ttl time.Duration) func(*middleware.Stack) error { | ||||||
return func(stack *middleware.Stack) error { | ||||||
return stack.Build.Add(middleware.BuildMiddlewareFunc("AddHeaders", func( | ||||||
ctx context.Context, in middleware.BuildInput, next middleware.BuildHandler, | ||||||
) ( | ||||||
middleware.BuildOutput, middleware.Metadata, error, | ||||||
) { | ||||||
if req, ok := in.Request.(*smithyhttp.Request); ok { | ||||||
req.Method = "POST" | ||||||
for header, value := range requestHeaders { | ||||||
req.Header.Add(header, value) | ||||||
} | ||||||
queryParams := req.URL.Query() | ||||||
queryParams.Set(authExpiresKey, fmt.Sprintf("%d", int(ttl.Seconds()))) | ||||||
req.URL.RawQuery = queryParams.Encode() | ||||||
} | ||||||
return next.HandleBuild(ctx, in) | ||||||
}), middleware.Before) | ||||||
} | ||||||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
package externalidp | ||
|
||
import "github.com/treeverse/lakefs/pkg/authentication/externalidp/awsiam" | ||
|
||
type Provider interface { | ||
NewRequest() *TokenInfo | ||
} | ||
type TokenInfo struct { | ||
AWSInfo *awsiam.AWSIdentityTokenInfo | ||
} |
Uh oh!
There was an error while loading. Please reload this page.