diff --git a/Makefile b/Makefile index 5f045584..6c34c559 100644 --- a/Makefile +++ b/Makefile @@ -75,12 +75,16 @@ IAMCTL_OUTPUT_DIR ?= ./pkg/controllers/awsloadbalancercontroller # Generated file name. IAMCTL_OUTPUT_FILE ?= iam_policy.go +IAMCTL_OUTPUT_MINIFY_FILE ?= iam_policy_minify.go + # Go Package of the generated file. IAMCTL_GO_PACKAGE ?= awsloadbalancercontroller # File name of the generated CredentialsRequest CR. IAMCTL_OUTPUT_CR_FILE ?= ./hack/controller/controller-credentials-request.yaml +IAMCTL_OUTPUT_MINIFY_CR_FILE ?= ./hack/controller/controller-credentials-request-minify.yaml + # Built go binary path. IAMCTL_BINARY ?= ./bin/iamctl @@ -127,13 +131,27 @@ vet: ## Run go vet against code. .PHONY: iamctl-gen iamctl-gen: iamctl-build iam-gen - $(IAMCTL_BINARY) -i $(IAMCTL_ASSETS_DIR)/iam-policy.json -o $(IAMCTL_OUTPUT_DIR)/$(IAMCTL_OUTPUT_FILE) -p $(IAMCTL_GO_PACKAGE) -c $(IAMCTL_OUTPUT_CR_FILE) - go fmt -mod=vendor $(IAMCTL_OUTPUT_DIR)/$(IAMCTL_OUTPUT_FILE) - go vet -mod=vendor $(IAMCTL_OUTPUT_DIR)/$(IAMCTL_OUTPUT_FILE) + # generate controller's IAM policy without minify. + @# This policy is for STS clusters as it's turned into a role policy which is limited to 10240 by AWS. + $(IAMCTL_BINARY) -i $(IAMCTL_ASSETS_DIR)/iam-policy.json -o $(IAMCTL_OUTPUT_DIR)/$(IAMCTL_OUTPUT_FILE) -p $(IAMCTL_GO_PACKAGE) -c $(IAMCTL_OUTPUT_CR_FILE) -n -s + + # generate controller's IAM policy with minify. + @# This policy is for non STS clusters as it's turned into an inline policy which is limited to 2048 by AWS. + $(IAMCTL_BINARY) -i $(IAMCTL_ASSETS_DIR)/iam-policy.json -o $(IAMCTL_OUTPUT_DIR)/$(IAMCTL_OUTPUT_MINIFY_FILE) -p $(IAMCTL_GO_PACKAGE) -f GetIAMPolicyMinify -c $(IAMCTL_OUTPUT_MINIFY_CR_FILE) + + go fmt -mod=vendor $(IAMCTL_OUTPUT_DIR)/$(IAMCTL_OUTPUT_FILE) $(IAMCTL_OUTPUT_DIR)/$(IAMCTL_OUTPUT_MINIFY_FILE) + go vet -mod=vendor $(IAMCTL_OUTPUT_DIR)/$(IAMCTL_OUTPUT_FILE) $(IAMCTL_OUTPUT_DIR)/$(IAMCTL_OUTPUT_MINIFY_FILE) + + # generate operator's IAM policy. + @# The operator's policy is small enough to fit into both limits: inline and role. $(IAMCTL_BINARY) -i $(IAMCTL_ASSETS_DIR)/operator-iam-policy.json -o ./pkg/operator/$(IAMCTL_OUTPUT_FILE) -p operator -n go fmt -mod=vendor ./pkg/operator/$(IAMCTL_OUTPUT_FILE) go vet -mod=vendor ./pkg/operator/$(IAMCTL_OUTPUT_FILE) +# The operator's CredentialsRequest is the source of truth for the operator's IAM policy. +# It's required to generate IAM role for STS clusters using ccoctl (docs/prerequisites.md#option-1-using-ccoctl). +# The below rule generates a corresponding AWS IAM policy JSON which can be used in AWS CLI commands (docs/prerequisites.md#option-2-using-the-aws-cli). +# The operator's IAM policy as go code is generated from the JSON policy and used in the operator to self provision credentials at startup. .PHONY: iam-gen iam-gen: ./hack/generate-iam-from-credrequest.sh ./hack/operator-credentials-request.yaml ./hack/operator-permission-policy.json diff --git a/assets/iam-policy.json b/assets/iam-policy.json index a8d47c8b..25293bfb 100644 --- a/assets/iam-policy.json +++ b/assets/iam-policy.json @@ -177,6 +177,28 @@ "arn:aws:elasticloadbalancing:*:*:listener-rule/app/*/*/*" ] }, + { + "Effect": "Allow", + "Action": [ + "elasticloadbalancing:AddTags" + ], + "Resource": [ + "arn:aws:elasticloadbalancing:*:*:targetgroup/*/*", + "arn:aws:elasticloadbalancing:*:*:loadbalancer/net/*/*", + "arn:aws:elasticloadbalancing:*:*:loadbalancer/app/*/*" + ], + "Condition": { + "StringEquals": { + "elasticloadbalancing:CreateAction": [ + "CreateTargetGroup", + "CreateLoadBalancer" + ] + }, + "Null": { + "aws:RequestTag/elbv2.k8s.aws/cluster": "false" + } + } + }, { "Effect": "Allow", "Action": [ diff --git a/cmd/iamctl/const.go b/cmd/iamctl/const.go index c54ef364..263f57bb 100644 --- a/cmd/iamctl/const.go +++ b/cmd/iamctl/const.go @@ -6,30 +6,43 @@ package {{.Package}} import cco "github.com/openshift/cloud-credential-operator/pkg/apis/cloudcredential/v1" +{{- with .Definition }} type IAMPolicy struct { Version string Statement []cco.StatementEntry } +{{- end }} -func GetIAMPolicy() IAMPolicy { +func {{ .Function }}() IAMPolicy { return IAMPolicy{ Statement: []cco.StatementEntry{ - {{range .Statement -}} - { - Effect: {{.Effect|printf "%q"}}, - Resource: {{range .Resource}}{{printf "%q" .}}{{end}}, - PolicyCondition: cco.IAMPolicyCondition{}, + {{- range .Statement }} + { + Effect: "{{ .Effect }}", + Resource: {{ range .Resource }}"{{ . }}"{{ end }}, + PolicyCondition: cco.IAMPolicyCondition{ + {{- with .Condition }} + {{- range $key, $value := . }} + "{{ $key }}": cco.IAMPolicyConditionKeyValue{ + {{- range $innerKey, $innerValue := $value }} + "{{ $innerKey }}": {{ stringOrSlice $innerValue false }}, + {{- end }} + }, + {{- end }} + {{- end }} + }, Action: []string{ - {{range $index, $element := .Action -}} - {{.|printf "%q"}},{{printf "\n"}} - {{- end}} + {{- range .Action }} + "{{ . }}", + {{- end }} }, }, - {{end}} + {{- end }} }, } } ` + credentialsRequestTemplate = `apiVersion: cloudcredential.openshift.io/v1 kind: CredentialsRequest metadata: @@ -40,14 +53,23 @@ spec: apiVersion: cloudcredential.openshift.io/v1 kind: AWSProviderSpec statementEntries: - {{range .Statement -}} + {{- range .Statement }} - action: - {{range $index, $element := .Action}}- {{$element}} - {{end -}} - - effect: {{.Effect}} + {{- range .Action }} + - {{ . }} + {{- end }} + effect: {{ .Effect }} resource: {{range .Resource}}{{printf "%q" .}}{{end}} - {{- end}} + {{- with .Condition }} + policyCondition: + {{- range $key, $value := . }} + "{{ $key }}": + {{- range $innerKey, $innerValue := $value }} + "{{ $innerKey }}": {{ stringOrSlice $innerValue true }} + {{- end }} + {{- end }} + {{- end }} + {{- end }} secretRef: name: aws-load-balancer-controller-cluster namespace: aws-load-balancer-operator diff --git a/cmd/iamctl/main.go b/cmd/iamctl/main.go index 5cdd30eb..de146cae 100644 --- a/cmd/iamctl/main.go +++ b/cmd/iamctl/main.go @@ -6,6 +6,10 @@ import ( "github.com/spf13/cobra" ) +const ( + defaultFunction = "GetIAMPolicy" +) + var ( // input file specifies the location for the input json. inputFile string @@ -19,8 +23,14 @@ var ( // pkg specifies the package with which the code is generated. pkg string + // function specifies the function name with which the code is generated. + function string + // skipMinify specifies whether the minification of the AWS policy has to be skipped. skipMinify bool + + // splitResource splits IAM policy's statement into many with one resource per statement. + splitResource bool ) // rootCmd represents the base command when called without any subcommands @@ -32,7 +42,7 @@ var rootCmd = &cobra.Command{ Also it can produce a CredentialsRequest YAML file which can provision the secret for the controller. `, Run: func(cmd *cobra.Command, args []string) { - generateIAMPolicy(inputFile, outputFile, outputCRFile, pkg) + generateIAMPolicy(inputFile, outputFile, outputCRFile, pkg, function) }, } @@ -56,8 +66,12 @@ func init() { rootCmd.PersistentFlags().StringVarP(&outputCRFile, "output-cr-file", "c", "", "Used to specify output CredentialsRequest YAML file path.") - rootCmd.PersistentFlags().StringVarP(&pkg, "package", "p", "main", "Used to specify output Go file path.") + rootCmd.PersistentFlags().StringVarP(&pkg, "package", "p", "main", "Used to specify the Go package in the output file.") _ = rootCmd.MarkPersistentFlagRequired("package") + rootCmd.PersistentFlags().StringVarP(&function, "function", "f", defaultFunction, "Used to specify the Go function name in the output file.") + rootCmd.PersistentFlags().BoolVarP(&skipMinify, "no-minify", "n", false, "Used to skip the minification of the output AWS policy.") + + rootCmd.PersistentFlags().BoolVarP(&splitResource, "split-resource", "s", false, "Used to split AWS policy's statement into many with one resource per statement.") } diff --git a/cmd/iamctl/policy.go b/cmd/iamctl/policy.go index bb4907b6..03ebc275 100644 --- a/cmd/iamctl/policy.go +++ b/cmd/iamctl/policy.go @@ -45,7 +45,7 @@ func (v *AWSValue) UnmarshalJSON(input []byte) error { type iamPolicyCondition map[string]iamPolicyConditionKeyValue -type iamPolicyConditionKeyValue map[string]string +type iamPolicyConditionKeyValue map[string]interface{} // compressionPrefixes defines a list of action prefixes in the policy // that are going to be compressed using wildcards. @@ -54,15 +54,34 @@ var compressionPrefixes = map[string]string{ "elasticloadbalancing:Describe": "elasticloadbalancing:Describe*", } -func generateIAMPolicy(inputFile, output, outputCR, pkg string) { - generateIAMPolicyFromTemplate(filetemplate, inputFile, output, pkg) +func generateIAMPolicy(inputFile, output, outputCR, pkg, function string) { + generateIAMPolicyFromTemplate(filetemplate, inputFile, output, pkg, function) if outputCR != "" { - generateIAMPolicyFromTemplate(credentialsRequestTemplate, inputFile, outputCR, pkg) + generateIAMPolicyFromTemplate(credentialsRequestTemplate, inputFile, outputCR, pkg, function) } } -func generateIAMPolicyFromTemplate(filetemplate string, inputFile, output, pkg string) { - tmpl, err := template.New("").Parse(filetemplate) +func generateIAMPolicyFromTemplate(filetemplate string, inputFile, output, pkg, function string) { + funcMap := template.FuncMap{ + "stringOrSlice": func(value interface{}, yaml bool) string { + if values, slice := value.([]interface{}); slice { + result := "" + for i, v := range values { + if i > 0 { + result += "," + } + result += fmt.Sprintf("%q", v) + } + if yaml { + return "[" + result + "]" + } + return "[]string{" + result + "}" + } + return fmt.Sprintf("%q", value) + }, + } + + tmpl, err := template.New("").Funcs(funcMap).Parse(filetemplate) if err != nil { panic(err) } @@ -79,6 +98,12 @@ func generateIAMPolicyFromTemplate(filetemplate string, inputFile, output, pkg s panic(fmt.Errorf("failed to parse policy JSON %v", err)) } + if splitResource { + // Splitting policy statement into many with one resource per statement + // because credentials request's resource is not a slice. + policy = split(policy) + } + if !skipMinify { // Minifying here as a workaround for current limitations // in credential requests length (2048 max bytes). @@ -92,9 +117,16 @@ func generateIAMPolicyFromTemplate(filetemplate string, inputFile, output, pkg s var in bytes.Buffer err = tmpl.Execute(&in, struct { - Package string - Statement []policyStatement - }{Package: pkg, Statement: policy.Statement}) + Package string + Function string + Definition bool + Statement []policyStatement + }{ + Package: pkg, + Function: function, + Definition: function == defaultFunction, + Statement: policy.Statement, + }) if err != nil { panic(err) } @@ -148,3 +180,27 @@ func minify(policy iamPolicy) iamPolicy { } return miniPolicy } + +func split(policy iamPolicy) iamPolicy { + var splitPolicy iamPolicy + splitPolicy.Version = policy.Version + + for _, statement := range policy.Statement { + if len(statement.Resource) > 1 { + newStatements := []policyStatement{} + for _, resource := range statement.Resource { + newStatement := policyStatement{ + Effect: statement.Effect, + Action: statement.Action, + Resource: AWSValue{resource}, + Condition: statement.Condition, + } + newStatements = append(newStatements, newStatement) + } + splitPolicy.Statement = append(splitPolicy.Statement, newStatements...) + } else { + splitPolicy.Statement = append(splitPolicy.Statement, statement) + } + } + return splitPolicy +} diff --git a/hack/controller/controller-credentials-request-minify.yaml b/hack/controller/controller-credentials-request-minify.yaml new file mode 100644 index 00000000..d3ffc878 --- /dev/null +++ b/hack/controller/controller-credentials-request-minify.yaml @@ -0,0 +1,68 @@ +apiVersion: cloudcredential.openshift.io/v1 +kind: CredentialsRequest +metadata: + name: aws-load-balancer-controller + namespace: openshift-cloud-credential-operator +spec: + providerSpec: + apiVersion: cloudcredential.openshift.io/v1 + kind: AWSProviderSpec + statementEntries: + - action: + - acm:DescribeCertificate + - acm:ListCertificates + - cognito-idp:DescribeUserPoolClient + - ec2:AuthorizeSecurityGroupIngress + - ec2:CreateSecurityGroup + - ec2:CreateTags + - ec2:DeleteSecurityGroup + - ec2:DeleteTags + - ec2:Describe* + - ec2:GetCoipPoolUsage + - ec2:RevokeSecurityGroupIngress + - elasticloadbalancing:AddListenerCertificates + - elasticloadbalancing:AddTags + - elasticloadbalancing:CreateListener + - elasticloadbalancing:CreateLoadBalancer + - elasticloadbalancing:CreateRule + - elasticloadbalancing:CreateTargetGroup + - elasticloadbalancing:DeleteListener + - elasticloadbalancing:DeleteLoadBalancer + - elasticloadbalancing:DeleteRule + - elasticloadbalancing:DeleteTargetGroup + - elasticloadbalancing:DeregisterTargets + - elasticloadbalancing:Describe* + - elasticloadbalancing:ModifyListener + - elasticloadbalancing:ModifyLoadBalancerAttributes + - elasticloadbalancing:ModifyRule + - elasticloadbalancing:ModifyTargetGroup + - elasticloadbalancing:ModifyTargetGroupAttributes + - elasticloadbalancing:RegisterTargets + - elasticloadbalancing:RemoveListenerCertificates + - elasticloadbalancing:RemoveTags + - elasticloadbalancing:SetIpAddressType + - elasticloadbalancing:SetSecurityGroups + - elasticloadbalancing:SetSubnets + - elasticloadbalancing:SetWebAcl + - iam:CreateServiceLinkedRole + - iam:GetServerCertificate + - iam:ListServerCertificates + - shield:CreateProtection + - shield:DeleteProtection + - shield:DescribeProtection + - shield:GetSubscriptionState + - waf-regional:AssociateWebACL + - waf-regional:DisassociateWebACL + - waf-regional:GetWebACL + - waf-regional:GetWebACLForResource + - wafv2:AssociateWebACL + - wafv2:DisassociateWebACL + - wafv2:GetWebACL + - wafv2:GetWebACLForResource + effect: Allow + resource: "*" + secretRef: + name: aws-load-balancer-controller-cluster + namespace: aws-load-balancer-operator + serviceAccountNames: + - aws-load-balancer-controller-cluster diff --git a/hack/controller/controller-credentials-request.yaml b/hack/controller/controller-credentials-request.yaml index d3ffc878..8c9f5567 100644 --- a/hack/controller/controller-credentials-request.yaml +++ b/hack/controller/controller-credentials-request.yaml @@ -9,56 +9,208 @@ spec: kind: AWSProviderSpec statementEntries: - action: - - acm:DescribeCertificate - - acm:ListCertificates + - iam:CreateServiceLinkedRole + effect: Allow + resource: "*" + policyCondition: + "StringEquals": + "iam:AWSServiceName": "elasticloadbalancing.amazonaws.com" + - action: + - ec2:DescribeAccountAttributes + - ec2:DescribeAddresses + - ec2:DescribeAvailabilityZones + - ec2:DescribeInternetGateways + - ec2:DescribeVpcs + - ec2:DescribeVpcPeeringConnections + - ec2:DescribeSubnets + - ec2:DescribeSecurityGroups + - ec2:DescribeInstances + - ec2:DescribeNetworkInterfaces + - ec2:DescribeTags + - ec2:GetCoipPoolUsage + - ec2:DescribeCoipPools + - elasticloadbalancing:DescribeLoadBalancers + - elasticloadbalancing:DescribeLoadBalancerAttributes + - elasticloadbalancing:DescribeListeners + - elasticloadbalancing:DescribeListenerCertificates + - elasticloadbalancing:DescribeSSLPolicies + - elasticloadbalancing:DescribeRules + - elasticloadbalancing:DescribeTargetGroups + - elasticloadbalancing:DescribeTargetGroupAttributes + - elasticloadbalancing:DescribeTargetHealth + - elasticloadbalancing:DescribeTags + effect: Allow + resource: "*" + - action: - cognito-idp:DescribeUserPoolClient + - acm:ListCertificates + - acm:DescribeCertificate + - iam:ListServerCertificates + - iam:GetServerCertificate + - waf-regional:GetWebACL + - waf-regional:GetWebACLForResource + - waf-regional:AssociateWebACL + - waf-regional:DisassociateWebACL + - wafv2:GetWebACL + - wafv2:GetWebACLForResource + - wafv2:AssociateWebACL + - wafv2:DisassociateWebACL + - shield:GetSubscriptionState + - shield:DescribeProtection + - shield:CreateProtection + - shield:DeleteProtection + effect: Allow + resource: "*" + - action: - ec2:AuthorizeSecurityGroupIngress + - ec2:RevokeSecurityGroupIngress + effect: Allow + resource: "*" + - action: - ec2:CreateSecurityGroup + effect: Allow + resource: "*" + - action: + - ec2:CreateTags + effect: Allow + resource: "arn:aws:ec2:*:*:security-group/*" + policyCondition: + "Null": + "aws:RequestTag/elbv2.k8s.aws/cluster": "false" + "StringEquals": + "ec2:CreateAction": "CreateSecurityGroup" + - action: - ec2:CreateTags - - ec2:DeleteSecurityGroup - ec2:DeleteTags - - ec2:Describe* - - ec2:GetCoipPoolUsage + effect: Allow + resource: "arn:aws:ec2:*:*:security-group/*" + policyCondition: + "Null": + "aws:RequestTag/elbv2.k8s.aws/cluster": "true" + "aws:ResourceTag/elbv2.k8s.aws/cluster": "false" + - action: + - ec2:AuthorizeSecurityGroupIngress - ec2:RevokeSecurityGroupIngress - - elasticloadbalancing:AddListenerCertificates - - elasticloadbalancing:AddTags - - elasticloadbalancing:CreateListener + - ec2:DeleteSecurityGroup + effect: Allow + resource: "*" + policyCondition: + "Null": + "aws:ResourceTag/elbv2.k8s.aws/cluster": "false" + - action: - elasticloadbalancing:CreateLoadBalancer - - elasticloadbalancing:CreateRule - elasticloadbalancing:CreateTargetGroup + effect: Allow + resource: "*" + policyCondition: + "Null": + "aws:RequestTag/elbv2.k8s.aws/cluster": "false" + - action: + - elasticloadbalancing:CreateListener - elasticloadbalancing:DeleteListener - - elasticloadbalancing:DeleteLoadBalancer + - elasticloadbalancing:CreateRule - elasticloadbalancing:DeleteRule - - elasticloadbalancing:DeleteTargetGroup - - elasticloadbalancing:DeregisterTargets - - elasticloadbalancing:Describe* - - elasticloadbalancing:ModifyListener - - elasticloadbalancing:ModifyLoadBalancerAttributes - - elasticloadbalancing:ModifyRule - - elasticloadbalancing:ModifyTargetGroup - - elasticloadbalancing:ModifyTargetGroupAttributes - - elasticloadbalancing:RegisterTargets - - elasticloadbalancing:RemoveListenerCertificates + effect: Allow + resource: "*" + - action: + - elasticloadbalancing:AddTags + - elasticloadbalancing:RemoveTags + effect: Allow + resource: "arn:aws:elasticloadbalancing:*:*:targetgroup/*/*" + policyCondition: + "Null": + "aws:RequestTag/elbv2.k8s.aws/cluster": "true" + "aws:ResourceTag/elbv2.k8s.aws/cluster": "false" + - action: + - elasticloadbalancing:AddTags + - elasticloadbalancing:RemoveTags + effect: Allow + resource: "arn:aws:elasticloadbalancing:*:*:loadbalancer/net/*/*" + policyCondition: + "Null": + "aws:RequestTag/elbv2.k8s.aws/cluster": "true" + "aws:ResourceTag/elbv2.k8s.aws/cluster": "false" + - action: + - elasticloadbalancing:AddTags + - elasticloadbalancing:RemoveTags + effect: Allow + resource: "arn:aws:elasticloadbalancing:*:*:loadbalancer/app/*/*" + policyCondition: + "Null": + "aws:RequestTag/elbv2.k8s.aws/cluster": "true" + "aws:ResourceTag/elbv2.k8s.aws/cluster": "false" + - action: + - elasticloadbalancing:AddTags + - elasticloadbalancing:RemoveTags + effect: Allow + resource: "arn:aws:elasticloadbalancing:*:*:listener/net/*/*/*" + - action: + - elasticloadbalancing:AddTags + - elasticloadbalancing:RemoveTags + effect: Allow + resource: "arn:aws:elasticloadbalancing:*:*:listener/app/*/*/*" + - action: + - elasticloadbalancing:AddTags + - elasticloadbalancing:RemoveTags + effect: Allow + resource: "arn:aws:elasticloadbalancing:*:*:listener-rule/net/*/*/*" + - action: + - elasticloadbalancing:AddTags - elasticloadbalancing:RemoveTags + effect: Allow + resource: "arn:aws:elasticloadbalancing:*:*:listener-rule/app/*/*/*" + - action: + - elasticloadbalancing:AddTags + effect: Allow + resource: "arn:aws:elasticloadbalancing:*:*:targetgroup/*/*" + policyCondition: + "Null": + "aws:RequestTag/elbv2.k8s.aws/cluster": "false" + "StringEquals": + "elasticloadbalancing:CreateAction": ["CreateTargetGroup","CreateLoadBalancer"] + - action: + - elasticloadbalancing:AddTags + effect: Allow + resource: "arn:aws:elasticloadbalancing:*:*:loadbalancer/net/*/*" + policyCondition: + "Null": + "aws:RequestTag/elbv2.k8s.aws/cluster": "false" + "StringEquals": + "elasticloadbalancing:CreateAction": ["CreateTargetGroup","CreateLoadBalancer"] + - action: + - elasticloadbalancing:AddTags + effect: Allow + resource: "arn:aws:elasticloadbalancing:*:*:loadbalancer/app/*/*" + policyCondition: + "Null": + "aws:RequestTag/elbv2.k8s.aws/cluster": "false" + "StringEquals": + "elasticloadbalancing:CreateAction": ["CreateTargetGroup","CreateLoadBalancer"] + - action: + - elasticloadbalancing:ModifyLoadBalancerAttributes - elasticloadbalancing:SetIpAddressType - elasticloadbalancing:SetSecurityGroups - elasticloadbalancing:SetSubnets + - elasticloadbalancing:DeleteLoadBalancer + - elasticloadbalancing:ModifyTargetGroup + - elasticloadbalancing:ModifyTargetGroupAttributes + - elasticloadbalancing:DeleteTargetGroup + effect: Allow + resource: "*" + policyCondition: + "Null": + "aws:ResourceTag/elbv2.k8s.aws/cluster": "false" + - action: + - elasticloadbalancing:RegisterTargets + - elasticloadbalancing:DeregisterTargets + effect: Allow + resource: "arn:aws:elasticloadbalancing:*:*:targetgroup/*/*" + - action: - elasticloadbalancing:SetWebAcl - - iam:CreateServiceLinkedRole - - iam:GetServerCertificate - - iam:ListServerCertificates - - shield:CreateProtection - - shield:DeleteProtection - - shield:DescribeProtection - - shield:GetSubscriptionState - - waf-regional:AssociateWebACL - - waf-regional:DisassociateWebACL - - waf-regional:GetWebACL - - waf-regional:GetWebACLForResource - - wafv2:AssociateWebACL - - wafv2:DisassociateWebACL - - wafv2:GetWebACL - - wafv2:GetWebACLForResource + - elasticloadbalancing:ModifyListener + - elasticloadbalancing:AddListenerCertificates + - elasticloadbalancing:RemoveListenerCertificates + - elasticloadbalancing:ModifyRule effect: Allow resource: "*" secretRef: diff --git a/pkg/controllers/awsloadbalancercontroller/credentials_request.go b/pkg/controllers/awsloadbalancercontroller/credentials_request.go index 5eb2bb98..6c32526b 100644 --- a/pkg/controllers/awsloadbalancercontroller/credentials_request.go +++ b/pkg/controllers/awsloadbalancercontroller/credentials_request.go @@ -142,7 +142,7 @@ func desiredCredentialsRequest(name types.NamespacedName, secretRef corev1.Objec func createProviderConfig(codec *cco.ProviderCodec, config *albo.AWSLoadBalancerCredentialsRequestConfig) (*runtime.RawExtension, error) { providerSpec := &cco.AWSProviderSpec{ - StatementEntries: GetIAMPolicy().Statement, + StatementEntries: GetIAMPolicyMinify().Statement, } if config != nil && config.STSIAMRoleARN != "" { providerSpec.STSIAMRoleARN = config.STSIAMRoleARN diff --git a/pkg/controllers/awsloadbalancercontroller/iam_policy.go b/pkg/controllers/awsloadbalancercontroller/iam_policy.go index 13007e87..0b778927 100644 --- a/pkg/controllers/awsloadbalancercontroller/iam_policy.go +++ b/pkg/controllers/awsloadbalancercontroller/iam_policy.go @@ -10,61 +10,317 @@ type IAMPolicy struct { func GetIAMPolicy() IAMPolicy { return IAMPolicy{ Statement: []cco.StatementEntry{ + { + Effect: "Allow", + Resource: "*", + PolicyCondition: cco.IAMPolicyCondition{ + "StringEquals": cco.IAMPolicyConditionKeyValue{ + "iam:AWSServiceName": "elasticloadbalancing.amazonaws.com", + }, + }, + Action: []string{ + "iam:CreateServiceLinkedRole", + }, + }, + { + Effect: "Allow", + Resource: "*", + PolicyCondition: cco.IAMPolicyCondition{}, + Action: []string{ + "ec2:DescribeAccountAttributes", + "ec2:DescribeAddresses", + "ec2:DescribeAvailabilityZones", + "ec2:DescribeInternetGateways", + "ec2:DescribeVpcs", + "ec2:DescribeVpcPeeringConnections", + "ec2:DescribeSubnets", + "ec2:DescribeSecurityGroups", + "ec2:DescribeInstances", + "ec2:DescribeNetworkInterfaces", + "ec2:DescribeTags", + "ec2:GetCoipPoolUsage", + "ec2:DescribeCoipPools", + "elasticloadbalancing:DescribeLoadBalancers", + "elasticloadbalancing:DescribeLoadBalancerAttributes", + "elasticloadbalancing:DescribeListeners", + "elasticloadbalancing:DescribeListenerCertificates", + "elasticloadbalancing:DescribeSSLPolicies", + "elasticloadbalancing:DescribeRules", + "elasticloadbalancing:DescribeTargetGroups", + "elasticloadbalancing:DescribeTargetGroupAttributes", + "elasticloadbalancing:DescribeTargetHealth", + "elasticloadbalancing:DescribeTags", + }, + }, { Effect: "Allow", Resource: "*", PolicyCondition: cco.IAMPolicyCondition{}, Action: []string{ - "acm:DescribeCertificate", - "acm:ListCertificates", "cognito-idp:DescribeUserPoolClient", + "acm:ListCertificates", + "acm:DescribeCertificate", + "iam:ListServerCertificates", + "iam:GetServerCertificate", + "waf-regional:GetWebACL", + "waf-regional:GetWebACLForResource", + "waf-regional:AssociateWebACL", + "waf-regional:DisassociateWebACL", + "wafv2:GetWebACL", + "wafv2:GetWebACLForResource", + "wafv2:AssociateWebACL", + "wafv2:DisassociateWebACL", + "shield:GetSubscriptionState", + "shield:DescribeProtection", + "shield:CreateProtection", + "shield:DeleteProtection", + }, + }, + { + Effect: "Allow", + Resource: "*", + PolicyCondition: cco.IAMPolicyCondition{}, + Action: []string{ "ec2:AuthorizeSecurityGroupIngress", + "ec2:RevokeSecurityGroupIngress", + }, + }, + { + Effect: "Allow", + Resource: "*", + PolicyCondition: cco.IAMPolicyCondition{}, + Action: []string{ "ec2:CreateSecurityGroup", + }, + }, + { + Effect: "Allow", + Resource: "arn:aws:ec2:*:*:security-group/*", + PolicyCondition: cco.IAMPolicyCondition{ + "Null": cco.IAMPolicyConditionKeyValue{ + "aws:RequestTag/elbv2.k8s.aws/cluster": "false", + }, + "StringEquals": cco.IAMPolicyConditionKeyValue{ + "ec2:CreateAction": "CreateSecurityGroup", + }, + }, + Action: []string{ + "ec2:CreateTags", + }, + }, + { + Effect: "Allow", + Resource: "arn:aws:ec2:*:*:security-group/*", + PolicyCondition: cco.IAMPolicyCondition{ + "Null": cco.IAMPolicyConditionKeyValue{ + "aws:RequestTag/elbv2.k8s.aws/cluster": "true", + "aws:ResourceTag/elbv2.k8s.aws/cluster": "false", + }, + }, + Action: []string{ "ec2:CreateTags", - "ec2:DeleteSecurityGroup", "ec2:DeleteTags", - "ec2:Describe*", - "ec2:GetCoipPoolUsage", + }, + }, + { + Effect: "Allow", + Resource: "*", + PolicyCondition: cco.IAMPolicyCondition{ + "Null": cco.IAMPolicyConditionKeyValue{ + "aws:ResourceTag/elbv2.k8s.aws/cluster": "false", + }, + }, + Action: []string{ + "ec2:AuthorizeSecurityGroupIngress", "ec2:RevokeSecurityGroupIngress", - "elasticloadbalancing:AddListenerCertificates", - "elasticloadbalancing:AddTags", - "elasticloadbalancing:CreateListener", + "ec2:DeleteSecurityGroup", + }, + }, + { + Effect: "Allow", + Resource: "*", + PolicyCondition: cco.IAMPolicyCondition{ + "Null": cco.IAMPolicyConditionKeyValue{ + "aws:RequestTag/elbv2.k8s.aws/cluster": "false", + }, + }, + Action: []string{ "elasticloadbalancing:CreateLoadBalancer", - "elasticloadbalancing:CreateRule", "elasticloadbalancing:CreateTargetGroup", + }, + }, + { + Effect: "Allow", + Resource: "*", + PolicyCondition: cco.IAMPolicyCondition{}, + Action: []string{ + "elasticloadbalancing:CreateListener", "elasticloadbalancing:DeleteListener", - "elasticloadbalancing:DeleteLoadBalancer", + "elasticloadbalancing:CreateRule", "elasticloadbalancing:DeleteRule", - "elasticloadbalancing:DeleteTargetGroup", - "elasticloadbalancing:DeregisterTargets", - "elasticloadbalancing:Describe*", - "elasticloadbalancing:ModifyListener", - "elasticloadbalancing:ModifyLoadBalancerAttributes", - "elasticloadbalancing:ModifyRule", - "elasticloadbalancing:ModifyTargetGroup", - "elasticloadbalancing:ModifyTargetGroupAttributes", - "elasticloadbalancing:RegisterTargets", - "elasticloadbalancing:RemoveListenerCertificates", + }, + }, + { + Effect: "Allow", + Resource: "arn:aws:elasticloadbalancing:*:*:targetgroup/*/*", + PolicyCondition: cco.IAMPolicyCondition{ + "Null": cco.IAMPolicyConditionKeyValue{ + "aws:RequestTag/elbv2.k8s.aws/cluster": "true", + "aws:ResourceTag/elbv2.k8s.aws/cluster": "false", + }, + }, + Action: []string{ + "elasticloadbalancing:AddTags", "elasticloadbalancing:RemoveTags", + }, + }, + { + Effect: "Allow", + Resource: "arn:aws:elasticloadbalancing:*:*:loadbalancer/net/*/*", + PolicyCondition: cco.IAMPolicyCondition{ + "Null": cco.IAMPolicyConditionKeyValue{ + "aws:RequestTag/elbv2.k8s.aws/cluster": "true", + "aws:ResourceTag/elbv2.k8s.aws/cluster": "false", + }, + }, + Action: []string{ + "elasticloadbalancing:AddTags", + "elasticloadbalancing:RemoveTags", + }, + }, + { + Effect: "Allow", + Resource: "arn:aws:elasticloadbalancing:*:*:loadbalancer/app/*/*", + PolicyCondition: cco.IAMPolicyCondition{ + "Null": cco.IAMPolicyConditionKeyValue{ + "aws:RequestTag/elbv2.k8s.aws/cluster": "true", + "aws:ResourceTag/elbv2.k8s.aws/cluster": "false", + }, + }, + Action: []string{ + "elasticloadbalancing:AddTags", + "elasticloadbalancing:RemoveTags", + }, + }, + { + Effect: "Allow", + Resource: "arn:aws:elasticloadbalancing:*:*:listener/net/*/*/*", + PolicyCondition: cco.IAMPolicyCondition{}, + Action: []string{ + "elasticloadbalancing:AddTags", + "elasticloadbalancing:RemoveTags", + }, + }, + { + Effect: "Allow", + Resource: "arn:aws:elasticloadbalancing:*:*:listener/app/*/*/*", + PolicyCondition: cco.IAMPolicyCondition{}, + Action: []string{ + "elasticloadbalancing:AddTags", + "elasticloadbalancing:RemoveTags", + }, + }, + { + Effect: "Allow", + Resource: "arn:aws:elasticloadbalancing:*:*:listener-rule/net/*/*/*", + PolicyCondition: cco.IAMPolicyCondition{}, + Action: []string{ + "elasticloadbalancing:AddTags", + "elasticloadbalancing:RemoveTags", + }, + }, + { + Effect: "Allow", + Resource: "arn:aws:elasticloadbalancing:*:*:listener-rule/app/*/*/*", + PolicyCondition: cco.IAMPolicyCondition{}, + Action: []string{ + "elasticloadbalancing:AddTags", + "elasticloadbalancing:RemoveTags", + }, + }, + { + Effect: "Allow", + Resource: "arn:aws:elasticloadbalancing:*:*:targetgroup/*/*", + PolicyCondition: cco.IAMPolicyCondition{ + "Null": cco.IAMPolicyConditionKeyValue{ + "aws:RequestTag/elbv2.k8s.aws/cluster": "false", + }, + "StringEquals": cco.IAMPolicyConditionKeyValue{ + "elasticloadbalancing:CreateAction": []string{"CreateTargetGroup", "CreateLoadBalancer"}, + }, + }, + Action: []string{ + "elasticloadbalancing:AddTags", + }, + }, + { + Effect: "Allow", + Resource: "arn:aws:elasticloadbalancing:*:*:loadbalancer/net/*/*", + PolicyCondition: cco.IAMPolicyCondition{ + "Null": cco.IAMPolicyConditionKeyValue{ + "aws:RequestTag/elbv2.k8s.aws/cluster": "false", + }, + "StringEquals": cco.IAMPolicyConditionKeyValue{ + "elasticloadbalancing:CreateAction": []string{"CreateTargetGroup", "CreateLoadBalancer"}, + }, + }, + Action: []string{ + "elasticloadbalancing:AddTags", + }, + }, + { + Effect: "Allow", + Resource: "arn:aws:elasticloadbalancing:*:*:loadbalancer/app/*/*", + PolicyCondition: cco.IAMPolicyCondition{ + "Null": cco.IAMPolicyConditionKeyValue{ + "aws:RequestTag/elbv2.k8s.aws/cluster": "false", + }, + "StringEquals": cco.IAMPolicyConditionKeyValue{ + "elasticloadbalancing:CreateAction": []string{"CreateTargetGroup", "CreateLoadBalancer"}, + }, + }, + Action: []string{ + "elasticloadbalancing:AddTags", + }, + }, + { + Effect: "Allow", + Resource: "*", + PolicyCondition: cco.IAMPolicyCondition{ + "Null": cco.IAMPolicyConditionKeyValue{ + "aws:ResourceTag/elbv2.k8s.aws/cluster": "false", + }, + }, + Action: []string{ + "elasticloadbalancing:ModifyLoadBalancerAttributes", "elasticloadbalancing:SetIpAddressType", "elasticloadbalancing:SetSecurityGroups", "elasticloadbalancing:SetSubnets", + "elasticloadbalancing:DeleteLoadBalancer", + "elasticloadbalancing:ModifyTargetGroup", + "elasticloadbalancing:ModifyTargetGroupAttributes", + "elasticloadbalancing:DeleteTargetGroup", + }, + }, + { + Effect: "Allow", + Resource: "arn:aws:elasticloadbalancing:*:*:targetgroup/*/*", + PolicyCondition: cco.IAMPolicyCondition{}, + Action: []string{ + "elasticloadbalancing:RegisterTargets", + "elasticloadbalancing:DeregisterTargets", + }, + }, + { + Effect: "Allow", + Resource: "*", + PolicyCondition: cco.IAMPolicyCondition{}, + Action: []string{ "elasticloadbalancing:SetWebAcl", - "iam:CreateServiceLinkedRole", - "iam:GetServerCertificate", - "iam:ListServerCertificates", - "shield:CreateProtection", - "shield:DeleteProtection", - "shield:DescribeProtection", - "shield:GetSubscriptionState", - "waf-regional:AssociateWebACL", - "waf-regional:DisassociateWebACL", - "waf-regional:GetWebACL", - "waf-regional:GetWebACLForResource", - "wafv2:AssociateWebACL", - "wafv2:DisassociateWebACL", - "wafv2:GetWebACL", - "wafv2:GetWebACLForResource", + "elasticloadbalancing:ModifyListener", + "elasticloadbalancing:AddListenerCertificates", + "elasticloadbalancing:RemoveListenerCertificates", + "elasticloadbalancing:ModifyRule", }, }, }, diff --git a/pkg/controllers/awsloadbalancercontroller/iam_policy_minify.go b/pkg/controllers/awsloadbalancercontroller/iam_policy_minify.go new file mode 100644 index 00000000..6c8163b7 --- /dev/null +++ b/pkg/controllers/awsloadbalancercontroller/iam_policy_minify.go @@ -0,0 +1,67 @@ +package awsloadbalancercontroller + +import cco "github.com/openshift/cloud-credential-operator/pkg/apis/cloudcredential/v1" + +func GetIAMPolicyMinify() IAMPolicy { + return IAMPolicy{ + Statement: []cco.StatementEntry{ + { + Effect: "Allow", + Resource: "*", + PolicyCondition: cco.IAMPolicyCondition{}, + Action: []string{ + "acm:DescribeCertificate", + "acm:ListCertificates", + "cognito-idp:DescribeUserPoolClient", + "ec2:AuthorizeSecurityGroupIngress", + "ec2:CreateSecurityGroup", + "ec2:CreateTags", + "ec2:DeleteSecurityGroup", + "ec2:DeleteTags", + "ec2:Describe*", + "ec2:GetCoipPoolUsage", + "ec2:RevokeSecurityGroupIngress", + "elasticloadbalancing:AddListenerCertificates", + "elasticloadbalancing:AddTags", + "elasticloadbalancing:CreateListener", + "elasticloadbalancing:CreateLoadBalancer", + "elasticloadbalancing:CreateRule", + "elasticloadbalancing:CreateTargetGroup", + "elasticloadbalancing:DeleteListener", + "elasticloadbalancing:DeleteLoadBalancer", + "elasticloadbalancing:DeleteRule", + "elasticloadbalancing:DeleteTargetGroup", + "elasticloadbalancing:DeregisterTargets", + "elasticloadbalancing:Describe*", + "elasticloadbalancing:ModifyListener", + "elasticloadbalancing:ModifyLoadBalancerAttributes", + "elasticloadbalancing:ModifyRule", + "elasticloadbalancing:ModifyTargetGroup", + "elasticloadbalancing:ModifyTargetGroupAttributes", + "elasticloadbalancing:RegisterTargets", + "elasticloadbalancing:RemoveListenerCertificates", + "elasticloadbalancing:RemoveTags", + "elasticloadbalancing:SetIpAddressType", + "elasticloadbalancing:SetSecurityGroups", + "elasticloadbalancing:SetSubnets", + "elasticloadbalancing:SetWebAcl", + "iam:CreateServiceLinkedRole", + "iam:GetServerCertificate", + "iam:ListServerCertificates", + "shield:CreateProtection", + "shield:DeleteProtection", + "shield:DescribeProtection", + "shield:GetSubscriptionState", + "waf-regional:AssociateWebACL", + "waf-regional:DisassociateWebACL", + "waf-regional:GetWebACL", + "waf-regional:GetWebACLForResource", + "wafv2:AssociateWebACL", + "wafv2:DisassociateWebACL", + "wafv2:GetWebACL", + "wafv2:GetWebACLForResource", + }, + }, + }, + } +}