Skip to content
Permalink
Browse files Browse the repository at this point in the history
fix: LoginSTS should be an inline implementation (#11337)
STS tokens can be obtained by using local APIs
once the remote JWT token is presented, current
code was not validating the incoming token in the
first place and was incorrectly making a network
operation using that token.

For the most part this always works without issues,
but under adversarial scenarios it exposes client
to hand-craft a request that can reach internal
services without authentication.

This kind of proxying should be avoided before
validating the incoming token.
  • Loading branch information
harshavardhana committed Jan 25, 2021
1 parent 9cdd981 commit eb6871e
Show file tree
Hide file tree
Showing 2 changed files with 42 additions and 28 deletions.
6 changes: 6 additions & 0 deletions cmd/api-errors.go
Expand Up @@ -2125,6 +2125,12 @@ func toAPIError(ctx context.Context, err error) APIError {
HTTPStatusCode: e.Response().StatusCode,
}
// Add more Gateway SDKs here if any in future.
default:
apiErr = APIError{
Code: apiErr.Code,
Description: fmt.Sprintf("%s: cause(%v)", apiErr.Description, err),
HTTPStatusCode: apiErr.HTTPStatusCode,
}
}
}

Expand Down
64 changes: 36 additions & 28 deletions cmd/web-handlers.go
Expand Up @@ -20,7 +20,6 @@ import (
"context"
"crypto/subtle"
"encoding/json"
"encoding/xml"
"errors"
"fmt"
"io"
Expand Down Expand Up @@ -2202,54 +2201,61 @@ type LoginSTSArgs struct {
Token string `json:"token" form:"token"`
}

var errSTSNotInitialized = errors.New("STS API not initialized, please configure STS support")

// LoginSTS - STS user login handler.
func (web *webAPIHandlers) LoginSTS(r *http.Request, args *LoginSTSArgs, reply *LoginRep) error {
ctx := newWebContext(r, args, "WebLoginSTS")

v := url.Values{}
v.Set("Action", webIdentity)
v.Set("WebIdentityToken", args.Token)
v.Set("Version", stsAPIVersion)

scheme := "http"
if sourceScheme := handlers.GetSourceScheme(r); sourceScheme != "" {
scheme = sourceScheme
}
if globalIsTLS {
scheme = "https"
if globalOpenIDValidators == nil {
return toJSONError(ctx, errSTSNotInitialized)
}

u := &url.URL{
Scheme: scheme,
Host: r.Host,
v, err := globalOpenIDValidators.Get("jwt")
if err != nil {
logger.LogIf(ctx, err)
return toJSONError(ctx, errSTSNotInitialized)
}

u.RawQuery = v.Encode()

req, err := http.NewRequest(http.MethodPost, u.String(), nil)
m, err := v.Validate(args.Token, "")
if err != nil {
return toJSONError(ctx, err)
}

clnt := &http.Client{
Transport: NewGatewayHTTPTransport(),
// 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 {
policyName = globalIAMSys.CurrentPolicies(strings.Join(policySet.ToSlice(), ","))
}
resp, err := clnt.Do(req)
if policyName == "" && globalPolicyOPA == nil {
return toJSONError(ctx, fmt.Errorf("%s claim missing from the JWT token, credentials will not be generated", iamPolicyClaimNameOpenID()))
}
m[iamPolicyClaimNameOpenID()] = policyName

secret := globalActiveCred.SecretKey
cred, err := auth.GetNewCredentialsWithMetadata(m, secret)
if err != nil {
return toJSONError(ctx, err)
}
defer xhttp.DrainBody(resp.Body)

if resp.StatusCode != http.StatusOK {
return toJSONError(ctx, errors.New(resp.Status))
// Set the newly generated credentials.
if err = globalIAMSys.SetTempUser(cred.AccessKey, cred, policyName); err != nil {
return toJSONError(ctx, err)
}

a := AssumeRoleWithWebIdentityResponse{}
if err = xml.NewDecoder(resp.Body).Decode(&a); err != nil {
return toJSONError(ctx, err)
// 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)
}
}

reply.Token = a.Result.Credentials.SessionToken
reply.Token = cred.SessionToken
reply.UIVersion = browser.UIVersion
return nil
}
Expand Down Expand Up @@ -2304,6 +2310,8 @@ func toWebAPIError(ctx context.Context, err error) APIError {
HTTPStatusCode: http.StatusBadRequest,
Description: err.Error(),
}
case errSTSNotInitialized:
return APIError(stsErrCodes.ToSTSErr(ErrSTSNotInitialized))
case errServerNotInitialized:
return APIError{
Code: "XMinioServerNotInitialized",
Expand Down

0 comments on commit eb6871e

Please sign in to comment.