Skip to content

Commit

Permalink
Make checkPermissionsUsingQueryClient a method of PolicyDocument
Browse files Browse the repository at this point in the history
  • Loading branch information
tbrisker committed Jun 16, 2022
1 parent 8e73563 commit 8ca362e
Show file tree
Hide file tree
Showing 2 changed files with 58 additions and 77 deletions.
78 changes: 1 addition & 77 deletions pkg/aws/permissions.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import (
"fmt"
"reflect"

"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/arn"
"github.com/aws/aws-sdk-go/service/iam"
)
Expand All @@ -27,7 +26,6 @@ func (c *awsClient) ValidateSCP(target *string, policies map[string]string) (boo
if err != nil {
return false, err
}
policyDocuments := []PolicyDocument{osdPolicyDocument}

// Get Creator details
creator, err := c.GetCreator()
Expand Down Expand Up @@ -70,7 +68,7 @@ func (c *awsClient) ValidateSCP(target *string, policies map[string]string) (boo
}

// Validate permissions
hasPermissions, err := validatePolicyDocuments(c, targetUserARN.String(), policyDocuments, sParams)
hasPermissions, err := osdPolicyDocument.checkPermissionsUsingQueryClient(c, targetUserARN.String(), sParams)
if err != nil {
return false, err
}
Expand All @@ -81,80 +79,6 @@ func (c *awsClient) ValidateSCP(target *string, policies map[string]string) (boo
return true, nil
}

// checkPermissionsUsingQueryClient will use queryClient to query whether the credentials in targetClient can perform
// the actions listed in the statementEntries. queryClient will need
// sts:GetCallerIdentity and iam:SimulatePrincipalPolicy
func checkPermissionsUsingQueryClient(queryClient *awsClient, targetUserARN string, policyDocument PolicyDocument,
params *SimulateParams) (bool, error) {
// Ignoring isRoot here since we only warn the user that its not best practice to use it.
// TODO: Add a check for isRoot in the initialize
allowList := []*string{}
for _, statement := range policyDocument.Statement {
actionArr := getActionAllowed(statement.Action)
for _, action := range actionArr {
allowList = append(allowList, aws.String(action))
}
}

input := &iam.SimulatePrincipalPolicyInput{
PolicySourceArn: aws.String(targetUserARN),
ActionNames: allowList,
ContextEntries: []*iam.ContextEntry{},
}

if params != nil {
if params.Region != "" {
input.ContextEntries = append(input.ContextEntries, &iam.ContextEntry{
ContextKeyName: aws.String("aws:RequestedRegion"),
ContextKeyType: aws.String("stringList"),
ContextKeyValues: []*string{aws.String(params.Region)},
})
}
}

// Either all actions are allowed and we'll return 'true', or it's a failure
allClear := true
// Collect all failed actions
var failedActions []string

err := queryClient.iamClient.SimulatePrincipalPolicyPages(input,
func(response *iam.SimulatePolicyResponse, lastPage bool) bool {
for _, result := range response.EvaluationResults {
if *result.EvalDecision != "allowed" {
// Don't bail out after the first failure, so we can log the full list
// of failed/denied actions
failedActions = append(failedActions, *result.EvalActionName)
allClear = false
}
}
return !lastPage
})
if err != nil {
return false, fmt.Errorf("Error simulating policy: %v", err)
}

if !allClear {
return false, fmt.Errorf("Actions not allowed with tested credentials: %v", failedActions)
}

return true, nil
}

func validatePolicyDocuments(queryClient *awsClient, targetUserARN string, policyDocuments []PolicyDocument,
sParams *SimulateParams) (bool, error) {
for _, policyDocument := range policyDocuments {
permissionsOk, err := checkPermissionsUsingQueryClient(queryClient, targetUserARN, policyDocument, sParams)
if err != nil {
return false, err
}
if !permissionsOk {
return false, fmt.Errorf("Unable to validate permissions in %s", policyDocument.ID)
}
}

return true, nil
}

func getActionAllowed(action interface{}) []string {
var actionArr []string
switch reflect.TypeOf(action).Kind() {
Expand Down
57 changes: 57 additions & 0 deletions pkg/aws/policy_document.go
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,63 @@ func (p *PolicyDocument) IsActionAllowed(wanted string) bool {
return false
}

// checkPermissionsUsingQueryClient will use queryClient to query whether the credentials in targetClient can perform
// the actions listed in the statementEntries. queryClient will need
// sts:GetCallerIdentity and iam:SimulatePrincipalPolicy
func (p *PolicyDocument) checkPermissionsUsingQueryClient(queryClient *awsClient, targetUserARN string,
params *SimulateParams) (bool, error) {
// Ignoring isRoot here since we only warn the user that its not best practice to use it.
// TODO: Add a check for isRoot in the initialize
allowList := []*string{}
for _, statement := range p.Statement {
actionArr := getActionAllowed(statement.Action)
for _, action := range actionArr {
allowList = append(allowList, aws.String(action))
}
}

input := &iam.SimulatePrincipalPolicyInput{
PolicySourceArn: aws.String(targetUserARN),
ActionNames: allowList,
ContextEntries: []*iam.ContextEntry{},
}

if params != nil && params.Region != "" {
input.ContextEntries = append(input.ContextEntries, &iam.ContextEntry{
ContextKeyName: aws.String("aws:RequestedRegion"),
ContextKeyType: aws.String("stringList"),
ContextKeyValues: []*string{aws.String(params.Region)},
})
}

// Either all actions are allowed and we'll return 'true', or it's a failure
allClear := true
// Collect all failed actions
var failedActions []string

err := queryClient.iamClient.SimulatePrincipalPolicyPages(input,
func(response *iam.SimulatePolicyResponse, lastPage bool) bool {
for _, result := range response.EvaluationResults {
if *result.EvalDecision != "allowed" {
// Don't bail out after the first failure, so we can log the full list
// of failed/denied actions
failedActions = append(failedActions, *result.EvalActionName)
allClear = false
}
}
return !lastPage
})
if err != nil {
return false, fmt.Errorf("Error simulating policy: %v", err)
}

if !allClear {
return false, fmt.Errorf("Actions not allowed with tested credentials: %v", failedActions)
}

return true, nil
}

func (p PolicyDocument) String() string {
res, err := json.Marshal(p)
if err != nil {
Expand Down

0 comments on commit 8ca362e

Please sign in to comment.