-
Notifications
You must be signed in to change notification settings - Fork 4k
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
fix(aws-ec2): fix SecurityGroup's all egress traffic rule #998
Merged
Merged
Changes from 2 commits
Commits
Show all changes
10 commits
Select commit
Hold shift + click to select a range
cf5f9c7
docs(aws-ec2): Explain Security Groups better
rix0rrr ad7c31c
Mention that no egress rules = all traffic
9c9f912
fix(aws-ec2): SecurityGroups ensure outbound rules
641d888
Update expectation
3fc6c75
WIP
b168d0f
Merge remote-tracking branch 'origin/huijbers/sg-docs' into huijbers/…
1e04e0f
Rename AllConnections() -> AllTraffic()
ed013f6
Merge remote-tracking branch 'origin/master' into huijbers/explicit-o…
e4b6b3e
Update expectations
95a39df
Make allowAllOutbound configurable for Lambda
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -115,6 +115,17 @@ export interface SecurityGroupProps { | |
* The VPC in which to create the security group. | ||
*/ | ||
vpc: VpcNetworkRef; | ||
|
||
/** | ||
* Whether to allow all outbound traffic by default. | ||
* | ||
* If this is set to true, there will only be a single egress rule which allows all | ||
* outbound traffic. If this is set to false, no outbound traffic will be allowed by | ||
* default and all egress traffic must be explicitly authorized. | ||
* | ||
* @default false | ||
*/ | ||
allowAllOutbound?: boolean; | ||
} | ||
|
||
/** | ||
|
@@ -149,11 +160,16 @@ export class SecurityGroup extends SecurityGroupRef implements ITaggable { | |
private readonly directIngressRules: cloudformation.SecurityGroupResource.IngressProperty[] = []; | ||
private readonly directEgressRules: cloudformation.SecurityGroupResource.EgressProperty[] = []; | ||
|
||
private readonly allowAllOutbound: boolean; | ||
|
||
constructor(parent: Construct, name: string, props: SecurityGroupProps) { | ||
super(parent, name); | ||
|
||
this.tags = new TagManager(this, { initialTags: props.tags}); | ||
const groupDescription = props.description || this.path; | ||
|
||
this.allowAllOutbound = props.allowAllOutbound || false; | ||
|
||
this.securityGroup = new cloudformation.SecurityGroupResource(this, 'Resource', { | ||
groupName: props.groupName, | ||
groupDescription, | ||
|
@@ -166,6 +182,8 @@ export class SecurityGroup extends SecurityGroupRef implements ITaggable { | |
this.securityGroupId = this.securityGroup.securityGroupId; | ||
this.groupName = this.securityGroup.securityGroupName; | ||
this.vpcId = this.securityGroup.securityGroupVpcId; | ||
|
||
this.addDefaultEgressRule(); | ||
} | ||
|
||
public addIngressRule(peer: ISecurityGroupRule, connection: IPortRange, description?: string) { | ||
|
@@ -186,6 +204,18 @@ export class SecurityGroup extends SecurityGroupRef implements ITaggable { | |
} | ||
|
||
public addEgressRule(peer: ISecurityGroupRule, connection: IPortRange, description?: string) { | ||
if (this.allowAllOutbound) { | ||
// In the case of "allowAllOutbound", we don't add any more rules. There | ||
// is only one rule which allows all traffic and that subsumes any other | ||
// rule. | ||
return; | ||
} else { | ||
// Otherwise, if the bogus rule exists we can now remove it because the | ||
// presence of any other rule will get rid of EC2's implicit "all | ||
// outbound" rule anyway. | ||
this.removeBogusRule(); | ||
} | ||
|
||
if (!peer.canInlineRule || !connection.canInlineRule) { | ||
super.addEgressRule(peer, connection, description); | ||
return; | ||
|
@@ -233,8 +263,66 @@ export class SecurityGroup extends SecurityGroupRef implements ITaggable { | |
private hasEgressRule(rule: cloudformation.SecurityGroupResource.EgressProperty): boolean { | ||
return this.directEgressRules.findIndex(r => egressRulesEqual(r, rule)) > -1; | ||
} | ||
|
||
/** | ||
* Add the default egress rule to the securityGroup | ||
* | ||
* This depends on allowAllOutbound: | ||
* | ||
* - If allowAllOutbound is true, we *TECHNICALLY* don't need to do anything, because | ||
* EC2 is going to create this default rule anyway. But, for maximum readability | ||
* of the template, we will add one anyway. | ||
* - If allowAllOutbound is false, we add a bogus rule that matches no traffic in | ||
* order to get rid of the default "all outbound" rule that EC2 creates by default. | ||
* If other rules happen to get added later, we remove the bogus rule again so | ||
* that it doesn't clutter up the template too much (even though that's not | ||
* strictly necessary). | ||
*/ | ||
private addDefaultEgressRule() { | ||
if (this.allowAllOutbound) { | ||
this.directEgressRules.push(ALLOW_ALL_RULE); | ||
} else { | ||
this.directEgressRules.push(BOGUS_RULE); | ||
} | ||
} | ||
|
||
/** | ||
* Remove the bogus rule if it exists | ||
*/ | ||
private removeBogusRule() { | ||
const i = this.directEgressRules.findIndex(r => egressRulesEqual(r, BOGUS_RULE)); | ||
if (i > -1) { | ||
this.directEgressRules.splice(i, 1); | ||
} | ||
} | ||
} | ||
|
||
/** | ||
* Egress rule that purposely matches no traffic | ||
* | ||
* This is used in order to disable the "all traffic" default of Security Groups. | ||
* | ||
* No machine can ever actually have the 255.255.255.255 IP address, but | ||
* in order to lock it down even more we'll restrict to a nonexistent | ||
* ICMP traffic type. | ||
*/ | ||
const BOGUS_RULE = { | ||
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. Why not call this BLOCK_ALL_TRAFFIC? 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. Yeah, probably more professional. Actually it should be something like |
||
cidrIp: '255.255.255.255/32', | ||
description: 'Disallow all traffic', | ||
ipProtocol: 'icmp', | ||
fromPort: 252, | ||
toPort: 86 | ||
}; | ||
|
||
/** | ||
* Egress rule that matches all traffic | ||
*/ | ||
const ALLOW_ALL_RULE = { | ||
cidrIp: '0.0.0.0/0', | ||
description: 'Allow all outbound traffic by default', | ||
ipProtocol: '-1', | ||
}; | ||
|
||
export interface ConnectionRule { | ||
/** | ||
* The IP protocol name (tcp, udp, icmp) or number (see Protocol Numbers). | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
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.
Just for my future coding preference do we prefer:
My bigger question here is defaulting this to
false
. I think most users expect this to betrue
? However, other infra code products take this approach so perhaps that is better?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.
My thinking here has been torn between two ways.
This is the "secure by default" solution.
However, it takes more work to get to "what you want", if you want to access things outside your VPC (which you almost always want, to use AWS services). All constructs that create SG's now have to set the default to
true
(which ASGs also do), which is kinda silly.Yeah, maybe you're right.
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.
To be clear I'm also on the fence just like you, just highlighting the pivot in AWS status quo on this particular feature. I can support either direction.
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.
Going to go with "works by default" over "secure by default" after all I think.