...
 
Commits (5)
......@@ -97,11 +97,13 @@ func (a adminAPIHandlers) RemoveUser(w http.ResponseWriter, r *http.Request) {
func (a adminAPIHandlers) ListUsers(w http.ResponseWriter, r *http.Request) {
ctx := newContext(r, w, "ListUsers")
objectAPI, _ := validateAdminUsersReq(ctx, w, r, iampolicy.ListUsersAdminAction)
objectAPI, cred := validateAdminUsersReq(ctx, w, r, iampolicy.ListUsersAdminAction)
if objectAPI == nil {
return
}
password := cred.SecretKey
allCredentials, err := globalIAMSys.ListUsers()
if err != nil {
writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL)
......@@ -114,7 +116,6 @@ func (a adminAPIHandlers) ListUsers(w http.ResponseWriter, r *http.Request) {
return
}
password := globalActiveCred.SecretKey
econfigData, err := madmin.EncryptData(password, data)
if err != nil {
writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL)
......@@ -462,7 +463,7 @@ func (a adminAPIHandlers) AddServiceAccount(w http.ResponseWriter, r *http.Reque
func (a adminAPIHandlers) GetServiceAccount(w http.ResponseWriter, r *http.Request) {
ctx := newContext(r, w, "GetServiceAccount")
objectAPI, _ := validateAdminUsersReq(ctx, w, r, iampolicy.GetUserAdminAction)
objectAPI, cred := validateAdminUsersReq(ctx, w, r, iampolicy.GetUserAdminAction)
if objectAPI == nil {
return
}
......@@ -470,6 +471,8 @@ func (a adminAPIHandlers) GetServiceAccount(w http.ResponseWriter, r *http.Reque
vars := mux.Vars(r)
accessKey := vars["accessKey"]
password := cred.SecretKey
creds, err := globalIAMSys.GetServiceAccount(ctx, accessKey)
if err != nil {
writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL)
......@@ -482,7 +485,6 @@ func (a adminAPIHandlers) GetServiceAccount(w http.ResponseWriter, r *http.Reque
return
}
password := globalActiveCred.SecretKey
econfigData, err := madmin.EncryptData(password, data)
if err != nil {
writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL)
......
......@@ -211,8 +211,9 @@ func getClaimsFromToken(r *http.Request) (map[string]interface{}, error) {
// If OPA is not set, session token should
// have a policy and its mandatory, reject
// requests without policy claim.
_, pok := claims.Lookup(iamPolicyClaimName())
if !pok {
_, pokOpenID := claims.Lookup(iamPolicyClaimNameOpenID())
_, pokSA := claims.Lookup(iamPolicyClaimNameSA())
if !pokOpenID && !pokSA {
return nil, errAuthentication
}
......
......@@ -20,7 +20,6 @@ import (
"bytes"
"context"
"io"
"net/http"
"testing"
"github.com/minio/minio/pkg/hash"
......@@ -174,84 +173,6 @@ func TestCacheExclusion(t *testing.T) {
}
}
// Test diskCache.
func TestDiskCache(t *testing.T) {
fsDirs, err := getRandomDisks(1)
if err != nil {
t.Fatal(err)
}
d, err := initDiskCaches(fsDirs, 100, 0, 80, 90, t)
if err != nil {
t.Fatal(err)
}
c := cacheObjects{cache: d}
cache := c.cache[0]
ctx := context.Background()
bucketName := "testbucket"
objectName := "testobject"
content := "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
etag := "061208c10af71a30c6dcd6cf5d89f0fe"
contentType := "application/zip"
size := len(content)
httpMeta := make(map[string]string)
httpMeta["etag"] = etag
httpMeta["content-type"] = contentType
objInfo := ObjectInfo{}
objInfo.Bucket = bucketName
objInfo.Name = objectName
objInfo.Size = int64(size)
objInfo.ContentType = contentType
objInfo.ETag = etag
objInfo.UserDefined = httpMeta
var opts ObjectOptions
byteReader := bytes.NewReader([]byte(content))
hashReader, err := hash.NewReader(byteReader, int64(size), "", "", int64(size), globalCLIContext.StrictS3Compat)
if err != nil {
t.Fatal(err)
}
err = cache.Put(ctx, bucketName, objectName, hashReader, hashReader.Size(), nil, ObjectOptions{UserDefined: httpMeta}, false)
if err != nil {
t.Fatal(err)
}
cReader, _, err := cache.Get(ctx, bucketName, objectName, nil, http.Header{
"Content-Type": []string{"application/json"},
}, opts)
if err != nil {
t.Fatal(err)
}
cachedObjInfo := cReader.ObjInfo
if !cache.Exists(ctx, bucketName, objectName) {
t.Fatal("Expected object to exist on cache")
}
if cachedObjInfo.ETag != objInfo.ETag {
t.Fatal("Expected ETag to match")
}
if cachedObjInfo.Size != objInfo.Size {
t.Fatal("Size mismatch")
}
if cachedObjInfo.ContentType != objInfo.ContentType {
t.Fatal("Cached content-type does not match")
}
writer := bytes.NewBuffer(nil)
_, err = io.Copy(writer, cReader)
if err != nil {
t.Fatal(err)
}
if ccontent := writer.Bytes(); !bytes.Equal([]byte(content), ccontent) {
t.Errorf("wrong cached file content")
}
cReader.Close()
cache.Delete(ctx, bucketName, objectName)
online := cache.IsOnline()
if !online {
t.Errorf("expected cache drive to be online")
}
}
// Test diskCache with upper bound on max cache use.
func TestDiskCacheMaxUse(t *testing.T) {
fsDirs, err := getRandomDisks(1)
......
......@@ -798,17 +798,18 @@ func (sys *IAMSys) NewServiceAccount(ctx context.Context, parentUser, sessionPol
}
if len(sessionPolicy) > 16*1024 {
return auth.Credentials{}, fmt.Errorf("Session policy should not exceed 16*1024 characters")
return auth.Credentials{}, fmt.Errorf("Session policy should not exceed 16 KiB characters")
}
policy, err := iampolicy.ParseConfig(bytes.NewReader([]byte(sessionPolicy)))
if err != nil {
return auth.Credentials{}, err
}
// Version in policy must not be empty
if policy.Version == "" {
return auth.Credentials{}, fmt.Errorf("Invalid session policy version")
if len(sessionPolicy) > 0 {
policy, err := iampolicy.ParseConfig(bytes.NewReader([]byte(sessionPolicy)))
if err != nil {
return auth.Credentials{}, err
}
// Version in policy must not be empty
if policy.Version == "" {
return auth.Credentials{}, fmt.Errorf("Invalid session policy version")
}
}
sys.Lock()
......@@ -836,9 +837,14 @@ func (sys *IAMSys) NewServiceAccount(ctx context.Context, parentUser, sessionPol
}
m := make(map[string]interface{})
m[iampolicy.SessionPolicyName] = base64.StdEncoding.EncodeToString([]byte(sessionPolicy))
m[parentClaim] = parentUser
m[iamPolicyClaimName()] = "embedded-policy"
if len(sessionPolicy) > 0 {
m[iampolicy.SessionPolicyName] = base64.StdEncoding.EncodeToString([]byte(sessionPolicy))
m[iamPolicyClaimNameSA()] = "embedded-policy"
} else {
m[iamPolicyClaimNameSA()] = "inherited-policy"
}
secret := globalActiveCred.SecretKey
cred, err := auth.GetNewCredentialsWithMetadata(m, secret)
......@@ -1473,6 +1479,11 @@ func (sys *IAMSys) IsAllowedServiceAccount(args iampolicy.Args, parent string) b
if parentInClaim != parent {
return false
}
} else {
// This is needed so a malicious user cannot
// use a leaked session key of another user
// to widen its privileges.
return false
}
// Check if the parent is allowed to perform this action, reject if not
......@@ -1508,12 +1519,27 @@ func (sys *IAMSys) IsAllowedServiceAccount(args iampolicy.Args, parent string) b
availablePolicies[i].Statements...)
}
serviceAcc := args.AccountName
args.AccountName = parent
if !combinedPolicy.IsAllowed(args) {
parentArgs := args
parentArgs.AccountName = parent
if !combinedPolicy.IsAllowed(parentArgs) {
return false
}
args.AccountName = serviceAcc
saPolicyClaim, ok := args.Claims[iamPolicyClaimNameSA()]
if ok {
saPolicyClaimStr, ok := saPolicyClaim.(string)
if !ok {
// Sub policy if set, should be a string reject
// malformed/malicious requests.
return false
}
if saPolicyClaimStr == "inherited-policy" {
// Immediately returns true since at this stage, since
// parent user is allowed to do this action.
return true
}
}
// Now check if we have a sessionPolicy.
spolicy, ok := args.Claims[iampolicy.SessionPolicyName]
......@@ -1605,7 +1631,7 @@ func (sys *IAMSys) IsAllowedSTS(args iampolicy.Args) bool {
return combinedPolicy.IsAllowed(args)
}
pnameSlice, ok := args.GetPolicies(iamPolicyClaimName())
pnameSlice, ok := args.GetPolicies(iamPolicyClaimNameOpenID())
if !ok {
// When claims are set, it should have a policy claim field.
return false
......
......@@ -246,7 +246,9 @@ func connectLoadInitFormats(retryCount int, firstDisk bool, endpoints Endpoints,
}
// not critical error but still print the error, nonetheless, which is perhaps unhandled
if sErr != errUnformattedDisk && sErr != errDiskNotFound && retryCount >= 5 {
logger.Info("Unable to read 'format.json' from %s: %v\n", endpoints[i], sErr)
if sErr != nil {
logger.Info("Unable to read 'format.json' from %s: %v\n", endpoints[i], sErr)
}
}
}
......
......@@ -215,7 +215,7 @@ func (sts *stsAPIHandlers) AssumeRole(w http.ResponseWriter, r *http.Request) {
// This policy is the policy associated with the user
// requesting for temporary credentials. The temporary
// credentials will inherit the same policy requirements.
m[iamPolicyClaimName()] = policyName
m[iamPolicyClaimNameOpenID()] = policyName
if len(sessionPolicyStr) > 0 {
m[iampolicy.SessionPolicyName] = base64.StdEncoding.EncodeToString([]byte(sessionPolicyStr))
......@@ -351,7 +351,7 @@ func (sts *stsAPIHandlers) AssumeRoleWithJWT(w http.ResponseWriter, r *http.Requ
// be set and configured on your identity provider as part of
// JWT custom claims.
var policyName string
if v, ok := m[iamPolicyClaimName()]; ok {
if v, ok := m[iamPolicyClaimNameOpenID()]; ok {
policyName, _ = v.(string)
}
......
......@@ -620,10 +620,14 @@ func getMinioMode() string {
return mode
}
func iamPolicyClaimName() string {
func iamPolicyClaimNameOpenID() string {
return globalOpenIDConfig.ClaimPrefix + globalOpenIDConfig.ClaimName
}
func iamPolicyClaimNameSA() string {
return "sa-policy"
}
func isWORMEnabled(bucket string) bool {
if isMinioMetaBucketName(bucket) {
return false
......
......@@ -112,9 +112,10 @@ func sendEvents(target event.Target, eventKeyCh <-chan string, doneCh <-chan str
loggerOnce(context.Background(),
fmt.Errorf("target.Send() failed with '%w'", err),
target.ID())
continue
}
// Retrying after 3secs back-off
select {
case <-retryTicker.C:
case <-doneCh:
......