mirror of https://github.com/kubernetes/kops.git
Add support for extensible IAM permissions
This commit is contained in:
parent
b04cd5a9f5
commit
13ac2d49d3
|
@ -0,0 +1,103 @@
|
|||
# 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 Permissions
|
||||
|
||||
Sometimes you may need to extend the kops IAM roles to add additional permissions. You can do this
|
||||
through the `additionalMasterPermissions` and `additionalNodePermissions` spec fields. For instance, let's say you want
|
||||
to add DynamoDB and Elasicsearch permissions to your nodes.
|
||||
|
||||
Edit your cluster via `kops edit cluster ${CLUSTER_NAME}` and add the following to the spec:
|
||||
|
||||
```
|
||||
additionalNodePermissions: |
|
||||
[
|
||||
{
|
||||
"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
|
||||
additionalNodePermissions: |
|
||||
[
|
||||
{
|
||||
"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,11 @@ type ClusterSpec struct {
|
|||
// missing: default policy (currently OS security upgrades that do not require a reboot)
|
||||
UpdatePolicy *string `json:"updatePolicy,omitempty"`
|
||||
|
||||
// Additional permissions to add to the master role
|
||||
AdditionalMasterPermissions *string `json:"additionalMasterPermissions,omitempty"`
|
||||
// Additional permissions to add to the node role
|
||||
AdditionalNodePermissions *string `json:"additionalNodePermissions,omitempty"`
|
||||
|
||||
//HairpinMode string `json:",omitempty"`
|
||||
//
|
||||
//OpencontrailTag string `json:",omitempty"`
|
||||
|
|
|
@ -134,6 +134,11 @@ type ClusterSpec struct {
|
|||
// missing: default policy (currently OS security upgrades that do not require a reboot)
|
||||
UpdatePolicy *string `json:"updatePolicy,omitempty"`
|
||||
|
||||
// Additional permissions to add to the master role
|
||||
AdditionalMasterPermissions *string `json:"additionalMasterPermissions,omitempty"`
|
||||
// Additional permissions to add to the node role
|
||||
AdditionalNodePermissions *string `json:"additionalNodePermissions,omitempty"`
|
||||
|
||||
//HairpinMode string `json:",omitempty"`
|
||||
//
|
||||
//OpencontrailTag string `json:",omitempty"`
|
||||
|
|
|
@ -134,6 +134,11 @@ type ClusterSpec struct {
|
|||
// missing: default policy (currently OS security upgrades that do not require a reboot)
|
||||
UpdatePolicy *string `json:"updatePolicy,omitempty"`
|
||||
|
||||
// Additional permissions to add to the master role
|
||||
AdditionalMasterPermissions *string `json:"additionalMasterPermissions,omitempty"`
|
||||
// Additional permissions to add to the node role
|
||||
AdditionalNodePermissions *string `json:"additionalNodePermissions,omitempty"`
|
||||
|
||||
// EtcdClusters stores the configuration for each cluster
|
||||
EtcdClusters []*EtcdClusterSpec `json:"etcdClusters,omitempty"`
|
||||
|
||||
|
|
|
@ -100,6 +100,12 @@ func (b *IAMPolicyBuilder) BuildAWSIAMPolicy() (*IAMPolicy, error) {
|
|||
Action: []string{"route53:*"},
|
||||
Resource: []string{"*"},
|
||||
})
|
||||
|
||||
if b.Cluster.Spec.AdditionalNodePermissions != nil {
|
||||
additionalNodePermissions := make([]*IAMStatement, 0)
|
||||
json.Unmarshal([]byte(*b.Cluster.Spec.AdditionalNodePermissions), &additionalNodePermissions)
|
||||
p.Statement = append(p.Statement, additionalNodePermissions...)
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
|
@ -166,6 +172,12 @@ func (b *IAMPolicyBuilder) BuildAWSIAMPolicy() (*IAMPolicy, error) {
|
|||
Resource: kmsKeyIDs.List(),
|
||||
})
|
||||
}
|
||||
|
||||
if b.Cluster.Spec.AdditionalMasterPermissions != nil {
|
||||
additionalMasterPermissions := make([]*IAMStatement, 0)
|
||||
json.Unmarshal([]byte(*b.Cluster.Spec.AdditionalMasterPermissions), &additionalMasterPermissions)
|
||||
p.Statement = append(p.Statement, additionalMasterPermissions...)
|
||||
}
|
||||
}
|
||||
|
||||
// For S3 IAM permissions, we grant permissions to subtrees. So find the parents;
|
||||
|
|
|
@ -0,0 +1,110 @@
|
|||
/*
|
||||
Copyright 2016 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package cloudup
|
||||
|
||||
import (
|
||||
api "k8s.io/kops/pkg/apis/kops"
|
||||
"testing"
|
||||
"strings"
|
||||
"k8s.io/kops/upup/pkg/fi"
|
||||
"k8s.io/kops/pkg/model/iam"
|
||||
"regexp"
|
||||
"log"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
func TestBuildIAMPolicy_AdditionalMasterPermissions(t *testing.T) {
|
||||
|
||||
c := buildCluster(nil)
|
||||
additionalMasterPermissions := `{
|
||||
"Effect": "Allow",
|
||||
"Action": ["dynamodb:*"],
|
||||
"Resource": ["*"]
|
||||
}`
|
||||
|
||||
c.Spec.AdditionalMasterPermissions = fi.String(fmt.Sprintf("[%s]", additionalMasterPermissions))
|
||||
|
||||
iamPolicyBuilder := &iam.IAMPolicyBuilder{
|
||||
Cluster: c,
|
||||
Role: api.InstanceGroupRoleMaster,
|
||||
Region: "us-east-1",
|
||||
|
||||
}
|
||||
|
||||
iamPolicy, err := iamPolicyBuilder.BuildAWSIAMPolicy()
|
||||
if err != nil {
|
||||
t.Fatalf("BuildAWSIAMPolicy error: %v", err)
|
||||
}
|
||||
|
||||
json, err := iamPolicy.AsJSON()
|
||||
if err != nil {
|
||||
t.Fatalf("Error marshaling IAM policy: %v", err)
|
||||
}
|
||||
|
||||
reg, err := regexp.Compile("\\s")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
jsonWhitespaceLess := reg.ReplaceAllString(json, "")
|
||||
additionalMasterWhiteSpaceLess := reg.ReplaceAllString(additionalMasterPermissions, "")
|
||||
if !strings.Contains(jsonWhitespaceLess, additionalMasterWhiteSpaceLess) {
|
||||
t.Fatalf("IAM Policy did not contain additionalMasterPermissions: %v and %v", jsonWhitespaceLess, additionalMasterWhiteSpaceLess)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestBuildIAMPolicy_AdditionalNodePermissions(t *testing.T) {
|
||||
|
||||
c := buildCluster(nil)
|
||||
additionalNodePermissions := `{
|
||||
"Effect": "Allow",
|
||||
"Action": ["es:*"],
|
||||
"Resource": ["*"]
|
||||
}`
|
||||
|
||||
c.Spec.AdditionalNodePermissions = fi.String(fmt.Sprintf("[%s]", additionalNodePermissions))
|
||||
|
||||
iamPolicyBuilder := &iam.IAMPolicyBuilder{
|
||||
Cluster: c,
|
||||
Role: api.InstanceGroupRoleNode,
|
||||
Region: "us-west-2",
|
||||
|
||||
}
|
||||
|
||||
iamPolicy, err := iamPolicyBuilder.BuildAWSIAMPolicy()
|
||||
if err != nil {
|
||||
t.Fatalf("BuildAWSIAMPolicy error: %v", err)
|
||||
}
|
||||
|
||||
json, err := iamPolicy.AsJSON()
|
||||
if err != nil {
|
||||
t.Fatalf("Error marshaling IAM policy: %v", err)
|
||||
}
|
||||
|
||||
reg, err := regexp.Compile("\\s")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
jsonWhitespaceLess := reg.ReplaceAllString(json, "")
|
||||
additionalNodeWhiteSpaceLess := reg.ReplaceAllString(additionalNodePermissions, "")
|
||||
if !strings.Contains(jsonWhitespaceLess, additionalNodeWhiteSpaceLess) {
|
||||
t.Fatalf("IAM Policy did not contain additionalNodePermissions: %v and %v", jsonWhitespaceLess, additionalNodeWhiteSpaceLess)
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in New Issue