-
Notifications
You must be signed in to change notification settings - Fork 661
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add STSClientGrants and STSWebIdentity credential provider (#1059)
Both provider implements a way to retrieve temporary credentials from Minio STS service - using client grants token (Only Minio) - using web identity token (Both Minio and AWS) These temporary credentials will be used to perform API operations, to be used with applications which are never using static credentials.
- Loading branch information
1 parent
3c18c1a
commit e6694bc
Showing
3 changed files
with
343 additions
and
3 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,173 @@ | ||
/* | ||
* Minio Go Library for Amazon S3 Compatible Cloud Storage | ||
* Copyright 2019 Minio, Inc. | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
|
||
package credentials | ||
|
||
import ( | ||
"encoding/xml" | ||
"errors" | ||
"fmt" | ||
"net/http" | ||
"net/url" | ||
"time" | ||
) | ||
|
||
// AssumedRoleUser - The identifiers for the temporary security credentials that | ||
// the operation returns. Please also see https://docs.aws.amazon.com/goto/WebAPI/sts-2011-06-15/AssumedRoleUser | ||
type AssumedRoleUser struct { | ||
Arn string | ||
AssumedRoleID string `xml:"AssumeRoleId"` | ||
} | ||
|
||
// AssumeRoleWithClientGrantsResponse contains the result of successful AssumeRoleWithClientGrants request. | ||
type AssumeRoleWithClientGrantsResponse struct { | ||
XMLName xml.Name `xml:"https://sts.amazonaws.com/doc/2011-06-15/ AssumeRoleWithClientGrantsResponse" json:"-"` | ||
Result ClientGrantsResult `xml:"AssumeRoleWithClientGrantsResult"` | ||
ResponseMetadata struct { | ||
RequestID string `xml:"RequestId,omitempty"` | ||
} `xml:"ResponseMetadata,omitempty"` | ||
} | ||
|
||
// ClientGrantsResult - Contains the response to a successful AssumeRoleWithClientGrants | ||
// request, including temporary credentials that can be used to make Minio API requests. | ||
type ClientGrantsResult struct { | ||
AssumedRoleUser AssumedRoleUser `xml:",omitempty"` | ||
Audience string `xml:",omitempty"` | ||
Credentials struct { | ||
AccessKey string `xml:"AccessKeyId" json:"accessKey,omitempty"` | ||
SecretKey string `xml:"SecretAccessKey" json:"secretKey,omitempty"` | ||
Expiration time.Time `xml:"Expiration" json:"expiration,omitempty"` | ||
SessionToken string `xml:"SessionToken" json:"sessionToken,omitempty"` | ||
} `xml:",omitempty"` | ||
PackedPolicySize int `xml:",omitempty"` | ||
Provider string `xml:",omitempty"` | ||
SubjectFromClientGrantsToken string `xml:",omitempty"` | ||
} | ||
|
||
// ClientGrantsToken - client grants token with expiry. | ||
type ClientGrantsToken struct { | ||
token string | ||
expiry int | ||
} | ||
|
||
// Token - access token returned after authenticating client grants. | ||
func (c *ClientGrantsToken) Token() string { | ||
return c.token | ||
} | ||
|
||
// Expiry - expiry for the access token returned after authenticating | ||
// client grants. | ||
func (c *ClientGrantsToken) Expiry() string { | ||
return fmt.Sprintf("%d", c.expiry) | ||
} | ||
|
||
// A STSClientGrants retrieves credentials from Minio service, and keeps track if | ||
// those credentials are expired. | ||
type STSClientGrants struct { | ||
Expiry | ||
|
||
// Required http Client to use when connecting to Minio STS service. | ||
Client *http.Client | ||
|
||
// Minio endpoint to fetch STS credentials. | ||
stsEndpoint string | ||
|
||
// getClientGrantsTokenExpiry function to retrieve tokens | ||
// from IDP This function should return two values one is | ||
// accessToken which is a self contained access token (JWT) | ||
// and second return value is the expiry associated with | ||
// this token. This is a customer provided function and | ||
// is mandatory. | ||
getClientGrantsTokenExpiry func() (*ClientGrantsToken, error) | ||
} | ||
|
||
// NewSTSClientGrants returns a pointer to a new | ||
// Credentials object wrapping the STSClientGrants. | ||
func NewSTSClientGrants(stsEndpoint string, getClientGrantsTokenExpiry func() (*ClientGrantsToken, error)) (*Credentials, error) { | ||
if stsEndpoint == "" { | ||
return nil, errors.New("STS endpoint cannot be empty") | ||
} | ||
if getClientGrantsTokenExpiry == nil { | ||
return nil, errors.New("Client grants access token and expiry retrieval function should be defined") | ||
} | ||
return New(&STSClientGrants{ | ||
Client: &http.Client{ | ||
Transport: http.DefaultTransport, | ||
}, | ||
stsEndpoint: stsEndpoint, | ||
getClientGrantsTokenExpiry: getClientGrantsTokenExpiry, | ||
}), nil | ||
} | ||
|
||
func getClientGrantsCredentials(clnt *http.Client, endpoint string, | ||
getClientGrantsTokenExpiry func() (*ClientGrantsToken, error)) (AssumeRoleWithClientGrantsResponse, error) { | ||
|
||
accessToken, err := getClientGrantsTokenExpiry() | ||
if err != nil { | ||
return AssumeRoleWithClientGrantsResponse{}, err | ||
} | ||
|
||
v := url.Values{} | ||
v.Set("Action", "AssumeRoleWithClientGrants") | ||
v.Set("Token", accessToken.Token()) | ||
v.Set("DurationSeconds", accessToken.Expiry()) | ||
v.Set("Version", "2011-06-15") | ||
|
||
u, err := url.Parse(endpoint) | ||
if err != nil { | ||
return AssumeRoleWithClientGrantsResponse{}, err | ||
} | ||
u.RawQuery = v.Encode() | ||
|
||
req, err := http.NewRequest("POST", u.String(), nil) | ||
if err != nil { | ||
return AssumeRoleWithClientGrantsResponse{}, err | ||
} | ||
resp, err := clnt.Do(req) | ||
if err != nil { | ||
return AssumeRoleWithClientGrantsResponse{}, err | ||
} | ||
defer resp.Body.Close() | ||
if resp.StatusCode != http.StatusOK { | ||
return AssumeRoleWithClientGrantsResponse{}, errors.New(resp.Status) | ||
} | ||
|
||
a := AssumeRoleWithClientGrantsResponse{} | ||
if err = xml.NewDecoder(resp.Body).Decode(&a); err != nil { | ||
return AssumeRoleWithClientGrantsResponse{}, err | ||
} | ||
return a, nil | ||
} | ||
|
||
// Retrieve retrieves credentials from the Minio service. | ||
// Error will be returned if the request fails. | ||
func (m *STSClientGrants) Retrieve() (Value, error) { | ||
a, err := getClientGrantsCredentials(m.Client, m.stsEndpoint, m.getClientGrantsTokenExpiry) | ||
if err != nil { | ||
return Value{}, err | ||
} | ||
|
||
// Expiry window is set to 10secs. | ||
m.SetExpiration(a.Result.Credentials.Expiration, DefaultExpiryWindow) | ||
|
||
return Value{ | ||
AccessKeyID: a.Result.Credentials.AccessKey, | ||
SecretAccessKey: a.Result.Credentials.SecretKey, | ||
SessionToken: a.Result.Credentials.SessionToken, | ||
SignerType: SignatureV4, | ||
}, nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,169 @@ | ||
/* | ||
* Minio Go Library for Amazon S3 Compatible Cloud Storage | ||
* Copyright 2019 Minio, Inc. | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
|
||
package credentials | ||
|
||
import ( | ||
"encoding/xml" | ||
"errors" | ||
"fmt" | ||
"net/http" | ||
"net/url" | ||
"time" | ||
) | ||
|
||
// AssumeRoleWithWebIdentityResponse contains the result of successful AssumeRoleWithWebIdentity request. | ||
type AssumeRoleWithWebIdentityResponse struct { | ||
XMLName xml.Name `xml:"https://sts.amazonaws.com/doc/2011-06-15/ AssumeRoleWithWebIdentityResponse" json:"-"` | ||
Result WebIdentityResult `xml:"AssumeRoleWithWebIdentityResult"` | ||
ResponseMetadata struct { | ||
RequestID string `xml:"RequestId,omitempty"` | ||
} `xml:"ResponseMetadata,omitempty"` | ||
} | ||
|
||
// WebIdentityResult - Contains the response to a successful AssumeRoleWithWebIdentity | ||
// request, including temporary credentials that can be used to make Minio API requests. | ||
type WebIdentityResult struct { | ||
AssumedRoleUser AssumedRoleUser `xml:",omitempty"` | ||
Audience string `xml:",omitempty"` | ||
Credentials struct { | ||
AccessKey string `xml:"AccessKeyId" json:"accessKey,omitempty"` | ||
SecretKey string `xml:"SecretAccessKey" json:"secretKey,omitempty"` | ||
Expiration time.Time `xml:"Expiration" json:"expiration,omitempty"` | ||
SessionToken string `xml:"SessionToken" json:"sessionToken,omitempty"` | ||
} `xml:",omitempty"` | ||
PackedPolicySize int `xml:",omitempty"` | ||
Provider string `xml:",omitempty"` | ||
SubjectFromWebIdentityToken string `xml:",omitempty"` | ||
} | ||
|
||
// WebIdentityToken - web identity token with expiry. | ||
type WebIdentityToken struct { | ||
token string | ||
expiry int | ||
} | ||
|
||
// Token - access token returned after authenticating web identity. | ||
func (c *WebIdentityToken) Token() string { | ||
return c.token | ||
} | ||
|
||
// Expiry - expiry for the access token returned after authenticating | ||
// web identity. | ||
func (c *WebIdentityToken) Expiry() string { | ||
return fmt.Sprintf("%d", c.expiry) | ||
} | ||
|
||
// A STSWebIdentity retrieves credentials from Minio service, and keeps track if | ||
// those credentials are expired. | ||
type STSWebIdentity struct { | ||
Expiry | ||
|
||
// Required http Client to use when connecting to Minio STS service. | ||
Client *http.Client | ||
|
||
// Minio endpoint to fetch STS credentials. | ||
stsEndpoint string | ||
|
||
// getWebIDTokenExpiry function which returns ID tokens | ||
// from IDP. This function should return two values one | ||
// is ID token which is a self contained ID token (JWT) | ||
// and second return value is the expiry associated with | ||
// this token. | ||
// This is a customer provided function and is mandatory. | ||
getWebIDTokenExpiry func() (*WebIdentityToken, error) | ||
} | ||
|
||
// NewSTSWebIdentity returns a pointer to a new | ||
// Credentials object wrapping the STSWebIdentity. | ||
func NewSTSWebIdentity(stsEndpoint string, getWebIDTokenExpiry func() (*WebIdentityToken, error)) (*Credentials, error) { | ||
if stsEndpoint == "" { | ||
return nil, errors.New("STS endpoint cannot be empty") | ||
} | ||
if getWebIDTokenExpiry == nil { | ||
return nil, errors.New("Web ID token and expiry retrieval function should be defined") | ||
} | ||
return New(&STSWebIdentity{ | ||
Client: &http.Client{ | ||
Transport: http.DefaultTransport, | ||
}, | ||
stsEndpoint: stsEndpoint, | ||
getWebIDTokenExpiry: getWebIDTokenExpiry, | ||
}), nil | ||
} | ||
|
||
func getWebIdentityCredentials(clnt *http.Client, endpoint string, | ||
getWebIDTokenExpiry func() (*WebIdentityToken, error)) (AssumeRoleWithWebIdentityResponse, error) { | ||
idToken, err := getWebIDTokenExpiry() | ||
if err != nil { | ||
return AssumeRoleWithWebIdentityResponse{}, err | ||
} | ||
|
||
v := url.Values{} | ||
v.Set("Action", "AssumeRoleWithWebIdentity") | ||
v.Set("WebIdentityToken", idToken.Token()) | ||
v.Set("DurationSeconds", idToken.Expiry()) | ||
v.Set("Version", "2011-06-15") | ||
|
||
u, err := url.Parse(endpoint) | ||
if err != nil { | ||
return AssumeRoleWithWebIdentityResponse{}, err | ||
} | ||
|
||
u.RawQuery = v.Encode() | ||
|
||
req, err := http.NewRequest("POST", u.String(), nil) | ||
if err != nil { | ||
return AssumeRoleWithWebIdentityResponse{}, err | ||
} | ||
|
||
resp, err := clnt.Do(req) | ||
if err != nil { | ||
return AssumeRoleWithWebIdentityResponse{}, err | ||
} | ||
|
||
defer resp.Body.Close() | ||
if resp.StatusCode != http.StatusOK { | ||
return AssumeRoleWithWebIdentityResponse{}, errors.New(resp.Status) | ||
} | ||
|
||
a := AssumeRoleWithWebIdentityResponse{} | ||
if err = xml.NewDecoder(resp.Body).Decode(&a); err != nil { | ||
return AssumeRoleWithWebIdentityResponse{}, err | ||
} | ||
|
||
return a, nil | ||
} | ||
|
||
// Retrieve retrieves credentials from the Minio service. | ||
// Error will be returned if the request fails. | ||
func (m *STSWebIdentity) Retrieve() (Value, error) { | ||
a, err := getWebIdentityCredentials(m.Client, m.stsEndpoint, m.getWebIDTokenExpiry) | ||
if err != nil { | ||
return Value{}, err | ||
} | ||
|
||
// Expiry window is set to 10secs. | ||
m.SetExpiration(a.Result.Credentials.Expiration, DefaultExpiryWindow) | ||
|
||
return Value{ | ||
AccessKeyID: a.Result.Credentials.AccessKey, | ||
SecretAccessKey: a.Result.Credentials.SecretKey, | ||
SessionToken: a.Result.Credentials.SessionToken, | ||
SignerType: SignatureV4, | ||
}, nil | ||
} |