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 {
instanceProfileARN := *v.Profile
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,
"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"
"strings"
"github.com/aws/aws-sdk-go/aws/arn"
"github.com/blang/semver/v4"
"golang.org/x/net/ipv4"
"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/util/intstr"
utilnet "k8s.io/apimachinery/pkg/util/net"
"k8s.io/apimachinery/pkg/util/sets"
utilvalidation "k8s.io/apimachinery/pkg/util/validation"
"k8s.io/apimachinery/pkg/util/validation/field"
"k8s.io/kops/pkg/apis/kops"
"k8s.io/kops/pkg/featureflag"
"k8s.io/kops/pkg/model/components"
"k8s.io/kops/pkg/model/iam"
"k8s.io/kops/upup/pkg/fi"
)
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"))...)
}
// IAM additionalPolicies
// IAM additional policies
if spec.AdditionalPolicies != nil {
for k, v := range *spec.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
{
@ -795,31 +801,51 @@ func validateNetworkingGCE(c *kops.ClusterSpec, v *kops.GCENetworkingSpec, fldPa
}
func validateAdditionalPolicy(role string, policy string, fldPath *field.Path) field.ErrorList {
errs := field.ErrorList{}
allErrs := field.ErrorList{}
var valid []string
for _, r := range kops.AllInstanceGroupRoles {
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)
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
for i, statement := range statements {
fldEffect := fldPath.Key(role).Index(i).Child("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 {
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 {

View File

@ -50,11 +50,11 @@ spec:
nodes: public
externalPolicies:
node:
- aws:arn:iam:123456789000:policy:test-policy
- arn:aws:iam::123456789000:policy/test-policy
master:
- aws:arn:iam:123456789000:policy:test-policy
- arn:aws:iam::123456789000:policy/test-policy
bastion:
- aws:arn:iam:123456789000:policy:test-policy
- arn:aws:iam::123456789000:policy/test-policy
subnets:
- cidr: 172.20.32.0/19
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
}
resource "aws_iam_role_policy_attachment" "master-policyoverride-1178482798" {
policy_arn = "aws:arn:iam:123456789000:policy:test-policy"
resource "aws_iam_role_policy_attachment" "master-policyoverride-1242070525" {
policy_arn = "arn:aws:iam::123456789000:policy/test-policy"
role = aws_iam_role.masters-externalpolicies-example-com.name
}
resource "aws_iam_role_policy_attachment" "node-policyoverride-1178482798" {
policy_arn = "aws:arn:iam:123456789000:policy:test-policy"
resource "aws_iam_role_policy_attachment" "node-policyoverride-1242070525" {
policy_arn = "arn:aws:iam::123456789000:policy/test-policy"
role = aws_iam_role.nodes-externalpolicies-example-com.name
}