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.

Harshavardhana committed 5 years ago

Commit eb6871e1 parent 9cdd9812 files · +42 −28Browse files

Unified · Split

2 files
  1. cmd/api-errors.go +6 −0
  2. cmd/web-handlers.go +36 −28
cmd/api-errors.go+6−0
@@ -2125,6 +2125,12 @@ func toAPIError(ctx context.Context, err error) APIError { · 2124 unmodified lines
21252125 HTTPStatusCode: e.Response().StatusCode,
21262126 }
21272127 // Add more Gateway SDKs here if any in future.
2128+ default:
2129+ apiErr = APIError{
2130+ Code: apiErr.Code,
2131+ Description: fmt.Sprintf("%s: cause(%v)", apiErr.Description, err),
2132+ HTTPStatusCode: apiErr.HTTPStatusCode,
2133+ }
21282134 }
21292135 }
21302136
cmd/web-handlers.go+36−28
@@ -20,7 +20,6 @@ import ( · 19 unmodified lines
2020 "context"
2121 "crypto/subtle"
2222 "encoding/json"
23− "encoding/xml"
2423 "errors"
2524 "fmt"
2625 "io"
@@ -2202,54 +2201,61 @@ type LoginSTSArgs struct { · 2175 unmodified lines
22022201 Token string `json:"token" form:"token"`
22032202 }
22042203
2204+var errSTSNotInitialized = errors.New("STS API not initialized, please configure STS support")
2205+
22052206 // LoginSTS - STS user login handler.
22062207 func (web *webAPIHandlers) LoginSTS(r *http.Request, args *LoginSTSArgs, reply *LoginRep) error {
22072208 ctx := newWebContext(r, args, "WebLoginSTS")
22082209
2209− v := url.Values{}
2210− v.Set("Action", webIdentity)
2211− v.Set("WebIdentityToken", args.Token)
2212− v.Set("Version", stsAPIVersion)
2213−
2214− scheme := "http"
2215− if sourceScheme := handlers.GetSourceScheme(r); sourceScheme != "" {
2216− scheme = sourceScheme
2217− }
2218− if globalIsTLS {
2219− scheme = "https"
2210+ if globalOpenIDValidators == nil {
2211+ return toJSONError(ctx, errSTSNotInitialized)
22202212 }
22212213
2222− u := &url.URL{
2223− Scheme: scheme,
2224− Host: r.Host,
2214+ v, err := globalOpenIDValidators.Get("jwt")
2215+ if err != nil {
2216+ logger.LogIf(ctx, err)
2217+ return toJSONError(ctx, errSTSNotInitialized)
22252218 }
22262219
2227− u.RawQuery = v.Encode()
2228−
2229− req, err := http.NewRequest(http.MethodPost, u.String(), nil)
2220+ m, err := v.Validate(args.Token, "")
22302221 if err != nil {
22312222 return toJSONError(ctx, err)
22322223 }
22332224
2234− clnt := &http.Client{
2235− Transport: NewGatewayHTTPTransport(),
2225+ // JWT has requested a custom claim with policy value set.
2226+ // This is a MinIO STS API specific value, this value should
2227+ // be set and configured on your identity provider as part of
2228+ // JWT custom claims.
2229+ var policyName string
2230+ policySet, ok := iampolicy.GetPoliciesFromClaims(m, iamPolicyClaimNameOpenID())
2231+ if ok {
2232+ policyName = globalIAMSys.CurrentPolicies(strings.Join(policySet.ToSlice(), ","))
22362233 }
2237− resp, err := clnt.Do(req)
2234+ if policyName == "" && globalPolicyOPA == nil {
2235+ return toJSONError(ctx, fmt.Errorf("%s claim missing from the JWT token, credentials will not be generated", iamPolicyClaimNameOpenID()))
2236+ }
2237+ m[iamPolicyClaimNameOpenID()] = policyName
2238+
2239+ secret := globalActiveCred.SecretKey
2240+ cred, err := auth.GetNewCredentialsWithMetadata(m, secret)
22382241 if err != nil {
22392242 return toJSONError(ctx, err)
22402243 }
2241− defer xhttp.DrainBody(resp.Body)
22422244
2243− if resp.StatusCode != http.StatusOK {
2244− return toJSONError(ctx, errors.New(resp.Status))
2245+ // Set the newly generated credentials.
2246+ if err = globalIAMSys.SetTempUser(cred.AccessKey, cred, policyName); err != nil {
2247+ return toJSONError(ctx, err)
22452248 }
22462249
2247− a := AssumeRoleWithWebIdentityResponse{}
2248− if err = xml.NewDecoder(resp.Body).Decode(&a); err != nil {
2249− return toJSONError(ctx, err)
2250+ // Notify all other MinIO peers to reload temp users
2251+ for _, nerr := range globalNotificationSys.LoadUser(cred.AccessKey, true) {
2252+ if nerr.Err != nil {
2253+ logger.GetReqInfo(ctx).SetTags("peerAddress", nerr.Host.String())
2254+ logger.LogIf(ctx, nerr.Err)
2255+ }
22502256 }
22512257
2252− reply.Token = a.Result.Credentials.SessionToken
2258+ reply.Token = cred.SessionToken
22532259 reply.UIVersion = browser.UIVersion
22542260 return nil
22552261 }
@@ -2304,6 +2310,8 @@ func toWebAPIError(ctx context.Context, err error) APIError { · 48 unmodified lines
23042310 HTTPStatusCode: http.StatusBadRequest,
23052311 Description: err.Error(),
23062312 }
2313+ case errSTSNotInitialized:
2314+ return APIError(stsErrCodes.ToSTSErr(ErrSTSNotInitialized))
23072315 case errServerNotInitialized:
23082316 return APIError{
23092317 Code: "XMinioServerNotInitialized",

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t