From eae9c2f65bade4d9de2aac42f188114bf6dbe555 Mon Sep 17 00:00:00 2001 From: Aditya Manthramurthy Date: Thu, 15 Jul 2021 15:27:34 -0700 Subject: [PATCH] Add changes to ensure session policy is enforced in LDAP STS (#12716) - Bonus: Fix bug in webidentity sts that doesnt parse session policy correctly. - update ldap.go to support session policy argument --- cmd/iam.go | 75 ++++++++++++++++++++++++++++++++---------------- docs/sts/ldap.go | 66 +++++++++++++++++++++++++++++++++++------- 2 files changed, 107 insertions(+), 34 deletions(-) diff --git a/cmd/iam.go b/cmd/iam.go index 2d40f23b5..54f1c91f8 100644 --- a/cmd/iam.go +++ b/cmd/iam.go @@ -2199,6 +2199,11 @@ func (sys *IAMSys) IsAllowedLDAPSTS(args iampolicy.Args, parentUser string) bool availablePolicies[i].Statements...) } + hasSessionPolicy, isAllowedSP := isAllowedBySessionPolicy(args) + if hasSessionPolicy { + return isAllowedSP && combinedPolicy.IsAllowed(args) + } + return combinedPolicy.IsAllowed(args) } @@ -2263,30 +2268,9 @@ func (sys *IAMSys) IsAllowedSTS(args iampolicy.Args, parentUser string) bool { args.ConditionValues["userid"] = []string{parentUser} // Now check if we have a sessionPolicy. - spolicy, ok := args.Claims[iampolicy.SessionPolicyName] - if ok { - spolicyStr, ok := spolicy.(string) - if !ok { - // Sub policy if set, should be a string reject - // malformed/malicious requests. - return false - } - - // Check if policy is parseable. - subPolicy, err := iampolicy.ParseConfig(bytes.NewReader([]byte(spolicyStr))) - if err != nil { - // Log any error in input session policy config. - logger.LogIf(GlobalContext, err) - return false - } - - // Policy without Version string value reject it. - if subPolicy.Version == "" { - return false - } - - // Sub policy is set and valid. - return combinedPolicy.IsAllowed(args) && subPolicy.IsAllowed(args) + hasSessionPolicy, isAllowedSP := isAllowedBySessionPolicy(args) + if hasSessionPolicy { + return isAllowedSP && combinedPolicy.IsAllowed(args) } // Sub policy not set, this is most common since subPolicy @@ -2294,6 +2278,49 @@ func (sys *IAMSys) IsAllowedSTS(args iampolicy.Args, parentUser string) bool { return combinedPolicy.IsAllowed(args) } +func isAllowedBySessionPolicy(args iampolicy.Args) (hasSessionPolicy bool, isAllowed bool) { + hasSessionPolicy = false + isAllowed = false + + // Now check if we have a sessionPolicy. + spolicy, ok := args.Claims[iampolicy.SessionPolicyName] + if !ok { + return + } + + hasSessionPolicy = true + + spolicyStr, ok := spolicy.(string) + if !ok { + // Sub policy if set, should be a string reject + // malformed/malicious requests. + return + } + + policyBytes, err := base64.StdEncoding.DecodeString(spolicyStr) + if err != nil { + // Got a malformed base64 string + return + } + spolicyStr = string(policyBytes) + + // Check if policy is parseable. + subPolicy, err := iampolicy.ParseConfig(bytes.NewReader([]byte(spolicyStr))) + if err != nil { + // Log any error in input session policy config. + logger.LogIf(GlobalContext, err) + return + } + + // Policy without Version string value reject it. + if subPolicy.Version == "" { + return + } + + // Sub policy is set and valid. + return hasSessionPolicy, subPolicy.IsAllowed(args) +} + // GetCombinedPolicy returns a combined policy combining all policies func (sys *IAMSys) GetCombinedPolicy(policies ...string) iampolicy.Policy { // Policies were found, evaluate all of them. diff --git a/docs/sts/ldap.go b/docs/sts/ldap.go index 57dae8fa0..dd8cb2b92 100644 --- a/docs/sts/ldap.go +++ b/docs/sts/ldap.go @@ -23,8 +23,10 @@ import ( "context" "flag" "fmt" + "io/ioutil" "log" "net/url" + "os" "github.com/minio/minio-go/v7" cr "github.com/minio/minio-go/v7/pkg/credentials" @@ -37,12 +39,24 @@ var ( // LDAP credentials ldapUsername string ldapPassword string + + // Display credentials flag + displayCreds bool + + // Bucket to list + bucketToList string + + // Session policy file + sessionPolicyFile string ) func init() { flag.StringVar(&stsEndpoint, "sts-ep", "http://localhost:9000", "STS endpoint") flag.StringVar(&ldapUsername, "u", "", "AD/LDAP Username") flag.StringVar(&ldapPassword, "p", "", "AD/LDAP Password") + flag.BoolVar(&displayCreds, "d", false, "Only show generated credentials") + flag.StringVar(&bucketToList, "b", "", "Bucket to list (defaults to ldap username)") + flag.StringVar(&sessionPolicyFile, "s", "", "File containing session policy to apply to the STS request") } func main() { @@ -56,11 +70,30 @@ func main() { // LDAP STS API. // Initialize LDAP credentials - li, _ := cr.NewLDAPIdentity(stsEndpoint, ldapUsername, ldapPassword) + var li *cr.Credentials + var err error + if sessionPolicyFile == "" { + li, err = cr.NewLDAPIdentity(stsEndpoint, ldapUsername, ldapPassword) + } else { + var policy string + if f, err := os.Open(sessionPolicyFile); err != nil { + log.Fatalf("Unable to open session policy file: %v", sessionPolicyFile, err) + } else { + bs, err := ioutil.ReadAll(f) + if err != nil { + log.Fatalf("Error reading session policy file: %v", err) + } + policy = string(bs) + } + li, err = cr.NewLDAPIdentityWithSessionPolicy(stsEndpoint, ldapUsername, ldapPassword, policy) + } + if err != nil { + log.Fatalf("Error initializing LDAP Identity: %v", err) + } stsEndpointURL, err := url.Parse(stsEndpoint) if err != nil { - log.Fatalf("Err: %v", err) + log.Fatalf("Error parsing sts endpoint: %v", err) } opts := &minio.Options{ @@ -68,22 +101,35 @@ func main() { Secure: stsEndpointURL.Scheme == "https", } - fmt.Println(li.Get()) + v, err := li.Get() + if err != nil { + log.Fatalf("Error retrieving STS credentials: %v", err) + } + + if displayCreds { + fmt.Println("Only displaying credentials:") + fmt.Println("AccessKeyID:", v.AccessKeyID) + fmt.Println("SecretAccessKey:", v.SecretAccessKey) + fmt.Println("SessionToken:", v.SessionToken) + return + } + // Use generated credentials to authenticate with MinIO server minioClient, err := minio.New(stsEndpointURL.Host, opts) if err != nil { - log.Fatalln(err) + log.Fatalf("Error initializing client: ", err) } // Use minIO Client object normally like the regular client. - fmt.Println("Calling list objects with temp creds: ") - objCh := minioClient.ListObjects(context.Background(), ldapUsername, minio.ListObjectsOptions{}) + if bucketToList == "" { + bucketToList = ldapUsername + } + fmt.Printf("Calling list objects on bucket named `%s` with temp creds:\n===\n", bucketToList) + objCh := minioClient.ListObjects(context.Background(), bucketToList, minio.ListObjectsOptions{}) for obj := range objCh { if obj.Err != nil { - if err != nil { - log.Fatalln(err) - } + log.Fatalf("Listing error: %v", obj.Err) } - fmt.Println(obj) + fmt.Printf("Key: %s\nSize: %d\nLast Modified: %s\n===\n", obj.Key, obj.Size, obj.LastModified) } }