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