mirror of https://github.com/kubernetes/kops.git
				
				
				
			Merge pull request #1170 from yissacharcw/extensible-iam-roles
Add support for extensible IAM permissions
This commit is contained in:
		
						commit
						165ead4fac
					
				|  | @ -0,0 +1,105 @@ | |||
| # IAM Roles | ||||
| 
 | ||||
| Two IAM roles are created for the cluster: one for the masters, and one for the nodes. | ||||
| The permissions are kept to the minimum required to setup and maintain the cluster. | ||||
| 
 | ||||
| Master permissions: | ||||
| 
 | ||||
| ``` | ||||
| ec2:* | ||||
| route53:* | ||||
| elasticloadbalancing:* | ||||
| ecr:GetAuthorizationToken | ||||
| ecr:BatchCheckLayerAvailability | ||||
| ecr:GetDownloadUrlForLayer | ||||
| ecr:GetRepositoryPolicy | ||||
| ecr:DescribeRepositories | ||||
| ecr:ListImages | ||||
| ecr:BatchGetImage | ||||
| 
 | ||||
| // The following permissions are only created if you are using etcd volumes with "encrypted: true" and a custom kmsKeyId. | ||||
| // They are scoped to the kmsKeyId that you are using. | ||||
| kms:Encrypt | ||||
| kms:Decrypt | ||||
| kms:ReEncrypt* | ||||
| kms:GenerateDataKey* | ||||
| kms:DescribeKey | ||||
| kms:CreateGrant | ||||
| kms:ListGrants | ||||
| kms:RevokeGrant | ||||
| ``` | ||||
| 
 | ||||
| Node permissions: | ||||
| 
 | ||||
| ``` | ||||
| ec2:Describe* | ||||
| route53:* | ||||
| ecr:GetAuthorizationToken | ||||
| ecr:BatchCheckLayerAvailability | ||||
| ecr:GetDownloadUrlForLayer | ||||
| ecr:GetRepositoryPolicy | ||||
| ecr:DescribeRepositories | ||||
| ecr:ListImages | ||||
| ecr:BatchGetImage | ||||
| ``` | ||||
| 
 | ||||
| ## Adding Additional Policies | ||||
| 
 | ||||
| Sometimes you may need to extend the kops IAM roles to add additional policies. You can do this | ||||
| through the `additionalPolicies` spec field. For instance, let's say you want | ||||
| to add DynamoDB and Elasticsearch permissions to your nodes. | ||||
| 
 | ||||
| Edit your cluster via `kops edit cluster ${CLUSTER_NAME}` and add the following to the spec: | ||||
| 
 | ||||
| ``` | ||||
|   additionalPolicies: | ||||
|     node: | | ||||
|       [ | ||||
|         { | ||||
|           "Effect": "Allow", | ||||
|           "Action": ["dynamodb:*"], | ||||
|           "Resource": ["*"] | ||||
|         }, | ||||
|         { | ||||
|           "Effect": "Allow", | ||||
|           "Action": ["es:*"], | ||||
|           "Resource": ["*"] | ||||
|         } | ||||
|       ] | ||||
| ``` | ||||
| 
 | ||||
| After you're finished editing, your cluster spec should look something like this: | ||||
| 
 | ||||
| ``` | ||||
| metadata: | ||||
|   creationTimestamp: "2016-06-27T14:23:34Z" | ||||
|   name: ${CLUSTER_NAME} | ||||
| spec: | ||||
|   cloudProvider: aws | ||||
|   networkCIDR: 10.100.0.0/16 | ||||
|   networkID: vpc-a80734c1 | ||||
|   nonMasqueradeCIDR: 100.64.0.0/10 | ||||
|   zones: | ||||
|   - cidr: 10.100.32.0/19 | ||||
|     name: eu-central-1a | ||||
|   additionalPolicies: | ||||
|     node: | | ||||
|       [ | ||||
|         { | ||||
|           "Effect": "Allow", | ||||
|           "Action": ["dynamodb:*"], | ||||
|           "Resource": ["*"] | ||||
|         }, | ||||
|         { | ||||
|           "Effect": "Allow", | ||||
|           "Action": ["es:*"], | ||||
|           "Resource": ["*"] | ||||
|         } | ||||
|       ] | ||||
| ``` | ||||
| 
 | ||||
| Now you can update to have the changes take effect: | ||||
| 
 | ||||
| ``` | ||||
| kops update cluster ${CLUSTER_NAME} --yes | ||||
| ``` | ||||
|  | @ -135,6 +135,9 @@ type ClusterSpec struct { | |||
| 	//   missing: default policy (currently OS security upgrades that do not require a reboot)
 | ||||
| 	UpdatePolicy *string `json:"updatePolicy,omitempty"` | ||||
| 
 | ||||
| 	// Additional policies to add for roles
 | ||||
| 	AdditionalPolicies *map[string]string `json:"additionalPolicies,omitempty"` | ||||
| 
 | ||||
| 	//HairpinMode                   string `json:",omitempty"`
 | ||||
| 	//
 | ||||
| 	//OpencontrailTag               string `json:",omitempty"`
 | ||||
|  |  | |||
|  | @ -134,6 +134,9 @@ type ClusterSpec struct { | |||
| 	//   missing: default policy (currently OS security upgrades that do not require a reboot)
 | ||||
| 	UpdatePolicy *string `json:"updatePolicy,omitempty"` | ||||
| 
 | ||||
| 	// Additional policies to add for roles
 | ||||
| 	AdditionalPolicies *map[string]string `json:"additionalPolicies,omitempty"` | ||||
| 
 | ||||
| 	//HairpinMode                   string `json:",omitempty"`
 | ||||
| 	//
 | ||||
| 	//OpencontrailTag               string `json:",omitempty"`
 | ||||
|  |  | |||
|  | @ -313,6 +313,7 @@ func autoConvert_v1alpha1_ClusterSpec_To_kops_ClusterSpec(in *ClusterSpec, out * | |||
| 	// WARNING: in.AdminAccess requires manual conversion: does not exist in peer-type
 | ||||
| 	out.IsolateMasters = in.IsolateMasters | ||||
| 	out.UpdatePolicy = in.UpdatePolicy | ||||
| 	out.AdditionalPolicies = in.AdditionalPolicies | ||||
| 	if in.EtcdClusters != nil { | ||||
| 		in, out := &in.EtcdClusters, &out.EtcdClusters | ||||
| 		*out = make([]*kops.EtcdClusterSpec, len(*in)) | ||||
|  | @ -449,6 +450,7 @@ func autoConvert_kops_ClusterSpec_To_v1alpha1_ClusterSpec(in *kops.ClusterSpec, | |||
| 	// WARNING: in.KubernetesAPIAccess requires manual conversion: does not exist in peer-type
 | ||||
| 	out.IsolateMasters = in.IsolateMasters | ||||
| 	out.UpdatePolicy = in.UpdatePolicy | ||||
| 	out.AdditionalPolicies = in.AdditionalPolicies | ||||
| 	if in.EtcdClusters != nil { | ||||
| 		in, out := &in.EtcdClusters, &out.EtcdClusters | ||||
| 		*out = make([]*EtcdClusterSpec, len(*in)) | ||||
|  |  | |||
|  | @ -134,6 +134,9 @@ type ClusterSpec struct { | |||
| 	//   missing: default policy (currently OS security upgrades that do not require a reboot)
 | ||||
| 	UpdatePolicy *string `json:"updatePolicy,omitempty"` | ||||
| 
 | ||||
| 	// Additional policies to add for roles
 | ||||
| 	AdditionalPolicies *map[string]string `json:"additionalPolicies,omitempty"` | ||||
| 
 | ||||
| 	// EtcdClusters stores the configuration for each cluster
 | ||||
| 	EtcdClusters []*EtcdClusterSpec `json:"etcdClusters,omitempty"` | ||||
| 
 | ||||
|  |  | |||
|  | @ -17,11 +17,14 @@ limitations under the License. | |||
| package model | ||||
| 
 | ||||
| import ( | ||||
| 	"encoding/json" | ||||
| 	"fmt" | ||||
| 	"k8s.io/kops/pkg/apis/kops" | ||||
| 	"k8s.io/kops/pkg/model/iam" | ||||
| 	"k8s.io/kops/upup/pkg/fi" | ||||
| 	"k8s.io/kops/upup/pkg/fi/cloudup/awstasks" | ||||
| 	"reflect" | ||||
| 	"strings" | ||||
| 	"text/template" | ||||
| ) | ||||
| 
 | ||||
|  | @ -64,7 +67,7 @@ func (b *IAMModelBuilder) Build(c *fi.ModelBuilderContext) error { | |||
| 
 | ||||
| 		var iamRole *awstasks.IAMRole | ||||
| 		{ | ||||
| 			rolePolicy, err := b.buildAWSIAMRolePolicy(role) | ||||
| 			rolePolicy, err := b.buildAWSIAMRolePolicy() | ||||
| 			if err != nil { | ||||
| 				return err | ||||
| 			} | ||||
|  | @ -107,6 +110,63 @@ func (b *IAMModelBuilder) Build(c *fi.ModelBuilderContext) error { | |||
| 			} | ||||
| 			c.AddTask(iamInstanceProfileRole) | ||||
| 		} | ||||
| 
 | ||||
| 		// Generate additional policies if needed, and attach to existing instance profile
 | ||||
| 		if b.Cluster.Spec.AdditionalPolicies != nil { | ||||
| 			roleAsString := reflect.ValueOf(role).String() | ||||
| 			additionalPolicies := *(b.Cluster.Spec.AdditionalPolicies) | ||||
| 
 | ||||
| 			if additionalPolicy, ok := additionalPolicies[strings.ToLower(roleAsString)]; ok { | ||||
| 				roleName := "additional." + name | ||||
| 
 | ||||
| 				var iamRole *awstasks.IAMRole | ||||
| 				{ | ||||
| 					rolePolicy, err := b.buildAWSIAMRolePolicy() | ||||
| 					if err != nil { | ||||
| 						return err | ||||
| 					} | ||||
| 
 | ||||
| 					iamRole = &awstasks.IAMRole{ | ||||
| 						Name:               s(roleName), | ||||
| 						RolePolicyDocument: fi.WrapResource(rolePolicy), | ||||
| 					} | ||||
| 					c.AddTask(iamRole) | ||||
| 
 | ||||
| 				} | ||||
| 
 | ||||
| 				{ | ||||
| 					p := &iam.IAMPolicy{ | ||||
| 						Version: iam.IAMPolicyDefaultVersion, | ||||
| 					} | ||||
| 
 | ||||
| 					statements := make([]*iam.IAMStatement, 0) | ||||
| 					json.Unmarshal([]byte(additionalPolicy), &statements) | ||||
| 					p.Statement = append(p.Statement, statements...) | ||||
| 
 | ||||
| 					policy, err := p.AsJSON() | ||||
| 					if err != nil { | ||||
| 						return fmt.Errorf("error building IAM policy: %v", err) | ||||
| 					} | ||||
| 
 | ||||
| 					t := &awstasks.IAMRolePolicy{ | ||||
| 						Name:           s(roleName), | ||||
| 						Role:           iamRole, | ||||
| 						PolicyDocument: fi.WrapResource(fi.NewStringResource(policy)), | ||||
| 					} | ||||
| 					c.AddTask(t) | ||||
| 				} | ||||
| 
 | ||||
| 				{ | ||||
| 					iamInstanceProfileRole := &awstasks.IAMInstanceProfileRole{ | ||||
| 						Name: s(roleName), | ||||
| 
 | ||||
| 						InstanceProfile: iamInstanceProfile, | ||||
| 						Role:            iamRole, | ||||
| 					} | ||||
| 					c.AddTask(iamInstanceProfileRole) | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	return nil | ||||
|  | @ -132,7 +192,7 @@ func (b *IAMModelBuilder) buildAWSIAMPolicy(role kops.InstanceGroupRole) (string | |||
| } | ||||
| 
 | ||||
| // buildAWSIAMRolePolicy produces the AWS IAM role policy for the given role
 | ||||
| func (b *IAMModelBuilder) buildAWSIAMRolePolicy(role kops.InstanceGroupRole) (fi.Resource, error) { | ||||
| func (b *IAMModelBuilder) buildAWSIAMRolePolicy() (fi.Resource, error) { | ||||
| 	functions := template.FuncMap{ | ||||
| 		"IAMServiceEC2": func() string { | ||||
| 			// IAMServiceEC2 returns the name of the IAM service for EC2 in the current region
 | ||||
|  |  | |||
|  | @ -1901,6 +1901,9 @@ func ListIAMRoles(cloud fi.Cloud, clusterName string) ([]*ResourceTracker, error | |||
| 	remove["masters."+clusterName] = true | ||||
| 	remove["nodes."+clusterName] = true | ||||
| 	remove["bastions."+clusterName] = true | ||||
| 	remove["additional.nodes."+clusterName] = true | ||||
| 	remove["additional.masters."+clusterName] = true | ||||
| 	remove["additional.bastions."+clusterName] = true | ||||
| 
 | ||||
| 	var roles []*iam.Role | ||||
| 	// Find roles matching remove map
 | ||||
|  | @ -1979,6 +1982,9 @@ func ListIAMInstanceProfiles(cloud fi.Cloud, clusterName string) ([]*ResourceTra | |||
| 	remove["masters."+clusterName] = true | ||||
| 	remove["nodes."+clusterName] = true | ||||
| 	remove["bastions."+clusterName] = true | ||||
| 	remove["additional.nodes."+clusterName] = true | ||||
| 	remove["additional.masters."+clusterName] = true | ||||
| 	remove["additional.bastions."+clusterName] = true | ||||
| 
 | ||||
| 	var profiles []*iam.InstanceProfile | ||||
| 
 | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue