Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow using existing/shared Security Groups #5744

Merged
merged 3 commits into from
Oct 2, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
65 changes: 33 additions & 32 deletions cmd/kops/integration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,15 +52,15 @@ const updateClusterTestBase = "../../tests/integration/update_cluster/"

// TestMinimal runs the test on a minimum configuration, similar to kops create cluster minimal.example.com --zones us-west-1a
func TestMinimal(t *testing.T) {
runTestAWS(t, "minimal.example.com", "minimal", "v1alpha0", false, 1, true)
runTestAWS(t, "minimal.example.com", "minimal", "v1alpha1", false, 1, true)
runTestAWS(t, "minimal.example.com", "minimal", "v1alpha2", false, 1, true)
runTestAWS(t, "minimal.example.com", "minimal", "v1alpha0", false, 1, true, nil)
runTestAWS(t, "minimal.example.com", "minimal", "v1alpha1", false, 1, true, nil)
runTestAWS(t, "minimal.example.com", "minimal", "v1alpha2", false, 1, true, nil)
}

// TestHA runs the test on a simple HA configuration, similar to kops create cluster minimal.example.com --zones us-west-1a,us-west-1b,us-west-1c --master-count=3
func TestHA(t *testing.T) {
runTestAWS(t, "ha.example.com", "ha", "v1alpha1", false, 3, true)
runTestAWS(t, "ha.example.com", "ha", "v1alpha2", false, 3, true)
runTestAWS(t, "ha.example.com", "ha", "v1alpha1", false, 3, true, nil)
runTestAWS(t, "ha.example.com", "ha", "v1alpha2", false, 3, true, nil)
}

// TestHighAvailabilityGCE runs the test on a simple HA GCE configuration, similar to kops create cluster ha-gce.example.com
Expand All @@ -71,7 +71,7 @@ func TestHighAvailabilityGCE(t *testing.T) {

// TestComplex runs the test on a more complex configuration, intended to hit more of the edge cases
func TestComplex(t *testing.T) {
runTestAWS(t, "complex.example.com", "complex", "v1alpha2", false, 1, true)
runTestAWS(t, "complex.example.com", "complex", "v1alpha2", false, 1, true, nil)
}

// TestMinimalCloudformation runs the test on a minimum configuration, similar to kops create cluster minimal.example.com --zones us-west-1a
Expand All @@ -85,78 +85,85 @@ func TestExistingIAMCloudformation(t *testing.T) {
runTestCloudformation(t, "minimal.example.com", "existing_iam_cloudformation", "v1alpha2", false, lifecycleOverrides)
}

// TestExistingSG runs the test with existing Security Group, similar to kops create cluster minimal.example.com --zones us-west-1a
func TestExistingSG(t *testing.T) {
lifecycleOverrides := []string{"SecurityGroup=ExistsAndWarnIfChanges", "SecurityGroupRule=ExistsAndWarnIfChanges"}
runTestAWS(t, "existingsg.example.com", "existing_sg", "v1alpha2", false, 3, true, lifecycleOverrides)
}

// TestAdditionalUserData runs the test on passing additional user-data to an instance at bootstrap.
func TestAdditionalUserData(t *testing.T) {
runTestCloudformation(t, "additionaluserdata.example.com", "additional_user-data", "v1alpha2", false, nil)
}

// TestBastionAdditionalUserData runs the test on passing additional user-data to a bastion instance group
func TestBastionAdditionalUserData(t *testing.T) {
runTestAWS(t, "bastionuserdata.example.com", "bastionadditional_user-data", "v1alpha2", true, 1, true)
runTestAWS(t, "bastionuserdata.example.com", "bastionadditional_user-data", "v1alpha2", true, 1, true, nil)
}

// TestMinimal_141 runs the test on a configuration from 1.4.1 release
func TestMinimal_141(t *testing.T) {
runTestAWS(t, "minimal-141.example.com", "minimal-141", "v1alpha0", false, 1, true)
runTestAWS(t, "minimal-141.example.com", "minimal-141", "v1alpha0", false, 1, true, nil)
}

// TestPrivateWeave runs the test on a configuration with private topology, weave networking
func TestPrivateWeave(t *testing.T) {
runTestAWS(t, "privateweave.example.com", "privateweave", "v1alpha1", true, 1, true)
runTestAWS(t, "privateweave.example.com", "privateweave", "v1alpha2", true, 1, true)
runTestAWS(t, "privateweave.example.com", "privateweave", "v1alpha1", true, 1, true, nil)
runTestAWS(t, "privateweave.example.com", "privateweave", "v1alpha2", true, 1, true, nil)
}

// TestPrivateFlannel runs the test on a configuration with private topology, flannel networking
func TestPrivateFlannel(t *testing.T) {
runTestAWS(t, "privateflannel.example.com", "privateflannel", "v1alpha1", true, 1, true)
runTestAWS(t, "privateflannel.example.com", "privateflannel", "v1alpha2", true, 1, true)
runTestAWS(t, "privateflannel.example.com", "privateflannel", "v1alpha1", true, 1, true, nil)
runTestAWS(t, "privateflannel.example.com", "privateflannel", "v1alpha2", true, 1, true, nil)
}

// TestPrivateCalico runs the test on a configuration with private topology, calico networking
func TestPrivateCalico(t *testing.T) {
runTestAWS(t, "privatecalico.example.com", "privatecalico", "v1alpha1", true, 1, true)
runTestAWS(t, "privatecalico.example.com", "privatecalico", "v1alpha2", true, 1, true)
runTestAWS(t, "privatecalico.example.com", "privatecalico", "v1alpha1", true, 1, true, nil)
runTestAWS(t, "privatecalico.example.com", "privatecalico", "v1alpha2", true, 1, true, nil)
}

// TestPrivateCanal runs the test on a configuration with private topology, canal networking
func TestPrivateCanal(t *testing.T) {
runTestAWS(t, "privatecanal.example.com", "privatecanal", "v1alpha1", true, 1, true)
runTestAWS(t, "privatecanal.example.com", "privatecanal", "v1alpha2", true, 1, true)
runTestAWS(t, "privatecanal.example.com", "privatecanal", "v1alpha1", true, 1, true, nil)
runTestAWS(t, "privatecanal.example.com", "privatecanal", "v1alpha2", true, 1, true, nil)
}

// TestPrivateKopeio runs the test on a configuration with private topology, kopeio networking
func TestPrivateKopeio(t *testing.T) {
runTestAWS(t, "privatekopeio.example.com", "privatekopeio", "v1alpha2", true, 1, true)
runTestAWS(t, "privatekopeio.example.com", "privatekopeio", "v1alpha2", true, 1, true, nil)
}

// TestPrivateSharedSubnet runs the test on a configuration with private topology & shared subnets
func TestPrivateSharedSubnet(t *testing.T) {
runTestAWS(t, "private-shared-subnet.example.com", "private-shared-subnet", "v1alpha2", true, 1, true)
runTestAWS(t, "private-shared-subnet.example.com", "private-shared-subnet", "v1alpha2", true, 1, true, nil)
}

// TestPrivateDns1 runs the test on a configuration with private topology, private dns
func TestPrivateDns1(t *testing.T) {
runTestAWS(t, "privatedns1.example.com", "privatedns1", "v1alpha2", true, 1, true)
runTestAWS(t, "privatedns1.example.com", "privatedns1", "v1alpha2", true, 1, true, nil)
}

// TestPrivateDns2 runs the test on a configuration with private topology, private dns, extant vpc
func TestPrivateDns2(t *testing.T) {
runTestAWS(t, "privatedns2.example.com", "privatedns2", "v1alpha2", true, 1, true)
runTestAWS(t, "privatedns2.example.com", "privatedns2", "v1alpha2", true, 1, true, nil)
}

// TestSharedSubnet runs the test on a configuration with a shared subnet (and VPC)
func TestSharedSubnet(t *testing.T) {
runTestAWS(t, "sharedsubnet.example.com", "shared_subnet", "v1alpha2", false, 1, true)
runTestAWS(t, "sharedsubnet.example.com", "shared_subnet", "v1alpha2", false, 1, true, nil)
}

// TestSharedVPC runs the test on a configuration with a shared VPC
func TestSharedVPC(t *testing.T) {
runTestAWS(t, "sharedvpc.example.com", "shared_vpc", "v1alpha2", false, 1, true)
runTestAWS(t, "sharedvpc.example.com", "shared_vpc", "v1alpha2", false, 1, true, nil)
}

// TestExistingIAM runs the test on a configuration with existing IAM instance profiles
func TestExistingIAM(t *testing.T) {
runTestAWS(t, "existing-iam.example.com", "existing_iam", "v1alpha2", false, 3, false)
lifecycleOverrides := []string{"IAMRole=ExistsAndWarnIfChanges", "IAMRolePolicy=ExistsAndWarnIfChanges", "IAMInstanceProfileRole=ExistsAndWarnIfChanges"}
runTestAWS(t, "existing-iam.example.com", "existing_iam", "v1alpha2", false, 3, false, lifecycleOverrides)
}

// TestAdditionalCIDR runs the test on a configuration with a shared VPC
Expand All @@ -170,7 +177,7 @@ func TestPhaseNetwork(t *testing.T) {
}

func TestExternalLoadBalancer(t *testing.T) {
runTestAWS(t, "externallb.example.com", "externallb", "v1alpha2", false, 1, true)
runTestAWS(t, "externallb.example.com", "externallb", "v1alpha2", false, 1, true, nil)
runTestCloudformation(t, "externallb.example.com", "externallb", "v1alpha2", false, nil)
}

Expand Down Expand Up @@ -354,7 +361,7 @@ func runTest(t *testing.T, h *testutils.IntegrationTestHarness, clusterName stri
}
}

func runTestAWS(t *testing.T, clusterName string, srcDir string, version string, private bool, zones int, expectPolicies bool) {
func runTestAWS(t *testing.T, clusterName string, srcDir string, version string, private bool, zones int, expectPolicies bool, lifecycleOverrides []string) {
h := testutils.NewIntegrationTestHarness(t)
defer h.Close()

Expand All @@ -372,13 +379,6 @@ func runTestAWS(t *testing.T, clusterName string, srcDir string, version string,
expectedFilenames = append(expectedFilenames, s)
}

lifecycleOverrides := []string{}
if !expectPolicies {
lifecycleOverrides = append(lifecycleOverrides, "IAMRole=Ignore")
lifecycleOverrides = append(lifecycleOverrides, "IAMRolePolicy=Ignore")
lifecycleOverrides = append(lifecycleOverrides, "IAMInstanceProfileRole=Ignore")
}

if expectPolicies {
expectedFilenames = append(expectedFilenames, []string{
"aws_iam_role_masters." + clusterName + "_policy",
Expand All @@ -396,6 +396,7 @@ func runTestAWS(t *testing.T, clusterName string, srcDir string, version string,
}...)
}
}

// Special case that tests a bastion with user-data
if srcDir == "bastionadditional_user-data" {
expectedFilenames = append(expectedFilenames, "aws_launch_configuration_bastion."+clusterName+"_user_data")
Expand Down
55 changes: 55 additions & 0 deletions docs/security_groups.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
# Security Groups

## Use existing AWS Security Groups
**Note: Use this at your own risk, when existing SGs are used Kops will NOT ensure they are properly configured.**
rdrgmnzs marked this conversation as resolved.
Show resolved Hide resolved
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I do hope we can relax this (ExistsAndWarnIfChanges should warn!), but we'll see :-)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep, this already happens. The issue is that this displays a compressed object on the terminal and unless you are looking for it, it can be easy to miss. Hence the note here, however I'm fine with removing the note as well.


Rather than having Kops create and manage IAM Security Groups, it is possible to use an existing one. This is useful in organizations where security policies prevent tools from creating their own Security Groups.
Kops will still output any differences in the managed and your own Security Groups.
This is convenient for determining policy changes that need to be made when upgrading Kops.
**Using Managed Security Groups will not output these differences, it is up to the user to track expected changes to policies.**

rdrgmnzs marked this conversation as resolved.
Show resolved Hide resolved
*NOTE: Currently Kops only supports using existing Security Groups for every instance group and Load Balancer in the Cluster, not a mix of existing and managed Security Groups.
This is due to the lifecycle overrides being used to prevent creation of the Security Groups related resources.*

To do this first specify the Security Groups for the ELB (if you are using a LB) and Instance Groups
Example:
```yaml
apiVersion: kops/v1alpha2
kind: Cluster
metadata:
creationTimestamp: "2016-12-10T22:42:27Z"
name: mycluster.example.com
spec:
api:
loadBalancer:
securityGroupOverride: sg-abcd1234

.
.
.

apiVersion: kops/v1alpha2
kind: InstanceGroup
metadata:
creationTimestamp: "2017-01-01T00:00:00Z"
labels:
kops.k8s.io/cluster: mycluster.example.com
name: master-us-test-1a
spec:
securityGroupOverride: sg-1234dcba

```

Now run a cluster update to create the new launch configuration, using [lifecycle overrides](./cli/kops_update_cluster.md#options) to prevent Security Group resources from being created:

```
kops update cluster ${CLUSTER_NAME} --yes --lifecycle-overrides SecurityGroup=ExistsAndWarnIfChanges,SecurityGroupRule=ExistsAndWarnIfChanges
```

*Everytime `kops update cluster` is ran, it must include the above `--lifecycle-overrides`.*

Then perform a rolling update in order to replace EC2 instances in the ASG with the new launch configuration:

```
kops rolling-update cluster ${CLUSTER_NAME} --yes
```
17 changes: 12 additions & 5 deletions pkg/apis/kops/cluster.go
Original file line number Diff line number Diff line change
Expand Up @@ -315,11 +315,18 @@ const (

// LoadBalancerAccessSpec provides configuration details related to API LoadBalancer and its access
type LoadBalancerAccessSpec struct {
Type LoadBalancerType `json:"type,omitempty"`
IdleTimeoutSeconds *int64 `json:"idleTimeoutSeconds,omitempty"`
AdditionalSecurityGroups []string `json:"additionalSecurityGroups,omitempty"`
UseForInternalApi bool `json:"useForInternalApi,omitempty"`
SSLCertificate string `json:"sslCertificate,omitempty"`
// Type of load balancer to create may Public or Internal.
Type LoadBalancerType `json:"type,omitempty"`
// IdleTimeoutSeconds sets the timeout of the api loadbalancer.
IdleTimeoutSeconds *int64 `json:"idleTimeoutSeconds,omitempty"`
// SecurityGroupOverride overrides the default Kops created SG for the load balancer.
SecurityGroupOverride *string `json:"securityGroupOverride,omitempty"`
// AdditionalSecurityGroups attaches additional security groups (e.g. sg-123456).
AdditionalSecurityGroups []string `json:"additionalSecurityGroups,omitempty"`
// UseForInternalApi indicates wether the LB should be used by the kubelet
UseForInternalApi bool `json:"useForInternalApi,omitempty"`
// SSLCertificate allows you to specify the ACM cert to be used the the LB
SSLCertificate string `json:"sslCertificate,omitempty"`
}

// KubeDNSConfig defines the kube dns configuration
Expand Down
2 changes: 2 additions & 0 deletions pkg/apis/kops/instancegroup.go
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,8 @@ type InstanceGroupSpec struct {
DetailedInstanceMonitoring *bool `json:"detailedInstanceMonitoring,omitempty"`
// IAMProfileSpec defines the identity of the cloud group iam profile (AWS only).
IAM *IAMProfileSpec `json:"iam,omitempty"`
// SecurityGroupOverride overrides the defaut security group created by Kops for this IG (AWS only).
SecurityGroupOverride *string `json:"securityGroupOverride,omitempty"`
}

// UserData defines a user-data section
Expand Down
17 changes: 12 additions & 5 deletions pkg/apis/kops/v1alpha1/cluster.go
Original file line number Diff line number Diff line change
Expand Up @@ -314,11 +314,18 @@ const (

// LoadBalancerAccessSpec provides configuration details related to API LoadBalancer and its access
type LoadBalancerAccessSpec struct {
Type LoadBalancerType `json:"type,omitempty"`
IdleTimeoutSeconds *int64 `json:"idleTimeoutSeconds,omitempty"`
AdditionalSecurityGroups []string `json:"additionalSecurityGroups,omitempty"`
UseForInternalApi bool `json:"useForInternalApi,omitempty"`
SSLCertificate string `json:"sslCertificate,omitempty"`
// Type of load balancer to create may Public or Internal.
Type LoadBalancerType `json:"type,omitempty"`
// IdleTimeoutSeconds sets the timeout of the api loadbalancer.
IdleTimeoutSeconds *int64 `json:"idleTimeoutSeconds,omitempty"`
// SecurityGroupOverride overrides the default Kops created SG for the load balancer.
SecurityGroupOverride *string `json:"securityGroupOverride,omitempty"`
// AdditionalSecurityGroups attaches additional security groups (e.g. sg-123456).
AdditionalSecurityGroups []string `json:"additionalSecurityGroups,omitempty"`
// UseForInternalApi indicates wether the LB should be used by the kubelet
UseForInternalApi bool `json:"useForInternalApi,omitempty"`
// SSLCertificate allows you to specify the ACM cert to be used the the LB
SSLCertificate string `json:"sslCertificate,omitempty"`
}

// KubeDNSConfig defines the kube dns configuration
Expand Down
2 changes: 2 additions & 0 deletions pkg/apis/kops/v1alpha1/instancegroup.go
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,8 @@ type InstanceGroupSpec struct {
DetailedInstanceMonitoring *bool `json:"detailedInstanceMonitoring,omitempty"`
// IAMProfileSpec defines the identity of the cloud group iam profile (AWS only).
IAM *IAMProfileSpec `json:"iam,omitempty"`
// SecurityGroupOverride overrides the defaut security group created by Kops for this IG (AWS only).
SecurityGroupOverride *string `json:"securityGroupOverride,omitempty"`
}

// IAMProfileSpec is the AWS IAM Profile to attach to instances in this instance
Expand Down
4 changes: 4 additions & 0 deletions pkg/apis/kops/v1alpha1/zz_generated.conversion.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

18 changes: 18 additions & 0 deletions pkg/apis/kops/v1alpha1/zz_generated.deepcopy.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

17 changes: 12 additions & 5 deletions pkg/apis/kops/v1alpha2/cluster.go
Original file line number Diff line number Diff line change
Expand Up @@ -315,11 +315,18 @@ const (

// LoadBalancerAccessSpec provides configuration details related to API LoadBalancer and its access
type LoadBalancerAccessSpec struct {
Type LoadBalancerType `json:"type,omitempty"`
IdleTimeoutSeconds *int64 `json:"idleTimeoutSeconds,omitempty"`
AdditionalSecurityGroups []string `json:"additionalSecurityGroups,omitempty"`
UseForInternalApi bool `json:"useForInternalApi,omitempty"`
SSLCertificate string `json:"sslCertificate,omitempty"`
// Type of load balancer to create may Public or Internal.
Type LoadBalancerType `json:"type,omitempty"`
// IdleTimeoutSeconds sets the timeout of the api loadbalancer.
IdleTimeoutSeconds *int64 `json:"idleTimeoutSeconds,omitempty"`
// SecurityGroupOverride overrides the default Kops created SG for the load balancer.
SecurityGroupOverride *string `json:"securityGroupOverride,omitempty"`
// AdditionalSecurityGroups attaches additional security groups (e.g. sg-123456).
AdditionalSecurityGroups []string `json:"additionalSecurityGroups,omitempty"`
// UseForInternalApi indicates wether the LB should be used by the kubelet
UseForInternalApi bool `json:"useForInternalApi,omitempty"`
// SSLCertificate allows you to specify the ACM cert to be used the the LB
SSLCertificate string `json:"sslCertificate,omitempty"`
}

// KubeDNSConfig defines the kube dns configuration
Expand Down
2 changes: 2 additions & 0 deletions pkg/apis/kops/v1alpha2/instancegroup.go
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,8 @@ type InstanceGroupSpec struct {
DetailedInstanceMonitoring *bool `json:"detailedInstanceMonitoring,omitempty"`
// IAMProfileSpec defines the identity of the cloud group iam profile (AWS only).
IAM *IAMProfileSpec `json:"iam,omitempty"`
// SecurityGroupOverride overrides the defaut security group created by Kops for this IG (AWS only).
SecurityGroupOverride *string `json:"securityGroupOverride,omitempty"`
}

// UserData defines a user-data section
Expand Down
Loading