Skip to content
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

Get default security group from VPC #1606

Closed
FantasticFiasco opened this issue Jan 24, 2019 · 16 comments
Closed

Get default security group from VPC #1606

FantasticFiasco opened this issue Jan 24, 2019 · 16 comments
Assignees
Labels
@aws-cdk/aws-ec2 Related to Amazon Elastic Compute Cloud feature-request A feature should be added or improved.

Comments

@FantasticFiasco
Copy link

I would like to export the default security group id from a VPC I create. In CloudFormation I would reference the VPC return value of DefaultSecurityGroup, but how would I accomplish the same using the CDK?

@rix0rrr
Copy link
Contributor

rix0rrr commented Jan 24, 2019

The simplest way would be to use a findChild() call to access the CloudFormation CfnVpc object. It has the property you are looking for.

The resource is called "Resource":

https://github.com/awslabs/aws-cdk/blob/master/packages/%40aws-cdk/aws-ec2/lib/vpc.ts#L312

Can you tell us a little about what you're trying to achieve though, and why the existing SecurityGroup mechanisms aren't sufficient?

@rix0rrr rix0rrr added the @aws-cdk/aws-ec2 Related to Amazon Elastic Compute Cloud label Jan 24, 2019
@FantasticFiasco
Copy link
Author

Can you elaborate on what you mean with the existing security group mechanisms? My understanding is, and forgive me if I am wrong, that you cannot specify the security group when you are creating your VPC. Because of that I have to work around the fact that the security group is created automatically when the VPC is created.

Regarding the reason as to why I need the exported output, please let me check my code tomorrow at work, and I will update you with information.

@mattias-fjellstrom
Copy link

It's not that the existing security group mechanisms are insufficient, they are not. However, we expected the default security group to be readily available since it is created automatically and there is no way to prohibit it to be created (as far as I know).
What we were trying to accomplish here was simply to be able to use the default security group as a source in other security groups that we create.

@foscraig
Copy link

foscraig commented Apr 1, 2019

My issue #1995 seems to be a dup of this. I tried the "escape hatch" workaround suggested by @rix0rrr :

VpcNetworkProviderProps vpcProps = VpcNetworkProviderProps.builder()
        .withVpcName(CODEBUILD_VPC_NAME)
        .build();
    IVpcNetwork vpc = VpcNetwork.importFromContext(stack, "Imported VPC", vpcProps);
    CfnVPC cfnVPC = (CfnVPC) vpc.getNode().findChild("Resource");

but this doesn't seem to work either as I get a runtime error: Exception in thread "main" software.amazon.jsii.JsiiException: No child with path: 'Resource'

Just to be specific on the use case: when I'm creating an EFS file system mountpoint it requires I specify the security group ID associated with the subnet.

@SomayaB SomayaB added the feature-request A feature should be added or improved. label Oct 26, 2019
@rix0rrr
Copy link
Contributor

rix0rrr commented Oct 30, 2019

Still relevant, but not easy. The default security group is not created as part of CDK.

@rix0rrr
Copy link
Contributor

rix0rrr commented Jan 23, 2020

I misunderstood the question originally: someone wanting to do this can get the vpc.node.defaultChild, get the attribute they need with the default security group id, and SecurityGroup.fromSecurityGroupId() import in into their stack. But they really shouldn't use the default SG in the first place (and why would they save on them, SGs are free), so I'm not sure we should be making it easy.

Closing this, feel free to reopen if someone has a compelling use case.

@rix0rrr rix0rrr closed this as completed Jan 23, 2020
@shearn89
Copy link
Contributor

Just about to (I think) bump into this. One use case I can see is that the CIS security benchmark for AWS dictates that all default security groups should restrict all traffic (control 4.3 - https://docs.aws.amazon.com/securityhub/latest/userguide/securityhub-cis-controls.html#cis-4.3-remediation). To comply with that, it would be nice to configure the default security group as part of the CDK template.

I'll do some reading and find out whether (a) this is already possible as @rix0rr says, or (b) needs to be added. Had to do something similar to retrieve the default IGW id, so hopefully isn't too dissimilar.

@AaronFinn95
Copy link

Just about to (I think) bump into this. One use case I can see is that the CIS security benchmark for AWS dictates that all default security groups should restrict all traffic (control 4.3 - https://docs.aws.amazon.com/securityhub/latest/userguide/securityhub-cis-controls.html#cis-4.3-remediation). To comply with that, it would be nice to configure the default security group as part of the CDK template.

I'll do some reading and find out whether (a) this is already possible as @rix0rr says, or (b) needs to be added. Had to do something similar to retrieve the default IGW id, so hopefully isn't too dissimilar.

Did you find out if this was possible in a standard way? As the above is my exact use case

@shearn89
Copy link
Contributor

Sorry @AaronFinn95, read this on email and then forgot to reply! I think in the end I didn't chase this down any further. IIRC, I either manually changed the default VPC once I'd built it (since it wasn't getting torn down and recreated very much), or used the "escape hatch" method to get the group ID... If I get time I'll have to revisit this and try to raise a PR, doing the same thing for the IGW wasn't too taxing.

@basverweij-pp
Copy link

basverweij-pp commented Jan 7, 2022

@shearn89 & @AaronFinn95: FYI- I ran into this issue yesterday because of the same use case and solved it by wrapping the Vpc class and adding two custom resources that revoke the default security group's ingress and egress rules. (As a bonus they are also restored when deleting the construct, so that it nicely rolls back in case a stack deployment fails.)

import { Stack } from 'aws-cdk-lib';
import { CfnVPC, Vpc, VpcProps } from 'aws-cdk-lib/aws-ec2';
import {
    AwsCustomResource, AwsCustomResourcePolicy, PhysicalResourceId
} from 'aws-cdk-lib/custom-resources';
import { Construct } from 'constructs';

export class BaseVpc extends Vpc {

    constructor(
        scope: Construct,
        id: string,
        props: VpcProps) {

        super(
            scope,
            id,
            props);

        // Configure default security group according to "CIS AWS Foundations Benchmark controls",
        // section "4.3 – Ensure the default security group of every VPC restricts all traffic".
        // See https://docs.aws.amazon.com/securityhub/latest/userguide/securityhub-cis-controls.html#securityhub-cis-controls-4.3

        const cfnVpc = this.node.defaultChild as CfnVPC;

        const stack = Stack.of(this);

        const ingressParameters = {
            GroupId: cfnVpc.attrDefaultSecurityGroup,
            IpPermissions: [
                {
                    IpProtocol: '-1',
                    UserIdGroupPairs: [
                        {
                            GroupId: cfnVpc.attrDefaultSecurityGroup,
                        },
                    ],
                },
            ],
        };

        new AwsCustomResource(
            this,
            'RestrictSecurityGroupIngress',
            {
                onCreate: {
                    service: 'EC2',
                    action: 'revokeSecurityGroupIngress',
                    parameters: ingressParameters,
                    physicalResourceId: PhysicalResourceId.of(`restrict-ingress-${this.vpcId}-${cfnVpc.attrDefaultSecurityGroup}`),
                },
                onDelete: {
                    service: 'EC2',
                    action: 'authorizeSecurityGroupIngress',
                    parameters: ingressParameters,
                },
                policy: AwsCustomResourcePolicy.fromSdkCalls({
                    resources: [`arn:aws:ec2:${stack.region}:${stack.account}:security-group/${cfnVpc.attrDefaultSecurityGroup}`],
                }),
            });

        const egressParameters = {
            GroupId: cfnVpc.attrDefaultSecurityGroup,
            IpPermissions: [
                {
                    IpProtocol: '-1',
                    IpRanges: [
                        {
                            CidrIp: '0.0.0.0/0',
                        },
                    ],
                },
            ],
        };

        new AwsCustomResource(
            this,
            'RestrictSecurityGroupEgress',
            {
                onCreate: {
                    service: 'EC2',
                    action: 'revokeSecurityGroupEgress',
                    parameters: egressParameters,
                    physicalResourceId: PhysicalResourceId.of(`restrict-egress-${this.vpcId}-${cfnVpc.attrDefaultSecurityGroup}`),
                },
                onDelete: {
                    service: 'EC2',
                    action: 'authorizeSecurityGroupEgress',
                    parameters: egressParameters,
                },
                policy: AwsCustomResourcePolicy.fromSdkCalls({
                    resources: [`arn:aws:ec2:${stack.region}:${stack.account}:security-group/${cfnVpc.attrDefaultSecurityGroup}`],
                }),
            });
    }
}

@daniel4x
Copy link

Following @basverweij-pp reply, I had the same problem and translated his code to Python.
For reference:

from aws_cdk import aws_iam
from aws_cdk.aws_ec2 import CfnVPC, Vpc
from aws_cdk.core import Stack, Construct
from aws_cdk.custom_resources import AwsCustomResource, AwsCustomResourcePolicy, PhysicalResourceId, AwsSdkCall


class BaseVpc(Vpc):

    def __init__(self, scope: Construct, id: str, **kwargs):
        super().__init__(scope, id, **kwargs)

        # Configure default security group according to "CIS AWS Foundations Benchmark controls",
        # section "4.3 – Ensure the default security group of every VPC restricts all traffic".
        # See https://docs.aws.amazon.com/securityhub/latest/userguide/securityhub-cis-controls.html#securityhub-cis-controls-4.3

        cfn_vpc: CfnVPC = self.node.default_child
        stack: Stack = Stack.of(self)

        ingress_parameters = {
            'GroupId': cfn_vpc.attr_default_security_group,
            'IpPermissions': [
                {
                    'IpProtocol': '-1',
                    'UserIdGroupPairs': [
                        {
                            'GroupId': cfn_vpc.attr_default_security_group,
                        },
                    ],
                },
            ],
        }

        AwsCustomResource(
            self,
            'RestrictSecurityGroupIngress',
            on_create=AwsSdkCall(service='EC2',
                                 action='revokeSecurityGroupIngress',
                                 parameters=ingress_parameters,
                                 physical_resource_id=PhysicalResourceId.of(
                                     f'restrict-ingress-${self.vpc_id}-${cfn_vpc.attr_default_security_group}')),
            on_delete=AwsSdkCall(service='EC2', action='authorizeSecurityGroupIngress',
                                 parameters=ingress_parameters),
            policy=AwsCustomResourcePolicy.from_statements(statements=[
                aws_iam.PolicyStatement(
                    effect=aws_iam.Effect.ALLOW,
                    actions=['ec2:revokeSecurityGroupIngress'],
                    resources=[
                        f'arn:aws:ec2:{stack.region}:{stack.account}:security-group/{cfn_vpc.attr_default_security_group}']
                )])
        )

        egress_parameters = {
            'GroupId': cfn_vpc.attr_default_security_group,
            'IpPermissions': [
                {
                    'IpProtocol': '-1',
                    'IpRanges': [
                        {
                            'CidrIp': '0.0.0.0/0',
                        },
                    ],
                },
            ],
        }

        AwsCustomResource(
            self, 'RestrictSecurityGroupEgress',
            on_create=AwsSdkCall(service='EC2',
                                 action='revokeSecurityGroupEgress',
                                 parameters=egress_parameters,
                                 physical_resource_id=PhysicalResourceId.of(
                                     f'restrict-egress-${self.vpc_id}-${cfn_vpc.attr_default_security_group}')),
            on_delete=AwsSdkCall(service='EC2', action='authorizeSecurityGroupEgress',
                                 parameters=egress_parameters),
            policy=AwsCustomResourcePolicy.from_statements(statements=[
                aws_iam.PolicyStatement(
                    effect=aws_iam.Effect.ALLOW,
                    actions=['ec2:revokeSecurityGroupEgress'],
                    resources=[
                        f'arn:aws:ec2:{stack.region}:{stack.account}:security-group/{cfn_vpc.attr_default_security_group}']
                )]))

@qoomon
Copy link

qoomon commented Sep 29, 2022

@basverweij-pp could you explain why you have to define an onDelete call to add default ingress and egress access again?

@qoomon
Copy link

qoomon commented Sep 29, 2022

@basverweij-pp is it really necessary to define a physicalResourceId what happens I you omit it?

@basverweij-pp
Copy link

@basverweij-pp could you explain why you have to define an onDelete call to add default ingress and egress access again?

@qoomon It's been a while since I created this 🙂, but I think the problem was that if you don't restore the ingress and egress settings, deploying the stack a second time (e.g. after it failed deploying and rolled back because of a problem with another construct) causes an error because the specific ingress and egress rules that we are trying to delete do not exist anymore.

@basverweij-pp
Copy link

@basverweij-pp is it really necessary to define a physicalResourceId what happens I you omit it?

@qoomon I would say that there are two reasons: 1) it improves readability of your resources in CloudFormation; and 2) it prevents replacements due to a changed physical resource id which would not work in this scenario (as the replacement tries to create the new resource first - which would try to revoke the ingress & egress again, and hence failing; and even if it would succeed, it would then authorize the original ingress & egress when the resource with the 'old' physical resource id is deleted).

Hope this helps!

@qoomon
Copy link

qoomon commented Sep 30, 2022

@basverweij-pp thanks a lot for your explanation.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
@aws-cdk/aws-ec2 Related to Amazon Elastic Compute Cloud feature-request A feature should be added or improved.
Projects
None yet
Development

No branches or pull requests

10 participants