fix: pass security groups to load balancer

This commit is contained in:
Julian Sommer
2026-05-13 10:24:38 +10:00
parent 32ae9d190e
commit 4e5d7b0a13
2 changed files with 91 additions and 2 deletions
+10 -2
View File
@@ -611,7 +611,7 @@ func (s *ELBv2ServiceImpl) CreateLoadBalancer(input *elbv2.CreateLoadBalancerInp
vpcID := ""
if s.VPCService != nil && len(subnets) > 0 {
for _, subnetID := range subnets {
eniOut, eniErr := s.VPCService.CreateNetworkInterface(&ec2.CreateNetworkInterfaceInput{
eniIn := &ec2.CreateNetworkInterfaceInput{
SubnetId: aws.String(subnetID),
Description: aws.String(fmt.Sprintf("ELB %s/%s/%s", arnPathSegment, name, lbID)),
TagSpecifications: []*ec2.TagSpecification{
@@ -623,7 +623,15 @@ func (s *ELBv2ServiceImpl) CreateLoadBalancer(input *elbv2.CreateLoadBalancerInp
},
},
},
}, accountID)
}
// SG enforcement gates inbound traffic by the ENI's port-group
// membership, which is set at ENI creation. Empty Groups
// intentionally falls back to the VPC default SG inside
// eni.CreateNetworkInterface.
if len(securityGroups) > 0 {
eniIn.Groups = aws.StringSlice(securityGroups)
}
eniOut, eniErr := s.VPCService.CreateNetworkInterface(eniIn, accountID)
if eniErr != nil {
// Rollback: delete any ENIs already created
for _, rollbackENI := range eniIDs {
@@ -679,3 +679,84 @@ func TestCreateLoadBalancer_AMIResolverError_ReturnsServerInternal(t *testing.T)
require.NoError(t, err)
assert.Empty(t, lbs, "no LB record should be persisted when AMI resolver errors")
}
// TestCreateLoadBalancer_AttachesSpecifiedSecurityGroupsToENI verifies that
// when the caller supplies SecurityGroups on the LB input, the underlying ALB
// ENI is created with those SGs (not the VPC default SG). Without this the
// ENI's OVN port-group never gains the user's allow rules and inbound traffic
// is dropped by the SG enforcement default-deny ACL.
func TestCreateLoadBalancer_AttachesSpecifiedSecurityGroupsToENI(t *testing.T) {
svc, vpcSvc := setupTestServiceWithVPC(t)
subnets, err := vpcSvc.DescribeSubnets(&ec2.DescribeSubnetsInput{}, testAccountID)
require.NoError(t, err)
require.NotEmpty(t, subnets.Subnets)
subnetID := *subnets.Subnets[0].SubnetId
vpcID := *subnets.Subnets[0].VpcId
sgOut, err := vpcSvc.CreateSecurityGroup(&ec2.CreateSecurityGroupInput{
GroupName: aws.String("alb-sg"),
Description: aws.String("ALB ingress"),
VpcId: aws.String(vpcID),
}, testAccountID)
require.NoError(t, err)
sgID := *sgOut.GroupId
_, err = svc.CreateLoadBalancer(&elbv2.CreateLoadBalancerInput{
Name: aws.String("sg-attach-alb"),
Subnets: []*string{aws.String(subnetID)},
SecurityGroups: []*string{aws.String(sgID)},
}, testAccountID)
require.NoError(t, err)
eniDesc, err := vpcSvc.DescribeNetworkInterfaces(&ec2.DescribeNetworkInterfacesInput{}, testAccountID)
require.NoError(t, err)
var albENI *ec2.NetworkInterface
for _, eni := range eniDesc.NetworkInterfaces {
if eni.RequesterManaged != nil && *eni.RequesterManaged {
albENI = eni
break
}
}
require.NotNil(t, albENI, "ELB-managed ENI must be created")
require.Len(t, albENI.Groups, 1, "ALB ENI must join exactly the specified SG, not the default SG")
assert.Equal(t, sgID, *albENI.Groups[0].GroupId)
}
// TestCreateLoadBalancer_NoSecurityGroupsFallsBackToDefault preserves the
// AWS-semantics fallback: an ALB created without SecurityGroups joins the
// VPC's default SG (so the default-deny ACL has at least one allow source).
func TestCreateLoadBalancer_NoSecurityGroupsFallsBackToDefault(t *testing.T) {
svc, vpcSvc := setupTestServiceWithVPC(t)
subnets, err := vpcSvc.DescribeSubnets(&ec2.DescribeSubnetsInput{}, testAccountID)
require.NoError(t, err)
subnetID := *subnets.Subnets[0].SubnetId
vpcID := *subnets.Subnets[0].VpcId
defaultSGID, err := vpcSvc.FindDefaultSGForVPC(testAccountID, vpcID)
require.NoError(t, err)
require.NotEmpty(t, defaultSGID, "VPC must have a default SG provisioned")
_, err = svc.CreateLoadBalancer(&elbv2.CreateLoadBalancerInput{
Name: aws.String("no-sg-alb"),
Subnets: []*string{aws.String(subnetID)},
}, testAccountID)
require.NoError(t, err)
eniDesc, err := vpcSvc.DescribeNetworkInterfaces(&ec2.DescribeNetworkInterfacesInput{}, testAccountID)
require.NoError(t, err)
var albENI *ec2.NetworkInterface
for _, eni := range eniDesc.NetworkInterfaces {
if eni.RequesterManaged != nil && *eni.RequesterManaged {
albENI = eni
break
}
}
require.NotNil(t, albENI)
require.Len(t, albENI.Groups, 1)
assert.Equal(t, defaultSGID, *albENI.Groups[0].GroupId)
}