From 79b5b736e904c02c3d9f7ba0322d4ed0ff278120 Mon Sep 17 00:00:00 2001 From: Sanne Raymaekers Date: Mon, 4 Mar 2024 19:50:38 +0100 Subject: [PATCH] cloud/awscloud: restrict network egress for secure instance The security instance should no longer have any internet access. --- internal/cloud/awscloud/secure-instance.go | 69 +++++++++++++++++++++- 1 file changed, 68 insertions(+), 1 deletion(-) diff --git a/internal/cloud/awscloud/secure-instance.go b/internal/cloud/awscloud/secure-instance.go index c7365f101..2adfdbd79 100644 --- a/internal/cloud/awscloud/secure-instance.go +++ b/internal/cloud/awscloud/secure-instance.go @@ -277,9 +277,76 @@ func (a *AWS) createOrReplaceSG(hostInstanceID, hostIP, vpcID string) (string, e if len(describeSGOutput.SecurityGroups) != 1 { return sgID, fmt.Errorf("Expected 1 security group, got %d", len(describeSGOutput.SecurityGroups)) } + + if len(describeSGOutput.SecurityGroups[0].IpPermissionsEgress) != 1 { + return sgID, fmt.Errorf("Expected exactly 1 egress rule on the security group (got %d)", len(describeSGOutput.SecurityGroups[0].IpPermissionsEgress)) + } + + describeSGROutput, err := a.ec2.DescribeSecurityGroupRules(&ec2.DescribeSecurityGroupRulesInput{ + Filters: []*ec2.Filter{ + &ec2.Filter{ + Name: aws.String("group-id"), + Values: []*string{ + aws.String(sgID), + }, + }, + }, + }) + if err != nil { + return sgID, err + } + + for _, rule := range describeSGROutput.SecurityGroupRules { + if *rule.IsEgress { + revokeOutput, err := a.ec2.RevokeSecurityGroupEgress(&ec2.RevokeSecurityGroupEgressInput{ + GroupId: aws.String(sgID), + SecurityGroupRuleIds: []*string{ + rule.SecurityGroupRuleId, + }, + }) + if err != nil { + return sgID, err + } + if !*revokeOutput.Return { + return sgID, fmt.Errorf("Failed to revoke security group %s's egress rule %s", sgID, *rule.SecurityGroupRuleId) + } + } + } + + sgEgressOutput, err := a.ec2.AuthorizeSecurityGroupEgress(&ec2.AuthorizeSecurityGroupEgressInput{ + GroupId: aws.String(sgID), + IpPermissions: []*ec2.IpPermission{ + &ec2.IpPermission{ + IpProtocol: aws.String(ec2.ProtocolTcp), + FromPort: aws.Int64(1), + ToPort: aws.Int64(65535), + IpRanges: []*ec2.IpRange{ + &ec2.IpRange{ + CidrIp: aws.String(fmt.Sprintf("%s/32", hostIP)), + }, + }, + }, + }, + }) + if err != nil { + return sgID, err + } + if !*sgEgressOutput.Return { + return sgID, fmt.Errorf("Unable to attach egress rules to SG") + } + + describeSGOutput, err = a.ec2.DescribeSecurityGroups(&ec2.DescribeSecurityGroupsInput{ + GroupIds: []*string{ + aws.String(sgID), + }, + }) + if err != nil { + return sgID, err + } + // SGs are created with a predefind egress rule that allows all outgoing traffic, so expecting 1 outbound rule if len(describeSGOutput.SecurityGroups[0].IpPermissions) != 1 || len(describeSGOutput.SecurityGroups[0].IpPermissionsEgress) != 1 { - return sgID, fmt.Errorf("Expected 3 security group rules: 1 inbound (got %d) and 1 outbound (got %d)", + return sgID, fmt.Errorf("Expected 2 security group rules: 1 inbound (got %d) and 1 outbound (got %d)", len(describeSGOutput.SecurityGroups[0].IpPermissions), len(describeSGOutput.SecurityGroups[0].IpPermissionsEgress)) }