Validate external IAM policies

This commit is contained in:
Ciprian Hacman 2020-11-11 09:03:56 +02:00
parent 4ef7ee1cb9
commit 4579a1bcdc
4 changed files with 45 additions and 19 deletions

View File

@ -248,7 +248,7 @@ func validateInstanceProfile(v *kops.IAMProfileSpec, fldPath *field.Path) field.
if v != nil && v.Profile != nil { if v != nil && v.Profile != nil {
instanceProfileARN := *v.Profile instanceProfileARN := *v.Profile
parsedARN, err := arn.Parse(instanceProfileARN) parsedARN, err := arn.Parse(instanceProfileARN)
if err != nil || !strings.HasPrefix(parsedARN.Resource, "instance-profile") { if err != nil || !strings.HasPrefix(parsedARN.Resource, "instance-profile/") {
allErrs = append(allErrs, field.Invalid(fldPath.Child("profile"), instanceProfileARN, allErrs = append(allErrs, field.Invalid(fldPath.Child("profile"), instanceProfileARN,
"Instance Group IAM Instance Profile must be a valid aws arn such as arn:aws:iam::123456789012:instance-profile/KopsExampleRole")) "Instance Group IAM Instance Profile must be a valid aws arn such as arn:aws:iam::123456789012:instance-profile/KopsExampleRole"))
} }

View File

@ -23,21 +23,21 @@ import (
"regexp" "regexp"
"strings" "strings"
"github.com/aws/aws-sdk-go/aws/arn"
"github.com/blang/semver/v4" "github.com/blang/semver/v4"
"golang.org/x/net/ipv4" "golang.org/x/net/ipv4"
"golang.org/x/net/ipv6" "golang.org/x/net/ipv6"
"k8s.io/apimachinery/pkg/util/intstr"
"k8s.io/kops/pkg/featureflag"
"k8s.io/kops/upup/pkg/fi"
"k8s.io/apimachinery/pkg/api/validation" "k8s.io/apimachinery/pkg/api/validation"
"k8s.io/apimachinery/pkg/util/intstr"
utilnet "k8s.io/apimachinery/pkg/util/net" utilnet "k8s.io/apimachinery/pkg/util/net"
"k8s.io/apimachinery/pkg/util/sets" "k8s.io/apimachinery/pkg/util/sets"
utilvalidation "k8s.io/apimachinery/pkg/util/validation" utilvalidation "k8s.io/apimachinery/pkg/util/validation"
"k8s.io/apimachinery/pkg/util/validation/field" "k8s.io/apimachinery/pkg/util/validation/field"
"k8s.io/kops/pkg/apis/kops" "k8s.io/kops/pkg/apis/kops"
"k8s.io/kops/pkg/featureflag"
"k8s.io/kops/pkg/model/components" "k8s.io/kops/pkg/model/components"
"k8s.io/kops/pkg/model/iam" "k8s.io/kops/pkg/model/iam"
"k8s.io/kops/upup/pkg/fi"
) )
func newValidateCluster(cluster *kops.Cluster) field.ErrorList { func newValidateCluster(cluster *kops.Cluster) field.ErrorList {
@ -158,12 +158,18 @@ func validateClusterSpec(spec *kops.ClusterSpec, c *kops.Cluster, fieldPath *fie
allErrs = append(allErrs, validateNodeTerminationHandler(c, spec.NodeTerminationHandler, fieldPath.Child("nodeTerminationHandler"))...) allErrs = append(allErrs, validateNodeTerminationHandler(c, spec.NodeTerminationHandler, fieldPath.Child("nodeTerminationHandler"))...)
} }
// IAM additionalPolicies // IAM additional policies
if spec.AdditionalPolicies != nil { if spec.AdditionalPolicies != nil {
for k, v := range *spec.AdditionalPolicies { for k, v := range *spec.AdditionalPolicies {
allErrs = append(allErrs, validateAdditionalPolicy(k, v, fieldPath.Child("additionalPolicies"))...) allErrs = append(allErrs, validateAdditionalPolicy(k, v, fieldPath.Child("additionalPolicies"))...)
} }
} }
// IAM external policies
if spec.ExternalPolicies != nil {
for k, v := range *spec.ExternalPolicies {
allErrs = append(allErrs, validateExternalPolicies(k, v, fieldPath.Child("externalPolicies"))...)
}
}
// EtcdClusters // EtcdClusters
{ {
@ -795,31 +801,51 @@ func validateNetworkingGCE(c *kops.ClusterSpec, v *kops.GCENetworkingSpec, fldPa
} }
func validateAdditionalPolicy(role string, policy string, fldPath *field.Path) field.ErrorList { func validateAdditionalPolicy(role string, policy string, fldPath *field.Path) field.ErrorList {
errs := field.ErrorList{} allErrs := field.ErrorList{}
var valid []string var valid []string
for _, r := range kops.AllInstanceGroupRoles { for _, r := range kops.AllInstanceGroupRoles {
valid = append(valid, strings.ToLower(string(r))) valid = append(valid, strings.ToLower(string(r)))
} }
errs = append(errs, IsValidValue(fldPath, &role, valid)...) allErrs = append(allErrs, IsValidValue(fldPath, &role, valid)...)
statements, err := iam.ParseStatements(policy) statements, err := iam.ParseStatements(policy)
if err != nil { if err != nil {
errs = append(errs, field.Invalid(fldPath.Key(role), policy, "policy was not valid JSON: "+err.Error())) allErrs = append(allErrs, field.Invalid(fldPath.Key(role), policy, "policy was not valid JSON: "+err.Error()))
} }
// Trivial validation of policy, mostly to make sure it isn't some other random object // Trivial validation of policy, mostly to make sure it isn't some other random object
for i, statement := range statements { for i, statement := range statements {
fldEffect := fldPath.Key(role).Index(i).Child("Effect") fldEffect := fldPath.Key(role).Index(i).Child("Effect")
if statement.Effect == "" { if statement.Effect == "" {
errs = append(errs, field.Required(fldEffect, "Effect must be specified for IAM policy")) allErrs = append(allErrs, field.Required(fldEffect, "Effect must be specified for IAM policy"))
} else { } else {
value := string(statement.Effect) value := string(statement.Effect)
errs = append(errs, IsValidValue(fldEffect, &value, []string{"Allow", "Deny"})...) allErrs = append(allErrs, IsValidValue(fldEffect, &value, []string{"Allow", "Deny"})...)
} }
} }
return errs return allErrs
}
func validateExternalPolicies(role string, policies []string, fldPath *field.Path) field.ErrorList {
allErrs := field.ErrorList{}
var valid []string
for _, r := range kops.AllInstanceGroupRoles {
valid = append(valid, strings.ToLower(string(r)))
}
allErrs = append(allErrs, IsValidValue(fldPath, &role, valid)...)
for _, policy := range policies {
parsedARN, err := arn.Parse(policy)
if err != nil || !strings.HasPrefix(parsedARN.Resource, "policy/") {
allErrs = append(allErrs, field.Invalid(fldPath.Child(role), policy,
"Policy must be a valid AWS ARN such as arn:aws:iam::123456789012:policy/KopsExamplePolicy"))
}
}
return allErrs
} }
func validateEtcdClusterSpec(spec kops.EtcdClusterSpec, c *kops.Cluster, fieldPath *field.Path) field.ErrorList { func validateEtcdClusterSpec(spec kops.EtcdClusterSpec, c *kops.Cluster, fieldPath *field.Path) field.ErrorList {

View File

@ -50,11 +50,11 @@ spec:
nodes: public nodes: public
externalPolicies: externalPolicies:
node: node:
- aws:arn:iam:123456789000:policy:test-policy - arn:aws:iam::123456789000:policy/test-policy
master: master:
- aws:arn:iam:123456789000:policy:test-policy - arn:aws:iam::123456789000:policy/test-policy
bastion: bastion:
- aws:arn:iam:123456789000:policy:test-policy - arn:aws:iam::123456789000:policy/test-policy
subnets: subnets:
- cidr: 172.20.32.0/19 - cidr: 172.20.32.0/19
name: us-test-1a name: us-test-1a

View File

@ -268,13 +268,13 @@ resource "aws_iam_instance_profile" "nodes-externalpolicies-example-com" {
role = aws_iam_role.nodes-externalpolicies-example-com.name role = aws_iam_role.nodes-externalpolicies-example-com.name
} }
resource "aws_iam_role_policy_attachment" "master-policyoverride-1178482798" { resource "aws_iam_role_policy_attachment" "master-policyoverride-1242070525" {
policy_arn = "aws:arn:iam:123456789000:policy:test-policy" policy_arn = "arn:aws:iam::123456789000:policy/test-policy"
role = aws_iam_role.masters-externalpolicies-example-com.name role = aws_iam_role.masters-externalpolicies-example-com.name
} }
resource "aws_iam_role_policy_attachment" "node-policyoverride-1178482798" { resource "aws_iam_role_policy_attachment" "node-policyoverride-1242070525" {
policy_arn = "aws:arn:iam:123456789000:policy:test-policy" policy_arn = "arn:aws:iam::123456789000:policy/test-policy"
role = aws_iam_role.nodes-externalpolicies-example-com.name role = aws_iam_role.nodes-externalpolicies-example-com.name
} }