packages/aws-cdk-lib/aws-ec2/lib/security-group.ts (355 lines of code) (raw):
import { Construct } from 'constructs';
import { Connections } from './connections';
import { CfnSecurityGroup, CfnSecurityGroupEgress, CfnSecurityGroupIngress } from './ec2.generated';
import { IPeer, Peer } from './peer';
import { Port } from './port';
import { IVpc } from './vpc';
import * as cxschema from '../../cloud-assembly-schema';
import { Annotations, ContextProvider, IResource, Lazy, Names, Resource, ResourceProps, Stack, Token, ValidationError } from '../../core';
import { addConstructMetadata, MethodMetadata } from '../../core/lib/metadata-resource';
import * as cxapi from '../../cx-api';
const SECURITY_GROUP_SYMBOL = Symbol.for('@aws-cdk/iam.SecurityGroup');
const SECURITY_GROUP_DISABLE_INLINE_RULES_CONTEXT_KEY = '@aws-cdk/aws-ec2.securityGroupDisableInlineRules';
/**
* Interface for security group-like objects
*/
export interface ISecurityGroup extends IResource, IPeer {
/**
* ID for the current security group
* @attribute
*/
readonly securityGroupId: string;
/**
* Whether the SecurityGroup has been configured to allow all outbound traffic
*/
readonly allowAllOutbound: boolean;
/**
* Add an ingress rule for the current security group
*
* `remoteRule` controls where the Rule object is created if the peer is also a
* securityGroup and they are in different stack. If false (default) the
* rule object is created under the current SecurityGroup object. If true and the
* peer is also a SecurityGroup, the rule object is created under the remote
* SecurityGroup object.
*/
addIngressRule(peer: IPeer, connection: Port, description?: string, remoteRule?: boolean): void;
/**
* Add an egress rule for the current security group
*
* `remoteRule` controls where the Rule object is created if the peer is also a
* securityGroup and they are in different stack. If false (default) the
* rule object is created under the current SecurityGroup object. If true and the
* peer is also a SecurityGroup, the rule object is created under the remote
* SecurityGroup object.
*/
addEgressRule(peer: IPeer, connection: Port, description?: string, remoteRule?: boolean): void;
}
/**
* A SecurityGroup that is not created in this template
*/
abstract class SecurityGroupBase extends Resource implements ISecurityGroup {
/**
* Return whether the indicated object is a security group
*/
public static isSecurityGroup(x: any): x is SecurityGroupBase {
return SECURITY_GROUP_SYMBOL in x;
}
public abstract readonly securityGroupId: string;
public abstract readonly allowAllOutbound: boolean;
public abstract readonly allowAllIpv6Outbound: boolean;
public readonly canInlineRule = false;
public readonly connections: Connections = new Connections({ securityGroups: [this] });
public readonly defaultPort?: Port;
private peerAsTokenCount: number = 0;
constructor(scope: Construct, id: string, props?: ResourceProps) {
super(scope, id, props);
Object.defineProperty(this, SECURITY_GROUP_SYMBOL, { value: true });
}
public get uniqueId() {
return Names.nodeUniqueId(this.node);
}
public addIngressRule(peer: IPeer, connection: Port, description?: string, remoteRule?: boolean) {
if (description === undefined) {
description = `from ${peer.uniqueId}:${connection}`;
}
const { scope, id } = this.determineRuleScope(peer, connection, 'from', remoteRule);
// Skip duplicates
if (scope.node.tryFindChild(id) === undefined) {
new CfnSecurityGroupIngress(scope, id, {
groupId: this.securityGroupId,
...peer.toIngressRuleConfig(),
...connection.toRuleJson(),
description,
});
}
}
public addEgressRule(peer: IPeer, connection: Port, description?: string, remoteRule?: boolean) {
if (description === undefined) {
description = `to ${peer.uniqueId}:${connection}`;
}
const { scope, id } = this.determineRuleScope(peer, connection, 'to', remoteRule);
// Skip duplicates
if (scope.node.tryFindChild(id) === undefined) {
new CfnSecurityGroupEgress(scope, id, {
groupId: this.securityGroupId,
...peer.toEgressRuleConfig(),
...connection.toRuleJson(),
description,
});
}
}
public toIngressRuleConfig(): any {
return { sourceSecurityGroupId: this.securityGroupId };
}
public toEgressRuleConfig(): any {
return { destinationSecurityGroupId: this.securityGroupId };
}
/**
* Determine where to parent a new ingress/egress rule
*
* A SecurityGroup rule is parented under the group it's related to, UNLESS
* we're in a cross-stack scenario with another Security Group. In that case,
* we respect the 'remoteRule' flag and will parent under the other security
* group.
*
* This is necessary to avoid cyclic dependencies between stacks, since both
* ingress and egress rules will reference both security groups, and a naive
* parenting will lead to the following situation:
*
* ╔════════════════════╗ ╔════════════════════╗
* ║ ┌───────────┐ ║ ║ ┌───────────┐ ║
* ║ │ GroupA │◀────╬─┐ ┌───╬───▶│ GroupB │ ║
* ║ └───────────┘ ║ │ │ ║ └───────────┘ ║
* ║ ▲ ║ │ │ ║ ▲ ║
* ║ │ ║ │ │ ║ │ ║
* ║ │ ║ │ │ ║ │ ║
* ║ ┌───────────┐ ║ └───┼───╬────┌───────────┐ ║
* ║ │ EgressA │─────╬─────┘ ║ │ IngressB │ ║
* ║ └───────────┘ ║ ║ └───────────┘ ║
* ║ ║ ║ ║
* ╚════════════════════╝ ╚════════════════════╝
*
* By having the ability to switch the parent, we avoid the cyclic reference by
* keeping all rules in a single stack.
*
* If this happens, we also have to change the construct ID, because
* otherwise we might have two objects with the same ID if we have
* multiple reversed security group relationships.
*
* ╔═══════════════════════════════════╗
* ║┌───────────┐ ║
* ║│ GroupB │ ║
* ║└───────────┘ ║
* ║ ▲ ║
* ║ │ ┌───────────┐ ║
* ║ ├────"from A"──│ IngressB │ ║
* ║ │ └───────────┘ ║
* ║ │ ┌───────────┐ ║
* ║ ├─────"to B"───│ EgressA │ ║
* ║ │ └───────────┘ ║
* ║ │ ┌───────────┐ ║
* ║ └─────"to B"───│ EgressC │ ║ <-- oops
* ║ └───────────┘ ║
* ╚═══════════════════════════════════╝
*/
protected determineRuleScope(
peer: IPeer,
connection: Port,
fromTo: 'from' | 'to',
remoteRule?: boolean): RuleScope {
if (remoteRule && SecurityGroupBase.isSecurityGroup(peer) && differentStacks(this, peer)) {
// Reversed
const reversedFromTo = fromTo === 'from' ? 'to' : 'from';
return { scope: peer, id: `${this.uniqueId}:${connection} ${reversedFromTo}` };
} else {
// Regular (do old ID escaping to in order to not disturb existing deployments)
return { scope: this, id: `${fromTo} ${this.renderPeer(peer)}:${connection}`.replace('/', '_') };
}
}
private renderPeer(peer: IPeer) {
if (Token.isUnresolved(peer.uniqueId)) {
// Need to return a unique value each time a peer
// is an unresolved token, else the duplicate skipper
// in `sg.addXxxRule` can detect unique rules as duplicates
return this.peerAsTokenCount++ ? `'{IndirectPeer${this.peerAsTokenCount}}'` : '{IndirectPeer}';
} else {
return peer.uniqueId;
}
}
}
/**
* The scope and id in which a given SecurityGroup rule should be defined.
*/
export interface RuleScope {
/**
* The SecurityGroup in which a rule should be scoped.
*/
readonly scope: ISecurityGroup;
/**
* The construct ID to use for the rule.
*/
readonly id: string;
}
function differentStacks(group1: SecurityGroupBase, group2: SecurityGroupBase) {
return Stack.of(group1) !== Stack.of(group2);
}
export interface SecurityGroupProps {
/**
* The name of the security group. For valid values, see the GroupName
* parameter of the CreateSecurityGroup action in the Amazon EC2 API
* Reference.
*
* It is not recommended to use an explicit group name.
*
* @default If you don't specify a GroupName, AWS CloudFormation generates a
* unique physical ID and uses that ID for the group name.
*/
readonly securityGroupName?: string;
/**
* A description of the security group.
*
* @default The default name will be the construct's CDK path.
*/
readonly description?: string;
/**
* The VPC in which to create the security group.
*/
readonly vpc: IVpc;
/**
* 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.
*
* To allow all ipv6 traffic use allowAllIpv6Outbound
*
* @default true
*/
readonly allowAllOutbound?: boolean;
/**
* Whether to allow all outbound ipv6 traffic by default.
*
* If this is set to true, there will only be a single egress rule which allows all
* outbound ipv6 traffic. If this is set to false, no outbound traffic will be allowed by
* default and all egress ipv6 traffic must be explicitly authorized.
*
* To allow all ipv4 traffic use allowAllOutbound
*
* @default false
*/
readonly allowAllIpv6Outbound?: boolean;
/**
* Whether to disable inline ingress and egress rule optimization.
*
* If this is set to true, ingress and egress rules will not be declared under the
* SecurityGroup in cloudformation, but will be separate elements.
*
* Inlining rules is an optimization for producing smaller stack templates. Sometimes
* this is not desirable, for example when security group access is managed via tags.
*
* The default value can be overridden globally by setting the context variable
* '@aws-cdk/aws-ec2.securityGroupDisableInlineRules'.
*
* @default false
*/
readonly disableInlineRules?: boolean;
}
/**
* Additional options for imported security groups
*/
export interface SecurityGroupImportOptions {
/**
* Mark the SecurityGroup as having been created allowing all outbound traffic
*
* Only if this is set to false will egress rules be added to this security
* group. Be aware, this would undo any potential "all outbound traffic"
* default.
*
*
* @default true
*/
readonly allowAllOutbound?: boolean;
/**
* Mark the SecurityGroup as having been created allowing all outbound ipv6 traffic
*
* Only if this is set to false will egress rules for ipv6 be added to this security
* group. Be aware, this would undo any potential "all outbound traffic"
* default.
*
* @default false
*/
readonly allowAllIpv6Outbound?: boolean;
/**
* If a SecurityGroup is mutable CDK can add rules to existing groups
*
* Beware that making a SecurityGroup immutable might lead to issue
* due to missing ingress/egress rules for new resources.
*
*
* @default true
*/
readonly mutable?: boolean;
}
/**
* Creates an Amazon EC2 security group within a VPC.
*
* Security Groups act like a firewall with a set of rules, and are associated
* with any AWS resource that has or creates Elastic Network Interfaces (ENIs).
* A typical example of a resource that has a security group is an Instance (or
* Auto Scaling Group of instances)
*
* If you are defining new infrastructure in CDK, there is a good chance you
* won't have to interact with this class at all. Like IAM Roles, Security
* Groups need to exist to control access between AWS resources, but CDK will
* automatically generate and populate them with least-privilege permissions
* for you so you can concentrate on your business logic.
*
* All Constructs that require Security Groups will create one for you if you
* don't specify one at construction. After construction, you can selectively
* allow connections to and between constructs via--for example-- the `instance.connections`
* object. Think of it as "allowing connections to your instance", rather than
* "adding ingress rules a security group". See the [Allowing
* Connections](https://docs.aws.amazon.com/cdk/api/latest/docs/aws-cdk-lib.aws_ec2-readme.html#allowing-connections)
* section in the library documentation for examples.
*
* Direct manipulation of the Security Group through `addIngressRule` and
* `addEgressRule` is possible, but mutation through the `.connections` object
* is recommended. If you peer two constructs with security groups this way,
* appropriate rules will be created in both.
*
* If you have an existing security group you want to use in your CDK application,
* you would import it like this:
*
* ```ts
* const securityGroup = ec2.SecurityGroup.fromSecurityGroupId(this, 'SG', 'sg-12345', {
* mutable: false
* });
* ```
*/
export class SecurityGroup extends SecurityGroupBase {
/**
* Look up a security group by id.
*
* @deprecated Use `fromLookupById()` instead
*/
public static fromLookup(scope: Construct, id: string, securityGroupId: string) {
return this.fromLookupAttributes(scope, id, { securityGroupId });
}
/**
* Look up a security group by id.
*/
public static fromLookupById(scope: Construct, id: string, securityGroupId: string) {
return this.fromLookupAttributes(scope, id, { securityGroupId });
}
/**
* Look up a security group by name.
*/
public static fromLookupByName(scope: Construct, id: string, securityGroupName: string, vpc: IVpc) {
return this.fromLookupAttributes(scope, id, { securityGroupName, vpc });
}
/**
* Import an existing security group into this app.
*
* This method will assume that the Security Group has a rule in it which allows
* all outbound traffic, and so will not add egress rules to the imported Security
* Group (only ingress rules).
*
* If your existing Security Group needs to have egress rules added, pass the
* `allowAllOutbound: false` option on import.
*/
public static fromSecurityGroupId(scope: Construct, id: string, securityGroupId: string, options: SecurityGroupImportOptions = {}): ISecurityGroup {
class MutableImport extends SecurityGroupBase {
public securityGroupId = securityGroupId;
public allowAllOutbound = options.allowAllOutbound ?? true;
public allowAllIpv6Outbound = options.allowAllIpv6Outbound ?? false;
public addEgressRule(peer: IPeer, connection: Port, description?: string, remoteRule?: boolean) {
// Only if allowAllOutbound has been disabled
if (options.allowAllOutbound === false) {
super.addEgressRule(peer, connection, description, remoteRule);
}
}
}
class ImmutableImport extends SecurityGroupBase {
public securityGroupId = securityGroupId;
public allowAllOutbound = options.allowAllOutbound ?? true;
public allowAllIpv6Outbound = options.allowAllIpv6Outbound ?? false;
public addEgressRule(_peer: IPeer, _connection: Port, _description?: string, _remoteRule?: boolean) {
// do nothing
}
public addIngressRule(_peer: IPeer, _connection: Port, _description?: string, _remoteRule?: boolean) {
// do nothing
}
}
return options.mutable !== false
? new MutableImport(scope, id)
: new ImmutableImport(scope, id);
}
/**
* Look up a security group.
*/
private static fromLookupAttributes(scope: Construct, id: string, options: SecurityGroupLookupOptions) {
if (Token.isUnresolved(options.securityGroupId) || Token.isUnresolved(options.securityGroupName) || Token.isUnresolved(options.vpc?.vpcId)) {
throw new ValidationError('All arguments to look up a security group must be concrete (no Tokens)', scope);
}
const attributes: cxapi.SecurityGroupContextResponse = ContextProvider.getValue(scope, {
provider: cxschema.ContextProvider.SECURITY_GROUP_PROVIDER,
props: {
securityGroupId: options.securityGroupId,
securityGroupName: options.securityGroupName,
vpcId: options.vpc?.vpcId,
},
dummyValue: {
securityGroupId: 'sg-12345678',
allowAllOutbound: true,
} as cxapi.SecurityGroupContextResponse,
}).value;
return SecurityGroup.fromSecurityGroupId(scope, id, attributes.securityGroupId, {
allowAllOutbound: attributes.allowAllOutbound,
mutable: true,
});
}
/**
* An attribute that represents the security group name.
*
* @attribute
* @deprecated returns the security group ID, rather than the name.
*/
public readonly securityGroupName: string;
/**
* The ID of the security group
*
* @attribute
*/
public readonly securityGroupId: string;
/**
* The VPC ID this security group is part of.
*
* @attribute
*/
public readonly securityGroupVpcId: string;
/**
* Whether the SecurityGroup has been configured to allow all outbound traffic
*/
public readonly allowAllOutbound: boolean;
/**
* Whether the SecurityGroup has been configured to allow all outbound ipv6 traffic
*/
public readonly allowAllIpv6Outbound: boolean;
private readonly securityGroup: CfnSecurityGroup;
private readonly directIngressRules: CfnSecurityGroup.IngressProperty[] = [];
private readonly directEgressRules: CfnSecurityGroup.EgressProperty[] = [];
/**
* Whether to disable optimization for inline security group rules.
*/
private readonly disableInlineRules: boolean;
constructor(scope: Construct, id: string, props: SecurityGroupProps) {
super(scope, id, {
physicalName: props.securityGroupName,
});
// Enhanced CDK Analytics Telemetry
addConstructMetadata(this, props);
const groupDescription = props.description || this.node.path;
this.allowAllOutbound = props.allowAllOutbound !== false;
this.allowAllIpv6Outbound = props.allowAllIpv6Outbound ?? false;
this.disableInlineRules = props.disableInlineRules !== undefined ?
!!props.disableInlineRules :
!!this.node.tryGetContext(SECURITY_GROUP_DISABLE_INLINE_RULES_CONTEXT_KEY);
this.securityGroup = new CfnSecurityGroup(this, 'Resource', {
groupName: this.physicalName,
groupDescription,
securityGroupIngress: Lazy.any({ produce: () => this.directIngressRules }, { omitEmptyArray: true }),
securityGroupEgress: Lazy.any({ produce: () => this.directEgressRules }, { omitEmptyArray: true }),
vpcId: props.vpc.vpcId,
});
this.securityGroupId = this.securityGroup.attrGroupId;
this.securityGroupVpcId = this.securityGroup.attrVpcId;
this.securityGroupName = this.securityGroup.ref;
this.addDefaultEgressRule();
this.addDefaultIpv6EgressRule();
}
@MethodMetadata()
public addIngressRule(peer: IPeer, connection: Port, description?: string, remoteRule?: boolean) {
if (!peer.canInlineRule || !connection.canInlineRule || this.disableInlineRules) {
super.addIngressRule(peer, connection, description, remoteRule);
return;
}
if (description === undefined) {
description = `from ${peer.uniqueId}:${connection}`;
}
this.addDirectIngressRule({
...peer.toIngressRuleConfig(),
...connection.toRuleJson(),
description,
});
}
@MethodMetadata()
public addEgressRule(peer: IPeer, connection: Port, description?: string, remoteRule?: boolean) {
const isIpv6 = peer.toEgressRuleConfig().hasOwnProperty('cidrIpv6');
if (!isIpv6 && 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.
if (!remoteRule) { // Warn only if addEgressRule() was explicitly called
Annotations.of(this).addWarningV2('@aws-cdk/aws-ec2:ipv4IgnoreEgressRule', 'Ignoring Egress rule since \'allowAllOutbound\' is set to true; To add customized rules, set allowAllOutbound=false on the SecurityGroup');
}
return;
} else if (!isIpv6 && !this.allowAllOutbound) {
// 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.removeNoTrafficRule();
}
if (isIpv6 && this.allowAllIpv6Outbound) {
// In the case of "allowAllIpv6Outbound", we don't add any more rules. There
// is only one rule which allows all traffic and that subsumes any other
// rule.
if (!remoteRule) { // Warn only if addEgressRule() was explicitly called
Annotations.of(this).addWarningV2('@aws-cdk/aws-ec2:ipv6IgnoreEgressRule', 'Ignoring Egress rule since \'allowAllIpv6Outbound\' is set to true; To add customized rules, set allowAllIpv6Outbound=false on the SecurityGroup');
}
return;
}
if (!peer.canInlineRule || !connection.canInlineRule || this.disableInlineRules) {
super.addEgressRule(peer, connection, description, remoteRule);
return;
}
if (description === undefined) {
description = `from ${peer.uniqueId}:${connection}`;
}
const rule = {
...peer.toEgressRuleConfig(),
...connection.toRuleJson(),
description,
};
if (isAllTrafficRule(rule)) {
// We cannot allow this; if someone adds the rule in this way, it will be
// removed again if they add other rules. We also can't automatically switch
// to "allOutbound=true" mode, because we might have already emitted
// EgressRule objects (which count as rules added later) and there's no way
// to recall those. Better to prevent this for now.
throw new ValidationError('Cannot add an "all traffic" egress rule in this way; set allowAllOutbound=true (for ipv6) or allowAllIpv6Outbound=true (for ipv6) on the SecurityGroup instead.', this);
}
this.addDirectEgressRule(rule);
}
/**
* Add a direct ingress rule
*/
private addDirectIngressRule(rule: CfnSecurityGroup.IngressProperty) {
if (!this.hasIngressRule(rule)) {
this.directIngressRules.push(rule);
}
}
/**
* Return whether the given ingress rule exists on the group
*/
private hasIngressRule(rule: CfnSecurityGroup.IngressProperty): boolean {
return this.directIngressRules.findIndex(r => ingressRulesEqual(r, rule)) > -1;
}
/**
* Add a direct egress rule
*/
private addDirectEgressRule(rule: CfnSecurityGroup.EgressProperty) {
if (!this.hasEgressRule(rule)) {
this.directEgressRules.push(rule);
}
}
/**
* Return whether the given egress rule exists on the group
*/
private hasEgressRule(rule: CfnSecurityGroup.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.disableInlineRules) {
const peer = this.allowAllOutbound ? ALL_TRAFFIC_PEER : NO_TRAFFIC_PEER;
const port = this.allowAllOutbound ? ALL_TRAFFIC_PORT : NO_TRAFFIC_PORT;
const description = this.allowAllOutbound ? ALLOW_ALL_RULE.description : MATCH_NO_TRAFFIC.description;
super.addEgressRule(peer, port, description, false);
} else {
const rule = this.allowAllOutbound ? ALLOW_ALL_RULE : MATCH_NO_TRAFFIC;
this.directEgressRules.push(rule);
}
}
/**
* Add a allow all ipv6 egress rule to the securityGroup
*
* This depends on allowAllIpv6Outbound:
*
* - If allowAllIpv6Outbound is true, we will add an allow all rule.
* - If allowAllOutbound is false, we don't do anything since EC2 does not add
* a default allow all ipv6 rule.
*/
private addDefaultIpv6EgressRule() {
const description = 'Allow all outbound ipv6 traffic by default';
const peer = Peer.anyIpv6();
if (this.allowAllIpv6Outbound) {
if (this.disableInlineRules) {
super.addEgressRule(peer, Port.allTraffic(), description, false);
} else {
this.directEgressRules.push({
ipProtocol: '-1',
cidrIpv6: peer.uniqueId,
description,
});
}
}
}
/**
* Remove the bogus rule if it exists
*/
private removeNoTrafficRule() {
if (this.disableInlineRules) {
const { scope, id } = this.determineRuleScope(
NO_TRAFFIC_PEER,
NO_TRAFFIC_PORT,
'to',
false,
);
scope.node.tryRemoveChild(id);
} else {
const i = this.directEgressRules.findIndex(r => egressRulesEqual(r, MATCH_NO_TRAFFIC));
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 MATCH_NO_TRAFFIC = {
cidrIp: '255.255.255.255/32',
description: 'Disallow all traffic',
ipProtocol: 'icmp',
fromPort: 252,
toPort: 86,
};
const NO_TRAFFIC_PEER = Peer.ipv4(MATCH_NO_TRAFFIC.cidrIp);
const NO_TRAFFIC_PORT = Port.icmpTypeAndCode(MATCH_NO_TRAFFIC.fromPort, MATCH_NO_TRAFFIC.toPort);
/**
* 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',
};
const ALL_TRAFFIC_PEER = Peer.anyIpv4();
const ALL_TRAFFIC_PORT = Port.allTraffic();
export interface ConnectionRule {
/**
* The IP protocol name (tcp, udp, icmp) or number (see Protocol Numbers).
* Use -1 to specify all protocols. If you specify -1, or a protocol number
* other than tcp, udp, icmp, or 58 (ICMPv6), traffic on all ports is
* allowed, regardless of any ports you specify. For tcp, udp, and icmp, you
* must specify a port range. For protocol 58 (ICMPv6), you can optionally
* specify a port range; if you don't, traffic for all types and codes is
* allowed.
*
* @default tcp
*/
readonly protocol?: string;
/**
* Start of port range for the TCP and UDP protocols, or an ICMP type number.
*
* If you specify icmp for the IpProtocol property, you can specify
* -1 as a wildcard (i.e., any ICMP type number).
*/
readonly fromPort: number;
/**
* End of port range for the TCP and UDP protocols, or an ICMP code.
*
* If you specify icmp for the IpProtocol property, you can specify -1 as a
* wildcard (i.e., any ICMP code).
*
* @default If toPort is not specified, it will be the same as fromPort.
*/
readonly toPort?: number;
/**
* Description of this connection. It is applied to both the ingress rule
* and the egress rule.
*
* @default No description
*/
readonly description?: string;
}
/**
* Compare two ingress rules for equality the same way CloudFormation would (discarding description)
*/
function ingressRulesEqual(a: CfnSecurityGroup.IngressProperty, b: CfnSecurityGroup.IngressProperty) {
return a.cidrIp === b.cidrIp
&& a.cidrIpv6 === b.cidrIpv6
&& a.fromPort === b.fromPort
&& a.toPort === b.toPort
&& a.ipProtocol === b.ipProtocol
&& a.sourceSecurityGroupId === b.sourceSecurityGroupId
&& a.sourceSecurityGroupName === b.sourceSecurityGroupName
&& a.sourceSecurityGroupOwnerId === b.sourceSecurityGroupOwnerId;
}
/**
* Compare two egress rules for equality the same way CloudFormation would (discarding description)
*/
function egressRulesEqual(a: CfnSecurityGroup.EgressProperty, b: CfnSecurityGroup.EgressProperty) {
return a.cidrIp === b.cidrIp
&& a.cidrIpv6 === b.cidrIpv6
&& a.fromPort === b.fromPort
&& a.toPort === b.toPort
&& a.ipProtocol === b.ipProtocol
&& a.destinationPrefixListId === b.destinationPrefixListId
&& a.destinationSecurityGroupId === b.destinationSecurityGroupId;
}
/**
* Whether this rule refers to all traffic
*/
function isAllTrafficRule(rule: any) {
return (rule.cidrIp === '0.0.0.0/0' || rule.cidrIpv6 === '::/0') && rule.ipProtocol === '-1';
}
/**
* Properties for looking up an existing SecurityGroup.
*
* Either `securityGroupName` or `securityGroupId` has to be specified.
*/
interface SecurityGroupLookupOptions {
/**
* The name of the security group
*
* If given, will import the SecurityGroup with this name.
*
* @default Don't filter on securityGroupName
*/
readonly securityGroupName?: string;
/**
* The ID of the security group
*
* If given, will import the SecurityGroup with this ID.
*
* @default Don't filter on securityGroupId
*/
readonly securityGroupId?: string;
/**
* The VPC of the security group
*
* If given, will filter the SecurityGroup based on the VPC.
*
* @default Don't filter on VPC
*/
readonly vpc?: IVpc;
}