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
2125 HTTPStatusCode: e.Response().StatusCode,2125 HTTPStatusCode: e.Response().StatusCode,
2126 }2126 }
2127 // Add more Gateway SDKs here if any in future.2127 // 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 }
2128 }2134 }
2129 }2135 }
2130 2136
cmd/web-handlers.go+36−28
@@ -20,7 +20,6 @@ import ( · 19 unmodified lines
20 "context"20 "context"
21 "crypto/subtle"21 "crypto/subtle"
22 "encoding/json"22 "encoding/json"
23 "encoding/xml"
24 "errors"23 "errors"
25 "fmt"24 "fmt"
26 "io"25 "io"
@@ -2202,54 +2201,61 @@ type LoginSTSArgs struct { · 2175 unmodified lines
2202 Token string `json:"token" form:"token"`2201 Token string `json:"token" form:"token"`
2203}2202}
2204 2203
2204var errSTSNotInitialized = errors.New("STS API not initialized, please configure STS support")
2205
2205// LoginSTS - STS user login handler.2206// LoginSTS - STS user login handler.
2206func (web *webAPIHandlers) LoginSTS(r *http.Request, args *LoginSTSArgs, reply *LoginRep) error {2207func (web *webAPIHandlers) LoginSTS(r *http.Request, args *LoginSTSArgs, reply *LoginRep) error {
2207 ctx := newWebContext(r, args, "WebLoginSTS")2208 ctx := newWebContext(r, args, "WebLoginSTS")
2208 2209
2209 v := url.Values{}2210 if globalOpenIDValidators == nil {
2210 v.Set("Action", webIdentity)2211 return toJSONError(ctx, errSTSNotInitialized)
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"
2220 }2212 }
2221 2213
2222 u := &url.URL{2214 v, err := globalOpenIDValidators.Get("jwt")
2223 Scheme: scheme,2215 if err != nil {
2224 Host: r.Host,2216 logger.LogIf(ctx, err)
2217 return toJSONError(ctx, errSTSNotInitialized)
2225 }2218 }
2226 2219
2227 u.RawQuery = v.Encode()2220 m, err := v.Validate(args.Token, "")
2228
2229 req, err := http.NewRequest(http.MethodPost, u.String(), nil)
2230 if err != nil {2221 if err != nil {
2231 return toJSONError(ctx, err)2222 return toJSONError(ctx, err)
2232 }2223 }
2233 2224
2234 clnt := &http.Client{2225 // JWT has requested a custom claim with policy value set.
2235 Transport: NewGatewayHTTPTransport(),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(), ","))
2236 }2233 }
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)
2238 if err != nil {2241 if err != nil {
2239 return toJSONError(ctx, err)2242 return toJSONError(ctx, err)
2240 }2243 }
2241 defer xhttp.DrainBody(resp.Body)
2242 2244
2243 if resp.StatusCode != http.StatusOK {2245 // Set the newly generated credentials.
2244 return toJSONError(ctx, errors.New(resp.Status))2246 if err = globalIAMSys.SetTempUser(cred.AccessKey, cred, policyName); err != nil {
2247 return toJSONError(ctx, err)
2245 }2248 }
2246 2249
2247 a := AssumeRoleWithWebIdentityResponse{}2250 // Notify all other MinIO peers to reload temp users
2248 if err = xml.NewDecoder(resp.Body).Decode(&a); err != nil {2251 for _, nerr := range globalNotificationSys.LoadUser(cred.AccessKey, true) {
2249 return toJSONError(ctx, err)2252 if nerr.Err != nil {
2253 logger.GetReqInfo(ctx).SetTags("peerAddress", nerr.Host.String())
2254 logger.LogIf(ctx, nerr.Err)
2255 }
2250 }2256 }
2251 2257
2252 reply.Token = a.Result.Credentials.SessionToken2258 reply.Token = cred.SessionToken
2253 reply.UIVersion = browser.UIVersion2259 reply.UIVersion = browser.UIVersion
2254 return nil2260 return nil
2255}2261}
@@ -2304,6 +2310,8 @@ func toWebAPIError(ctx context.Context, err error) APIError { · 48 unmodified lines
2304 HTTPStatusCode: http.StatusBadRequest,2310 HTTPStatusCode: http.StatusBadRequest,
2305 Description: err.Error(),2311 Description: err.Error(),
2306 }2312 }
2313 case errSTSNotInitialized:
2314 return APIError(stsErrCodes.ToSTSErr(ErrSTSNotInitialized))
2307 case errServerNotInitialized:2315 case errServerNotInitialized:
2308 return APIError{2316 return APIError{
2309 Code: "XMinioServerNotInitialized",2317 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