2018-10-09 17:00:01 -04:00
/ *
2020-06-05 01:07:55 -04:00
* MinIO Cloud Storage , ( C ) 2018 - 2020 MinIO , Inc .
2018-10-09 17:00:01 -04:00
*
* 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 cmd
import (
2019-06-20 18:28:33 -04:00
"bytes"
2019-02-27 20:46:55 -05:00
"context"
2019-06-20 18:28:33 -04:00
"encoding/base64"
2019-02-05 18:47:11 -05:00
"fmt"
2018-10-09 17:00:01 -04:00
"net/http"
2020-04-28 15:49:56 -04:00
"strings"
2018-10-09 17:00:01 -04:00
"github.com/gorilla/mux"
2019-10-23 01:59:13 -04:00
"github.com/minio/minio/cmd/config/identity/openid"
2019-07-03 01:34:32 -04:00
xhttp "github.com/minio/minio/cmd/http"
2018-10-09 17:00:01 -04:00
"github.com/minio/minio/cmd/logger"
"github.com/minio/minio/pkg/auth"
2019-06-20 18:28:33 -04:00
iampolicy "github.com/minio/minio/pkg/iam/policy"
2019-04-23 18:55:41 -04:00
"github.com/minio/minio/pkg/wildcard"
2018-10-09 17:00:01 -04:00
)
const (
// STS API version.
2019-11-29 08:27:54 -05:00
stsAPIVersion = "2011-06-15"
stsVersion = "Version"
stsAction = "Action"
stsPolicy = "Policy"
stsToken = "Token"
stsWebIdentityToken = "WebIdentityToken"
stsDurationSeconds = "DurationSeconds"
stsLDAPUsername = "LDAPUsername"
stsLDAPPassword = "LDAPPassword"
2019-02-05 18:47:11 -05:00
// STS API action constants
clientGrants = "AssumeRoleWithClientGrants"
webIdentity = "AssumeRoleWithWebIdentity"
2019-09-09 19:12:29 -04:00
ldapIdentity = "AssumeRoleWithLDAPIdentity"
2019-02-27 20:46:55 -05:00
assumeRole = "AssumeRole"
2019-08-05 13:06:40 -04:00
stsRequestBodyLimit = 10 * ( 1 << 20 ) // 10 MiB
2019-09-09 19:12:29 -04:00
2019-11-29 08:27:54 -05:00
// JWT claim keys
expClaim = "exp"
subClaim = "sub"
2020-03-17 13:36:13 -04:00
// JWT claim to check the parent user
parentClaim = "parent"
2019-09-09 19:12:29 -04:00
// LDAP claim keys
2020-09-01 02:56:22 -04:00
ldapUser = "ldapUser"
2018-10-09 17:00:01 -04:00
)
// stsAPIHandlers implements and provides http handlers for AWS STS API.
type stsAPIHandlers struct { }
// registerSTSRouter - registers AWS STS compatible APIs.
func registerSTSRouter ( router * mux . Router ) {
// Initialize STS.
sts := & stsAPIHandlers { }
// STS Router
2019-08-06 15:08:58 -04:00
stsRouter := router . NewRoute ( ) . PathPrefix ( SlashSeparator ) . Subrouter ( )
2018-10-09 17:00:01 -04:00
2019-02-27 20:46:55 -05:00
// Assume roles with no JWT, handles AssumeRole.
2019-07-03 01:34:32 -04:00
stsRouter . Methods ( http . MethodPost ) . MatcherFunc ( func ( r * http . Request , rm * mux . RouteMatch ) bool {
ctypeOk := wildcard . MatchSimple ( "application/x-www-form-urlencoded*" , r . Header . Get ( xhttp . ContentType ) )
authOk := wildcard . MatchSimple ( signV4Algorithm + "*" , r . Header . Get ( xhttp . Authorization ) )
2019-04-23 18:55:41 -04:00
noQueries := len ( r . URL . Query ( ) ) == 0
return ctypeOk && authOk && noQueries
} ) . HandlerFunc ( httpTraceAll ( sts . AssumeRole ) )
2019-02-27 20:46:55 -05:00
2019-02-05 18:47:11 -05:00
// Assume roles with JWT handler, handles both ClientGrants and WebIdentity.
2019-11-29 08:27:54 -05:00
stsRouter . Methods ( http . MethodPost ) . MatcherFunc ( func ( r * http . Request , rm * mux . RouteMatch ) bool {
2019-07-03 01:34:32 -04:00
ctypeOk := wildcard . MatchSimple ( "application/x-www-form-urlencoded*" , r . Header . Get ( xhttp . ContentType ) )
2019-04-23 18:55:41 -04:00
noQueries := len ( r . URL . Query ( ) ) == 0
return ctypeOk && noQueries
2020-09-12 02:02:32 -04:00
} ) . HandlerFunc ( httpTraceAll ( sts . AssumeRoleWithSSO ) )
2019-02-05 18:47:11 -05:00
2018-10-09 17:00:01 -04:00
// AssumeRoleWithClientGrants
2019-11-29 08:27:54 -05:00
stsRouter . Methods ( http . MethodPost ) . HandlerFunc ( httpTraceAll ( sts . AssumeRoleWithClientGrants ) ) .
Queries ( stsAction , clientGrants ) .
Queries ( stsVersion , stsAPIVersion ) .
Queries ( stsToken , "{Token:.*}" )
2018-10-09 17:00:01 -04:00
2019-01-04 16:48:12 -05:00
// AssumeRoleWithWebIdentity
2019-11-29 08:27:54 -05:00
stsRouter . Methods ( http . MethodPost ) . HandlerFunc ( httpTraceAll ( sts . AssumeRoleWithWebIdentity ) ) .
Queries ( stsAction , webIdentity ) .
Queries ( stsVersion , stsAPIVersion ) .
Queries ( stsWebIdentityToken , "{Token:.*}" )
2018-10-09 17:00:01 -04:00
2019-09-09 19:12:29 -04:00
// AssumeRoleWithLDAPIdentity
2019-11-29 08:27:54 -05:00
stsRouter . Methods ( http . MethodPost ) . HandlerFunc ( httpTraceAll ( sts . AssumeRoleWithLDAPIdentity ) ) .
Queries ( stsAction , ldapIdentity ) .
Queries ( stsVersion , stsAPIVersion ) .
Queries ( stsLDAPUsername , "{LDAPUsername:.*}" ) .
Queries ( stsLDAPPassword , "{LDAPPassword:.*}" )
2018-10-09 17:00:01 -04:00
}
2020-05-21 12:09:18 -04:00
func checkAssumeRoleAuth ( ctx context . Context , r * http . Request ) ( user auth . Credentials , isErrCodeSTS bool , stsErr STSErrorCode ) {
2019-02-27 20:46:55 -05:00
switch getRequestAuthType ( r ) {
default :
2020-05-21 12:09:18 -04:00
return user , true , ErrSTSAccessDenied
2019-02-27 20:46:55 -05:00
case authTypeSigned :
2019-10-23 01:59:13 -04:00
s3Err := isReqAuthenticated ( ctx , r , globalServerRegion , serviceSTS )
2020-05-21 12:09:18 -04:00
if APIErrorCode ( s3Err ) != ErrNone {
return user , false , STSErrorCode ( s3Err )
2019-02-27 20:46:55 -05:00
}
var owner bool
2019-10-23 01:59:13 -04:00
user , owner , s3Err = getReqAccessKeyV4 ( r , globalServerRegion , serviceSTS )
2020-05-21 12:09:18 -04:00
if APIErrorCode ( s3Err ) != ErrNone {
return user , false , STSErrorCode ( s3Err )
2019-02-27 20:46:55 -05:00
}
// Root credentials are not allowed to use STS API
if owner {
2020-05-21 12:09:18 -04:00
return user , true , ErrSTSAccessDenied
2019-02-27 20:46:55 -05:00
}
}
// Session tokens are not allowed in STS AssumeRole requests.
if getSessionToken ( r ) != "" {
2020-05-21 12:09:18 -04:00
return user , true , ErrSTSAccessDenied
2019-02-27 20:46:55 -05:00
}
2020-04-28 15:49:56 -04:00
// Temporary credentials or Service accounts cannot generate further temporary credentials.
if user . IsTemp ( ) || user . IsServiceAccount ( ) {
2020-05-21 12:09:18 -04:00
return user , true , ErrSTSAccessDenied
2020-04-28 15:49:56 -04:00
}
2020-05-21 12:09:18 -04:00
return user , true , ErrSTSNone
2019-02-27 20:46:55 -05:00
}
// AssumeRole - implementation of AWS STS API AssumeRole to get temporary
// credentials for regular users on Minio.
// https://docs.aws.amazon.com/STS/latest/APIReference/API_AssumeRole.html
func ( sts * stsAPIHandlers ) AssumeRole ( w http . ResponseWriter , r * http . Request ) {
ctx := newContext ( r , w , "AssumeRole" )
2020-05-21 12:09:18 -04:00
user , isErrCodeSTS , stsErr := checkAssumeRoleAuth ( ctx , r )
2019-02-27 20:46:55 -05:00
if stsErr != ErrSTSNone {
2020-05-21 12:09:18 -04:00
writeSTSErrorResponse ( ctx , w , isErrCodeSTS , stsErr , nil )
2019-02-27 20:46:55 -05:00
return
}
if err := r . ParseForm ( ) ; err != nil {
2020-05-21 12:09:18 -04:00
writeSTSErrorResponse ( ctx , w , true , ErrSTSInvalidParameterValue , err )
2019-02-27 20:46:55 -05:00
return
}
2019-11-29 08:27:54 -05:00
if r . Form . Get ( stsVersion ) != stsAPIVersion {
2020-05-21 12:09:18 -04:00
writeSTSErrorResponse ( ctx , w , true , ErrSTSMissingParameter , fmt . Errorf ( "Invalid STS API version %s, expecting %s" , r . Form . Get ( stsVersion ) , stsAPIVersion ) )
2019-02-27 20:46:55 -05:00
return
}
2019-11-29 08:27:54 -05:00
action := r . Form . Get ( stsAction )
2019-02-27 20:46:55 -05:00
switch action {
case assumeRole :
default :
2020-05-21 12:09:18 -04:00
writeSTSErrorResponse ( ctx , w , true , ErrSTSInvalidParameterValue , fmt . Errorf ( "Unsupported action %s" , action ) )
2019-02-27 20:46:55 -05:00
return
}
ctx = newContext ( r , w , action )
2021-01-26 16:21:51 -05:00
defer logger . AuditLog ( ctx , w , r , nil )
2019-02-27 20:46:55 -05:00
2019-11-29 08:27:54 -05:00
sessionPolicyStr := r . Form . Get ( stsPolicy )
2019-06-20 18:28:33 -04:00
// https://docs.aws.amazon.com/STS/latest/APIReference/API_AssumeRole.html
// The plain text that you use for both inline and managed session
// policies shouldn't exceed 2048 characters.
if len ( sessionPolicyStr ) > 2048 {
2020-05-21 12:09:18 -04:00
writeSTSErrorResponse ( ctx , w , true , ErrSTSInvalidParameterValue , fmt . Errorf ( "Session policy shouldn't exceed 2048 characters" ) )
2019-06-20 18:28:33 -04:00
return
}
if len ( sessionPolicyStr ) > 0 {
sessionPolicy , err := iampolicy . ParseConfig ( bytes . NewReader ( [ ] byte ( sessionPolicyStr ) ) )
if err != nil {
2020-05-21 12:09:18 -04:00
writeSTSErrorResponse ( ctx , w , true , ErrSTSInvalidParameterValue , err )
2019-06-20 18:28:33 -04:00
return
}
// Version in policy must not be empty
if sessionPolicy . Version == "" {
2020-05-21 12:09:18 -04:00
writeSTSErrorResponse ( ctx , w , true , ErrSTSInvalidParameterValue , fmt . Errorf ( "Version cannot be empty expecting '2012-10-17'" ) )
2019-06-20 18:28:33 -04:00
return
}
}
2019-02-27 20:46:55 -05:00
var err error
m := make ( map [ string ] interface { } )
2019-11-29 08:27:54 -05:00
m [ expClaim ] , err = openid . GetDefaultExpiration ( r . Form . Get ( stsDurationSeconds ) )
2019-02-27 20:46:55 -05:00
if err != nil {
2020-05-21 12:09:18 -04:00
writeSTSErrorResponse ( ctx , w , true , ErrSTSInvalidParameterValue , err )
2019-02-27 20:46:55 -05:00
return
}
2019-08-02 17:25:00 -04:00
policies , err := globalIAMSys . PolicyDBGet ( user . AccessKey , false )
2019-02-27 20:46:55 -05:00
if err != nil {
2020-05-21 12:09:18 -04:00
writeSTSErrorResponse ( ctx , w , true , ErrSTSInvalidParameterValue , err )
2019-02-27 20:46:55 -05:00
return
}
2020-04-28 15:49:56 -04:00
policyName := strings . Join ( policies , "," )
2019-08-02 17:25:00 -04:00
2019-02-27 20:46:55 -05:00
// This policy is the policy associated with the user
// requesting for temporary credentials. The temporary
// credentials will inherit the same policy requirements.
2020-03-23 17:17:18 -04:00
m [ iamPolicyClaimNameOpenID ( ) ] = policyName
2019-06-20 18:28:33 -04:00
if len ( sessionPolicyStr ) > 0 {
m [ iampolicy . SessionPolicyName ] = base64 . StdEncoding . EncodeToString ( [ ] byte ( sessionPolicyStr ) )
}
2019-02-27 20:46:55 -05:00
2019-10-23 01:59:13 -04:00
secret := globalActiveCred . SecretKey
2019-02-27 20:46:55 -05:00
cred , err := auth . GetNewCredentialsWithMetadata ( m , secret )
if err != nil {
2020-05-21 12:09:18 -04:00
writeSTSErrorResponse ( ctx , w , true , ErrSTSInternalError , err )
2019-02-27 20:46:55 -05:00
return
}
2020-04-28 15:49:56 -04:00
// Set the parent of the temporary access key, this is useful
// in obtaining service accounts by this cred.
cred . ParentUser = user . AccessKey
2019-02-27 20:46:55 -05:00
// Set the newly generated credentials.
if err = globalIAMSys . SetTempUser ( cred . AccessKey , cred , policyName ) ; err != nil {
2020-05-21 12:09:18 -04:00
writeSTSErrorResponse ( ctx , w , true , ErrSTSInternalError , err )
2019-02-27 20:46:55 -05:00
return
}
2019-04-09 14:39:42 -04:00
// Notify all other MinIO peers to reload temp users
2019-06-06 20:46:22 -04:00
for _ , nerr := range globalNotificationSys . LoadUser ( cred . AccessKey , true ) {
2019-02-27 20:46:55 -05:00
if nerr . Err != nil {
logger . GetReqInfo ( ctx ) . SetTags ( "peerAddress" , nerr . Host . String ( ) )
logger . LogIf ( ctx , nerr . Err )
}
}
assumeRoleResponse := & AssumeRoleResponse {
Result : AssumeRoleResult {
Credentials : cred ,
} ,
}
2019-07-03 01:34:32 -04:00
assumeRoleResponse . ResponseMetadata . RequestID = w . Header ( ) . Get ( xhttp . AmzRequestID )
2019-02-27 20:46:55 -05:00
writeSuccessResponseXML ( w , encodeResponse ( assumeRoleResponse ) )
}
2020-09-12 02:02:32 -04:00
func ( sts * stsAPIHandlers ) AssumeRoleWithSSO ( w http . ResponseWriter , r * http . Request ) {
ctx := newContext ( r , w , "AssumeRoleSSOCommon" )
2019-01-04 16:48:12 -05:00
2019-02-05 18:47:11 -05:00
// Parse the incoming form data.
if err := r . ParseForm ( ) ; err != nil {
2020-05-21 12:09:18 -04:00
writeSTSErrorResponse ( ctx , w , true , ErrSTSInvalidParameterValue , err )
2019-01-04 16:48:12 -05:00
return
}
2019-11-29 08:27:54 -05:00
if r . Form . Get ( stsVersion ) != stsAPIVersion {
2020-05-21 12:09:18 -04:00
writeSTSErrorResponse ( ctx , w , true , ErrSTSMissingParameter , fmt . Errorf ( "Invalid STS API version %s, expecting %s" , r . Form . Get ( "Version" ) , stsAPIVersion ) )
2019-01-04 16:48:12 -05:00
return
}
2019-11-29 08:27:54 -05:00
action := r . Form . Get ( stsAction )
2019-02-05 18:47:11 -05:00
switch action {
2020-09-12 02:02:32 -04:00
case ldapIdentity :
sts . AssumeRoleWithLDAPIdentity ( w , r )
return
2019-02-05 18:47:11 -05:00
case clientGrants , webIdentity :
default :
2020-05-21 12:09:18 -04:00
writeSTSErrorResponse ( ctx , w , true , ErrSTSInvalidParameterValue , fmt . Errorf ( "Unsupported action %s" , action ) )
2019-01-04 16:48:12 -05:00
return
}
2019-02-05 18:47:11 -05:00
ctx = newContext ( r , w , action )
2021-01-26 16:21:51 -05:00
defer logger . AuditLog ( ctx , w , r , nil )
2018-11-21 23:03:24 -05:00
2019-10-04 13:35:33 -04:00
if globalOpenIDValidators == nil {
2020-05-21 12:09:18 -04:00
writeSTSErrorResponse ( ctx , w , true , ErrSTSNotInitialized , errServerNotInitialized )
2018-10-09 17:00:01 -04:00
return
}
2019-10-04 13:35:33 -04:00
v , err := globalOpenIDValidators . Get ( "jwt" )
2018-10-09 17:00:01 -04:00
if err != nil {
2020-05-21 12:09:18 -04:00
writeSTSErrorResponse ( ctx , w , true , ErrSTSInvalidParameterValue , err )
2018-10-09 17:00:01 -04:00
return
}
2019-11-29 08:27:54 -05:00
token := r . Form . Get ( stsToken )
2019-02-05 18:47:11 -05:00
if token == "" {
2019-11-29 08:27:54 -05:00
token = r . Form . Get ( stsWebIdentityToken )
2019-02-05 18:47:11 -05:00
}
2019-11-29 08:27:54 -05:00
m , err := v . Validate ( token , r . Form . Get ( stsDurationSeconds ) )
2018-10-09 17:00:01 -04:00
if err != nil {
switch err {
2019-10-01 18:07:20 -04:00
case openid . ErrTokenExpired :
2019-02-05 18:47:11 -05:00
switch action {
case clientGrants :
2020-05-21 12:09:18 -04:00
writeSTSErrorResponse ( ctx , w , true , ErrSTSClientGrantsExpiredToken , err )
2019-02-05 18:47:11 -05:00
case webIdentity :
2020-05-21 12:09:18 -04:00
writeSTSErrorResponse ( ctx , w , true , ErrSTSWebIdentityExpiredToken , err )
2019-02-05 18:47:11 -05:00
}
return
2019-11-29 08:27:54 -05:00
case auth . ErrInvalidDuration :
2020-05-21 12:09:18 -04:00
writeSTSErrorResponse ( ctx , w , true , ErrSTSInvalidParameterValue , err )
2019-02-05 18:47:11 -05:00
return
2018-10-09 17:00:01 -04:00
}
2020-05-21 12:09:18 -04:00
writeSTSErrorResponse ( ctx , w , true , ErrSTSInvalidParameterValue , err )
2018-10-09 17:00:01 -04:00
return
}
2020-07-19 18:34:01 -04:00
// JWT has requested a custom claim with policy value set.
// This is a MinIO STS API specific value, this value should
// be set and configured on your identity provider as part of
// JWT custom claims.
var policyName string
policySet , ok := iampolicy . GetPoliciesFromClaims ( m , iamPolicyClaimNameOpenID ( ) )
if ok {
2020-11-08 00:03:06 -05:00
policyName = globalIAMSys . CurrentPolicies ( strings . Join ( policySet . ToSlice ( ) , "," ) )
2020-07-19 18:34:01 -04:00
}
2020-07-28 14:47:57 -04:00
if policyName == "" && globalPolicyOPA == nil {
2020-11-08 00:03:06 -05:00
writeSTSErrorResponse ( ctx , w , true , ErrSTSInvalidParameterValue ,
fmt . Errorf ( "%s claim missing from the JWT token, credentials will not be generated" , iamPolicyClaimNameOpenID ( ) ) )
2020-07-19 18:34:01 -04:00
return
}
m [ iamPolicyClaimNameOpenID ( ) ] = policyName
2019-11-29 08:27:54 -05:00
sessionPolicyStr := r . Form . Get ( stsPolicy )
2019-06-20 18:28:33 -04:00
// https://docs.aws.amazon.com/STS/latest/APIReference/API_AssumeRoleWithWebIdentity.html
// The plain text that you use for both inline and managed session
// policies shouldn't exceed 2048 characters.
if len ( sessionPolicyStr ) > 2048 {
2020-05-21 12:09:18 -04:00
writeSTSErrorResponse ( ctx , w , true , ErrSTSInvalidParameterValue , fmt . Errorf ( "Session policy should not exceed 2048 characters" ) )
2019-06-20 18:28:33 -04:00
return
}
if len ( sessionPolicyStr ) > 0 {
sessionPolicy , err := iampolicy . ParseConfig ( bytes . NewReader ( [ ] byte ( sessionPolicyStr ) ) )
if err != nil {
2020-05-21 12:09:18 -04:00
writeSTSErrorResponse ( ctx , w , true , ErrSTSInvalidParameterValue , err )
2019-06-20 18:28:33 -04:00
return
}
// Version in policy must not be empty
if sessionPolicy . Version == "" {
2020-05-21 12:09:18 -04:00
writeSTSErrorResponse ( ctx , w , true , ErrSTSInvalidParameterValue , fmt . Errorf ( "Invalid session policy version" ) )
2019-06-20 18:28:33 -04:00
return
}
2019-09-23 18:21:16 -04:00
m [ iampolicy . SessionPolicyName ] = base64 . StdEncoding . EncodeToString ( [ ] byte ( sessionPolicyStr ) )
}
2019-10-23 01:59:13 -04:00
secret := globalActiveCred . SecretKey
2018-10-09 17:00:01 -04:00
cred , err := auth . GetNewCredentialsWithMetadata ( m , secret )
if err != nil {
2020-05-21 12:09:18 -04:00
writeSTSErrorResponse ( ctx , w , true , ErrSTSInternalError , err )
2018-10-09 17:00:01 -04:00
return
}
2019-01-04 16:48:12 -05:00
var subFromToken string
2019-11-29 08:27:54 -05:00
if v , ok := m [ subClaim ] ; ok {
2019-01-04 16:48:12 -05:00
subFromToken , _ = v . ( string )
}
2018-10-09 17:00:01 -04:00
// Set the newly generated credentials.
2018-10-29 14:08:59 -04:00
if err = globalIAMSys . SetTempUser ( cred . AccessKey , cred , policyName ) ; err != nil {
2020-05-21 12:09:18 -04:00
writeSTSErrorResponse ( ctx , w , true , ErrSTSInternalError , err )
2018-10-09 17:00:01 -04:00
return
}
2019-04-09 14:39:42 -04:00
// Notify all other MinIO peers to reload temp users
2019-06-06 20:46:22 -04:00
for _ , nerr := range globalNotificationSys . LoadUser ( cred . AccessKey , true ) {
2019-01-14 01:44:20 -05:00
if nerr . Err != nil {
logger . GetReqInfo ( ctx ) . SetTags ( "peerAddress" , nerr . Host . String ( ) )
logger . LogIf ( ctx , nerr . Err )
2019-01-04 16:48:12 -05:00
}
}
2019-02-05 18:47:11 -05:00
var encodedSuccessResponse [ ] byte
switch action {
case clientGrants :
2019-02-27 20:46:55 -05:00
clientGrantsResponse := & AssumeRoleWithClientGrantsResponse {
2019-02-05 18:47:11 -05:00
Result : ClientGrantsResult {
Credentials : cred ,
SubjectFromToken : subFromToken ,
} ,
2019-02-27 20:46:55 -05:00
}
2019-07-03 01:34:32 -04:00
clientGrantsResponse . ResponseMetadata . RequestID = w . Header ( ) . Get ( xhttp . AmzRequestID )
2019-02-27 20:46:55 -05:00
encodedSuccessResponse = encodeResponse ( clientGrantsResponse )
2019-02-05 18:47:11 -05:00
case webIdentity :
2019-02-27 20:46:55 -05:00
webIdentityResponse := & AssumeRoleWithWebIdentityResponse {
2019-02-05 18:47:11 -05:00
Result : WebIdentityResult {
Credentials : cred ,
SubjectFromWebIdentityToken : subFromToken ,
} ,
2019-02-27 20:46:55 -05:00
}
2019-07-03 01:34:32 -04:00
webIdentityResponse . ResponseMetadata . RequestID = w . Header ( ) . Get ( xhttp . AmzRequestID )
2019-02-27 20:46:55 -05:00
encodedSuccessResponse = encodeResponse ( webIdentityResponse )
2019-02-05 18:47:11 -05:00
}
2018-10-09 17:00:01 -04:00
writeSuccessResponseXML ( w , encodedSuccessResponse )
}
2019-02-05 18:47:11 -05:00
// AssumeRoleWithWebIdentity - implementation of AWS STS API supporting OAuth2.0
// users from web identity provider such as Facebook, Google, or any OpenID
// Connect-compatible identity provider.
//
// Eg:-
// $ curl https://minio:9000/?Action=AssumeRoleWithWebIdentity&WebIdentityToken=<jwt>
func ( sts * stsAPIHandlers ) AssumeRoleWithWebIdentity ( w http . ResponseWriter , r * http . Request ) {
2020-09-12 02:02:32 -04:00
sts . AssumeRoleWithSSO ( w , r )
2019-02-05 18:47:11 -05:00
}
// AssumeRoleWithClientGrants - implementation of AWS STS extension API supporting
// OAuth2.0 client credential grants.
//
// Eg:-
// $ curl https://minio:9000/?Action=AssumeRoleWithClientGrants&Token=<jwt>
func ( sts * stsAPIHandlers ) AssumeRoleWithClientGrants ( w http . ResponseWriter , r * http . Request ) {
2020-09-12 02:02:32 -04:00
sts . AssumeRoleWithSSO ( w , r )
2019-02-05 18:47:11 -05:00
}
2019-09-09 19:12:29 -04:00
// AssumeRoleWithLDAPIdentity - implements user auth against LDAP server
func ( sts * stsAPIHandlers ) AssumeRoleWithLDAPIdentity ( w http . ResponseWriter , r * http . Request ) {
ctx := newContext ( r , w , "AssumeRoleWithLDAPIdentity" )
2021-01-26 16:21:51 -05:00
defer logger . AuditLog ( ctx , w , r , nil , stsLDAPPassword )
2020-06-05 01:07:55 -04:00
2019-09-09 19:12:29 -04:00
// Parse the incoming form data.
if err := r . ParseForm ( ) ; err != nil {
2020-05-21 12:09:18 -04:00
writeSTSErrorResponse ( ctx , w , true , ErrSTSInvalidParameterValue , err )
2019-09-09 19:12:29 -04:00
return
}
2019-11-29 08:27:54 -05:00
if r . Form . Get ( stsVersion ) != stsAPIVersion {
2020-06-05 01:07:55 -04:00
writeSTSErrorResponse ( ctx , w , true , ErrSTSMissingParameter ,
fmt . Errorf ( "Invalid STS API version %s, expecting %s" , r . Form . Get ( "Version" ) , stsAPIVersion ) )
2019-09-09 19:12:29 -04:00
return
}
2019-11-29 08:27:54 -05:00
ldapUsername := r . Form . Get ( stsLDAPUsername )
ldapPassword := r . Form . Get ( stsLDAPPassword )
2019-09-09 19:12:29 -04:00
if ldapUsername == "" || ldapPassword == "" {
2020-05-21 12:09:18 -04:00
writeSTSErrorResponse ( ctx , w , true , ErrSTSMissingParameter , fmt . Errorf ( "LDAPUsername and LDAPPassword cannot be empty" ) )
2019-09-09 19:12:29 -04:00
return
}
2020-06-05 01:07:55 -04:00
action := r . Form . Get ( stsAction )
switch action {
case ldapIdentity :
default :
writeSTSErrorResponse ( ctx , w , true , ErrSTSInvalidParameterValue , fmt . Errorf ( "Unsupported action %s" , action ) )
return
}
2019-11-29 08:27:54 -05:00
sessionPolicyStr := r . Form . Get ( stsPolicy )
2019-09-23 18:21:16 -04:00
// https://docs.aws.amazon.com/STS/latest/APIReference/API_AssumeRole.html
// The plain text that you use for both inline and managed session
// policies shouldn't exceed 2048 characters.
if len ( sessionPolicyStr ) > 2048 {
2020-05-21 12:09:18 -04:00
writeSTSErrorResponse ( ctx , w , true , ErrSTSInvalidParameterValue , fmt . Errorf ( "Session policy should not exceed 2048 characters" ) )
2019-09-23 18:21:16 -04:00
return
}
if len ( sessionPolicyStr ) > 0 {
sessionPolicy , err := iampolicy . ParseConfig ( bytes . NewReader ( [ ] byte ( sessionPolicyStr ) ) )
if err != nil {
2020-05-21 12:09:18 -04:00
writeSTSErrorResponse ( ctx , w , true , ErrSTSInvalidParameterValue , err )
2019-09-23 18:21:16 -04:00
return
}
// Version in policy must not be empty
if sessionPolicy . Version == "" {
2020-05-21 12:09:18 -04:00
writeSTSErrorResponse ( ctx , w , true , ErrSTSInvalidParameterValue , fmt . Errorf ( "Version needs to be specified in session policy" ) )
2019-09-23 18:21:16 -04:00
return
}
}
2021-02-10 19:52:49 -05:00
ldapUserDN , groupDistNames , err := globalLDAPConfig . Bind ( ldapUsername , ldapPassword )
2019-09-09 19:12:29 -04:00
if err != nil {
2021-01-18 00:54:32 -05:00
err = fmt . Errorf ( "LDAP server error: %w" , err )
2020-05-21 12:09:18 -04:00
writeSTSErrorResponse ( ctx , w , true , ErrSTSInvalidParameterValue , err )
2019-09-09 19:12:29 -04:00
return
}
2021-02-10 19:52:49 -05:00
// Check if this user or their groups have a policy applied.
globalIAMSys . Lock ( )
found := false
if _ , ok := globalIAMSys . iamUserPolicyMap [ ldapUserDN ] ; ok {
found = true
}
for _ , groupDistName := range groupDistNames {
if _ , ok := globalIAMSys . iamGroupPolicyMap [ groupDistName ] ; ok {
found = true
break
}
}
globalIAMSys . Unlock ( )
if ! found {
writeSTSErrorResponse ( ctx , w , true , ErrSTSInvalidParameterValue , fmt . Errorf ( "expecting a policy to be set for user `%s` or one of their groups: `%s` - rejecting this request" , ldapUserDN , strings . Join ( groupDistNames , "`,`" ) ) )
return
}
2019-10-23 01:59:13 -04:00
expiryDur := globalLDAPConfig . GetExpiryDuration ( )
2019-09-09 19:12:29 -04:00
m := map [ string ] interface { } {
2020-05-20 14:33:35 -04:00
expClaim : UTCNow ( ) . Add ( expiryDur ) . Unix ( ) ,
2021-01-18 00:54:32 -05:00
ldapUser : ldapUserDN ,
2019-09-09 19:12:29 -04:00
}
2019-09-23 18:21:16 -04:00
if len ( sessionPolicyStr ) > 0 {
m [ iampolicy . SessionPolicyName ] = base64 . StdEncoding . EncodeToString ( [ ] byte ( sessionPolicyStr ) )
}
2019-10-23 01:59:13 -04:00
secret := globalActiveCred . SecretKey
2019-09-09 19:12:29 -04:00
cred , err := auth . GetNewCredentialsWithMetadata ( m , secret )
if err != nil {
2020-05-21 12:09:18 -04:00
writeSTSErrorResponse ( ctx , w , true , ErrSTSInternalError , err )
2019-09-09 19:12:29 -04:00
return
}
2020-04-28 15:49:56 -04:00
// Set the parent of the temporary access key, this is useful
// in obtaining service accounts by this cred.
2021-01-18 00:54:32 -05:00
cred . ParentUser = ldapUserDN
2020-04-28 15:49:56 -04:00
2020-05-20 14:33:35 -04:00
// Set this value to LDAP groups, LDAP user can be part
// of large number of groups
2021-02-10 19:52:49 -05:00
cred . Groups = groupDistNames
2020-05-20 14:33:35 -04:00
2020-04-28 15:49:56 -04:00
// Set the newly generated credentials, policyName is empty on purpose
// LDAP policies are applied automatically using their ldapUser, ldapGroups
// mapping.
if err = globalIAMSys . SetTempUser ( cred . AccessKey , cred , "" ) ; err != nil {
2020-05-21 12:09:18 -04:00
writeSTSErrorResponse ( ctx , w , true , ErrSTSInternalError , err )
2019-09-09 19:12:29 -04:00
return
}
// Notify all other MinIO peers to reload temp users
for _ , nerr := range globalNotificationSys . LoadUser ( cred . AccessKey , true ) {
if nerr . Err != nil {
logger . GetReqInfo ( ctx ) . SetTags ( "peerAddress" , nerr . Host . String ( ) )
logger . LogIf ( ctx , nerr . Err )
}
}
ldapIdentityResponse := & AssumeRoleWithLDAPResponse {
Result : LDAPIdentityResult {
Credentials : cred ,
} ,
}
ldapIdentityResponse . ResponseMetadata . RequestID = w . Header ( ) . Get ( xhttp . AmzRequestID )
encodedSuccessResponse := encodeResponse ( ldapIdentityResponse )
writeSuccessResponseXML ( w , encodedSuccessResponse )
}