-
Notifications
You must be signed in to change notification settings - Fork 9.6k
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
provider/aws: Fix EC2 Classic SG Rule issue #5533
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -24,7 +24,7 @@ func TestResourceAwsSecurityGroupIPPermGather(t *testing.T) { | |
IpRanges: []*ec2.IpRange{&ec2.IpRange{CidrIp: aws.String("0.0.0.0/0")}}, | ||
UserIdGroupPairs: []*ec2.UserIdGroupPair{ | ||
&ec2.UserIdGroupPair{ | ||
GroupId: aws.String("sg-22222"), | ||
GroupId: aws.String("sg-11111"), | ||
}, | ||
}, | ||
}, | ||
|
@@ -33,8 +33,27 @@ func TestResourceAwsSecurityGroupIPPermGather(t *testing.T) { | |
FromPort: aws.Int64(int64(80)), | ||
ToPort: aws.Int64(int64(80)), | ||
UserIdGroupPairs: []*ec2.UserIdGroupPair{ | ||
// VPC | ||
&ec2.UserIdGroupPair{ | ||
GroupId: aws.String("sg-22222"), | ||
}, | ||
}, | ||
}, | ||
&ec2.IpPermission{ | ||
IpProtocol: aws.String("tcp"), | ||
FromPort: aws.Int64(int64(443)), | ||
ToPort: aws.Int64(int64(443)), | ||
UserIdGroupPairs: []*ec2.UserIdGroupPair{ | ||
// Classic | ||
&ec2.UserIdGroupPair{ | ||
GroupId: aws.String("foo"), | ||
UserId: aws.String("12345"), | ||
GroupId: aws.String("sg-33333"), | ||
GroupName: aws.String("ec2_classic"), | ||
}, | ||
&ec2.UserIdGroupPair{ | ||
UserId: aws.String("amazon-elb"), | ||
GroupId: aws.String("sg-d2c979d3"), | ||
GroupName: aws.String("amazon-elb-sg"), | ||
}, | ||
}, | ||
}, | ||
|
@@ -53,12 +72,21 @@ func TestResourceAwsSecurityGroupIPPermGather(t *testing.T) { | |
"from_port": int64(80), | ||
"to_port": int64(80), | ||
"security_groups": schema.NewSet(schema.HashString, []interface{}{ | ||
"foo", | ||
"sg-22222", | ||
}), | ||
}, | ||
map[string]interface{}{ | ||
"protocol": "tcp", | ||
"from_port": int64(443), | ||
"to_port": int64(443), | ||
"security_groups": schema.NewSet(schema.HashString, []interface{}{ | ||
"ec2_classic", | ||
"amazon-elb/amazon-elb-sg", | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can you help me read the unit test diffs here? Trying to figure out the narrative of "when I do this, then I get this" and having trouble. Could use a bit of hand holding. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This chuck matches up with the newly added
It represents a security group in EC2 Classic with two name references, one to a group named |
||
}), | ||
}, | ||
} | ||
|
||
out := resourceAwsSecurityGroupIPPermGather("sg-22222", raw) | ||
out := resourceAwsSecurityGroupIPPermGather("sg-11111", raw, aws.String("12345")) | ||
for _, i := range out { | ||
// loop and match rules, because the ordering is not guarneteed | ||
for _, l := range local { | ||
|
@@ -636,6 +664,94 @@ func TestAccAWSSecurityGroup_CIDRandGroups(t *testing.T) { | |
}) | ||
} | ||
|
||
func TestAccAWSSecurityGroup_ingressWithCidrAndSGs(t *testing.T) { | ||
var group ec2.SecurityGroup | ||
|
||
resource.Test(t, resource.TestCase{ | ||
PreCheck: func() { testAccPreCheck(t) }, | ||
Providers: testAccProviders, | ||
CheckDestroy: testAccCheckAWSSecurityGroupDestroy, | ||
Steps: []resource.TestStep{ | ||
resource.TestStep{ | ||
Config: testAccAWSSecurityGroupConfig_ingressWithCidrAndSGs, | ||
Check: resource.ComposeTestCheckFunc( | ||
testAccCheckAWSSecurityGroupExists("aws_security_group.web", &group), | ||
testAccCheckAWSSecurityGroupSGandCidrAttributes(&group), | ||
resource.TestCheckResourceAttr( | ||
"aws_security_group.web", "name", "terraform_acceptance_test_example"), | ||
resource.TestCheckResourceAttr( | ||
"aws_security_group.web", "description", "Used in the terraform acceptance tests"), | ||
resource.TestCheckResourceAttr( | ||
"aws_security_group.web", "ingress.#", "2"), | ||
), | ||
}, | ||
}, | ||
}) | ||
} | ||
|
||
// This test requires an EC2 Classic region | ||
func TestAccAWSSecurityGroup_ingressWithCidrAndSGs_classic(t *testing.T) { | ||
var group ec2.SecurityGroup | ||
|
||
resource.Test(t, resource.TestCase{ | ||
PreCheck: func() { testAccPreCheck(t) }, | ||
Providers: testAccProviders, | ||
CheckDestroy: testAccCheckAWSSecurityGroupDestroy, | ||
Steps: []resource.TestStep{ | ||
resource.TestStep{ | ||
Config: testAccAWSSecurityGroupConfig_ingressWithCidrAndSGs_classic, | ||
Check: resource.ComposeTestCheckFunc( | ||
testAccCheckAWSSecurityGroupExists("aws_security_group.web", &group), | ||
testAccCheckAWSSecurityGroupSGandCidrAttributes(&group), | ||
resource.TestCheckResourceAttr( | ||
"aws_security_group.web", "name", "terraform_acceptance_test_example"), | ||
resource.TestCheckResourceAttr( | ||
"aws_security_group.web", "description", "Used in the terraform acceptance tests"), | ||
resource.TestCheckResourceAttr( | ||
"aws_security_group.web", "ingress.#", "2"), | ||
), | ||
}, | ||
}, | ||
}) | ||
} | ||
|
||
func testAccCheckAWSSecurityGroupSGandCidrAttributes(group *ec2.SecurityGroup) resource.TestCheckFunc { | ||
return func(s *terraform.State) error { | ||
if *group.GroupName != "terraform_acceptance_test_example" { | ||
return fmt.Errorf("Bad name: %s", *group.GroupName) | ||
} | ||
|
||
if *group.Description != "Used in the terraform acceptance tests" { | ||
return fmt.Errorf("Bad description: %s", *group.Description) | ||
} | ||
|
||
if len(group.IpPermissions) == 0 { | ||
return fmt.Errorf("No IPPerms") | ||
} | ||
|
||
if len(group.IpPermissions) != 2 { | ||
return fmt.Errorf("Expected 2 ingress rules, got %d", len(group.IpPermissions)) | ||
} | ||
|
||
for _, p := range group.IpPermissions { | ||
if *p.FromPort == int64(22) { | ||
if len(p.IpRanges) != 1 || p.UserIdGroupPairs != nil { | ||
return fmt.Errorf("Found ip perm of 22, but not the right ipranges / pairs: %s", p) | ||
} | ||
continue | ||
} else if *p.FromPort == int64(80) { | ||
if len(p.IpRanges) != 1 || len(p.UserIdGroupPairs) != 1 { | ||
return fmt.Errorf("Found ip perm of 80, but not the right ipranges / pairs: %s", p) | ||
} | ||
continue | ||
} | ||
return fmt.Errorf("Found a rouge rule") | ||
} | ||
|
||
return nil | ||
} | ||
} | ||
|
||
func testAccCheckAWSSecurityGroupAttributesChanged(group *ec2.SecurityGroup) resource.TestCheckFunc { | ||
return func(s *terraform.State) error { | ||
p := []*ec2.IpPermission{ | ||
|
@@ -1141,3 +1257,90 @@ resource "aws_security_group" "mixed" { | |
} | ||
} | ||
` | ||
|
||
const testAccAWSSecurityGroupConfig_ingressWithCidrAndSGs = ` | ||
resource "aws_security_group" "other_web" { | ||
name = "tf_other_acc_tests" | ||
description = "Used in the terraform acceptance tests" | ||
|
||
tags { | ||
Name = "tf-acc-test" | ||
} | ||
} | ||
|
||
resource "aws_security_group" "web" { | ||
name = "terraform_acceptance_test_example" | ||
description = "Used in the terraform acceptance tests" | ||
|
||
ingress { | ||
protocol = "tcp" | ||
from_port = "22" | ||
to_port = "22" | ||
|
||
cidr_blocks = [ | ||
"192.168.0.1/32", | ||
] | ||
} | ||
|
||
ingress { | ||
protocol = "tcp" | ||
from_port = 80 | ||
to_port = 8000 | ||
cidr_blocks = ["10.0.0.0/8"] | ||
security_groups = ["${aws_security_group.other_web.id}"] | ||
} | ||
|
||
egress { | ||
protocol = "tcp" | ||
from_port = 80 | ||
to_port = 8000 | ||
cidr_blocks = ["10.0.0.0/8"] | ||
} | ||
|
||
tags { | ||
Name = "tf-acc-test" | ||
} | ||
} | ||
` | ||
|
||
const testAccAWSSecurityGroupConfig_ingressWithCidrAndSGs_classic = ` | ||
provider "aws" { | ||
region = "us-east-1" | ||
} | ||
|
||
resource "aws_security_group" "other_web" { | ||
name = "tf_other_acc_tests" | ||
description = "Used in the terraform acceptance tests" | ||
|
||
tags { | ||
Name = "tf-acc-test" | ||
} | ||
} | ||
|
||
resource "aws_security_group" "web" { | ||
name = "terraform_acceptance_test_example" | ||
description = "Used in the terraform acceptance tests" | ||
|
||
ingress { | ||
protocol = "tcp" | ||
from_port = "22" | ||
to_port = "22" | ||
|
||
cidr_blocks = [ | ||
"192.168.0.1/32", | ||
] | ||
} | ||
|
||
ingress { | ||
protocol = "tcp" | ||
from_port = 80 | ||
to_port = 8000 | ||
cidr_blocks = ["10.0.0.0/8"] | ||
security_groups = ["${aws_security_group.other_web.name}"] | ||
} | ||
|
||
tags { | ||
Name = "tf-acc-test" | ||
} | ||
} | ||
` |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -137,7 +137,7 @@ func expandEcsLoadBalancers(configured []interface{}) []*ecs.LoadBalancer { | |
// to_port or from_port set to a non-zero value. | ||
func expandIPPerms( | ||
group *ec2.SecurityGroup, configured []interface{}) ([]*ec2.IpPermission, error) { | ||
vpc := group.VpcId != nil | ||
vpc := group.VpcId != nil && *group.VpcId != "" | ||
|
||
perms := make([]*ec2.IpPermission, len(configured)) | ||
for i, mRaw := range configured { | ||
|
@@ -321,11 +321,41 @@ func flattenHealthCheck(check *elb.HealthCheck) []map[string]interface{} { | |
return result | ||
} | ||
|
||
// Flattens an array of UserSecurityGroups into a []string | ||
func flattenSecurityGroups(list []*ec2.UserIdGroupPair) []string { | ||
result := make([]string, 0, len(list)) | ||
// Flattens an array of UserSecurityGroups into a []*ec2.GroupIdentifier | ||
func flattenSecurityGroups(list []*ec2.UserIdGroupPair, ownerId *string) []*ec2.GroupIdentifier { | ||
result := make([]*ec2.GroupIdentifier, 0, len(list)) | ||
for _, g := range list { | ||
result = append(result, *g.GroupId) | ||
var userId *string | ||
if g.UserId != nil && *g.UserId != "" && (ownerId == nil || *ownerId != *g.UserId) { | ||
userId = g.UserId | ||
} | ||
// userid nil here for same vpc groups | ||
|
||
vpc := g.GroupName == nil || *g.GroupName == "" | ||
var id *string | ||
if vpc { | ||
id = g.GroupId | ||
} else { | ||
id = g.GroupName | ||
} | ||
|
||
// id is groupid for vpcs | ||
// id is groupname for non vpc (classic) | ||
|
||
if userId != nil { | ||
id = aws.String(*userId + "/" + *id) | ||
} | ||
|
||
if vpc { | ||
result = append(result, &ec2.GroupIdentifier{ | ||
GroupId: id, | ||
}) | ||
} else { | ||
result = append(result, &ec2.GroupIdentifier{ | ||
GroupId: g.GroupId, | ||
GroupName: id, | ||
}) | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I see new logic here, but I don't see any unit tests covering it in There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nope, not missing it; doesn't look like There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Added in 23c42cd |
||
} | ||
return result | ||
} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This ELB thing seems sort of unrelated to the bugfix, but it's in the same category, so I'm ok leaving it in. 🙆♀️
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's somewhat related.... when using the ELB's source security group name, which you would only use in EC2 Classic, we need the full
amazon-elb/amazon-elb-sg
name to use as a reference inside another security group:Without this, we have just
amazon-elb-sg
which the API will not find.Inside a VPC, we get a default ELB security group in the format of
default_somecrypitic_id
, with the ID of that group stored insource_security_group_id
. Because we use_id
's in VPCs, this should be fine .There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Gotcha, make sense. SGTM 👍