How to restrict the default AWS VPC ingress to block all traffic - amazon-web-services

I have a VPC that gets created through CloudFormation, but it also creates a default Security Group with inbound and outbound access. I don't use this default security group, and would like to restrict it. I can't find anywhere that gives me a good reference on how to lock it down through CloudFormation. Egress I have setup, but not Ingress. The best I can do is to manually remove it once it's created, but I'm hoping to find a config that will do the same when it gets created. This is what I have, but I don't know if it's effective. How do I completely block all ingress and egress for this SG?:
"VPCDefaultSecurityGroupIngress": {
"Type" : "AWS::EC2::SecurityGroupIngress",
"Properties" : {
"GroupId": { "Fn::GetAtt" : ["Vpc", "DefaultSecurityGroup"] },
"CidrIp": "127.0.0.1/32",
"IpProtocol": "-1"
},
"DependsOn" : [
"Vpc"
]
},
"VPCDefaultSecurityGroupEgress": {
"Type" : "AWS::EC2::SecurityGroupEgress",
"Properties" : {
"GroupId": { "Fn::GetAtt" : ["Vpc", "DefaultSecurityGroup"] },
"CidrIp": "127.0.0.1/32",
"IpProtocol": "-1"
},
"DependsOn" : [
"Vpc"
]
},

Related

Cloudformation script taskset cannot pull image from ECR

In my current setup I use cloudformation to setup an environment. All the steps configured so far seem to work, except for the taskset. I use an image on AWS ECR repo to launch a Fargate instance, which is put into a new cluster with a service.
When I create the stack all the steps succeed but the creation of the service is stuck in "CREATE_IN_PROGRESS" forever. When I look at the created service and more specifically the task, I found that cloudformation was unable to fetch the image. The error is as follows:
STOPPED (CannotPullContainerError: Error response from daem)
Now when I manually try to an instance using the same image on said cluster, it works just fine. After some googling I came upon certain solutions. I have implemented all of them, such as:
Create a network with security groups which allows outgoing traffic to all destinations
Set the flag AssignPublicIp to 'ENABLED' in the awsvpc configuration
Set the user execution role to the same one I used in manual creation
Note that the task does get created and it does receive a public and private IP address. Also, I have tried to throw away the ECR instance and create a new one with a new name (and of course tagged and pushed the image).
The following snippet is my service and taskset definition (renamed some sensitive info):
"MyVPC": {
"Type": "AWS::EC2::VPC",
"Properties": {
"CidrBlock": "10.0.1.0/16",
"Tags": [
{"Key":"Name", "Value":"MyVPC"
}
]
},
},
"MyInternetGateway" : {
"Type" : "AWS::EC2::InternetGateway",
"Properties" : {
}
},
"MySubPublic": {
"Type": "AWS::EC2::Subnet",
"Properties": {
"AvailabilityZone": "eu-central-1a",
"CidrBlock": "10.0.1.0/28",
"MapPublicIpOnLaunch": true,
"VpcId": {
"Ref": "MyVPC"
}
},
"DependsOn": "MyInternetGateway"
},
"MySecGroup":
{
"Type" : "AWS::EC2::SecurityGroup",
"Properties" : {
"GroupDescription" : "My security group for all incoming and outgoing.",
"GroupName" : "MySecGroup",
"SecurityGroupEgress" : [ {
"CidrIp" : "0.0.0.0/0",
"Description" : "Allow machine to reach internet.",
"FromPort" : -1,
"IpProtocol" : -1,
"ToPort" : -1
} ],
"SecurityGroupIngress" : [ {
"CidrIp" : "0.0.0.0/0",
"Description" : "Allow machine to be reached from the entire internet.",
"FromPort" : -1,
"IpProtocol" : -1,
"ToPort" : -1
} ],
"VpcId" : {"Ref": "MyVPC"}
},
"DependsOn": "MyVPC"
},
"MyCluster": {
"Type": "AWS::ECS::Cluster",
"Properties": {
"ClusterName": "MyCluster"
},
"DependsOn": [
"MyVPC"
]
},
"MyDockerTask": {
"Type": "AWS::ECS::TaskDefinition",
"Properties": {
"ContainerDefinitions": [
{
"Cpu": 512,
"Image": "<NRHERE>.dkr.ecr.eu-central-1.amazonaws.com/mysqs",
"Memory": 1024,
"MemoryReservation": 1024,
"Name": "MyContainer"
}
],
"Cpu": "512",
"ExecutionRoleArn": "arn:aws:iam::<NRHERE>:role/ecsTaskExecutionRole",
"Family": "MyFam",
"Memory": "1024",
"NetworkMode": "awsvpc",
"RequiresCompatibilities": [
"FARGATE",
"EC2"
],
"TaskRoleArn": "arn:aws:iam::<NRHERE>:role/ecsTaskExecutionRole"
},
},
"MyService": {
"Type" : "AWS::ECS::Service",
"Properties" : {
"Cluster" : {"Fn::GetAtt": ["MyCluster", "Arn"]},
"DesiredCount" : 1,
"LaunchType" : "FARGATE",
"NetworkConfiguration" : {
"AwsvpcConfiguration" : {
"AssignPublicIp" : "ENABLED",
"SecurityGroups" : [ {"Fn::GetAtt": ["MySecGroup", "GroupId"]} ],
"Subnets" : [ {"Ref": "MySubPublic"}]
}
},
"SchedulingStrategy" : "REPLICA",
"ServiceName" : "MyService",
"TaskDefinition": {"Ref": "MyDockerTask"}
},
"DependsOn": "MySubPublic"
},
"MyTaskSet": {
"Type": "AWS::ECS::TaskSet",
"Properties": {
"Cluster": {
"Ref": "MyCluster"
},
"LaunchType": "FARGATE",
"NetworkConfiguration" : {
"AwsvpcConfiguration" : {
"AssignPublicIp" : "ENABLED",
"SecurityGroups" : [ {"Fn::GetAtt": ["MySecGroup", "GroupId"]} ],
"Subnets" : [ {"Ref": "MySubPublic"}]
}
},
"Service": {"Ref": "MyService"},
"TaskDefinition": {
"Ref": "MyDockerTask"
}
},
"DependsOn": [
"MyCluster"
]
},
None of the above provided the solution. I'm open to any suggestion/solution
From the CFN template provided I can conclude that MySubPublic has no internet access.
Specifically, I see that you are creating the following VPC-related resources:
AWS::EC2::VPC
AWS::EC2::InternetGateway
AWS::EC2::Subnet
AWS::EC2::SecurityGroup
However, the problem is the lack of:
AWS::EC2::VPCGatewayAttachment which will connect the InternetGateway to your VPC
Definitions of AWS::EC2::RouteTable and AWS::EC2::Route which will be used to make your MySubPublic route traffic to the AWS::EC2::InternetGateway.
General steps to enable internet access in a subnet are described here:
Attach an internet gateway to your VPC.
Add a route to your subnet's route table that directs internet-bound traffic to the internet gateway. If a subnet is
associated with a route table that has a route to an internet gateway,
it's known as a public subnet. If a subnet is associated with a route
table that does not have a route to an internet gateway, it's known as
a private subnet.
Ensure that instances in your subnet have a globally unique IP address (public IPv4 address, Elastic IP address, or IPv6 address).
Ensure that your network access control lists and security group rules allow the relevant traffic to flow to and from your instance.

Add a parameterized list of security groups to another security group's ingress

I'd like to create a CloudFormation template that creates a security group resource that allows ingress from a variable list of other security groups. The template would take a parameter of type List<AWS::EC2::SecurityGroup::Id>. I'll name this parameter SourceSecurityGroupIds for this example. Then, it would create a security group resource using something like:
{
"LogServerSecurityGroup": {
"Type": "AWS::EC2::SecurityGroup",
"Properties": {
"GroupDescription": "XYZ security group",
"VpcId": "vpc-abcxyz",
"SecurityGroupIngress": [
{
"IpProtocol": "tcp",
"FromPort": 1234,
"ToPort": 1234,
"SourceSecurityGroupId": { "Ref": "SourceSecurityGroupIds" }
}
]
}
}
}
Of course, the SourceSecurityGroupId property of SecurityGroupIngress doesn't take a list. Is there a way to make this work?
Update - Feb 27, 2019
In retrospect, the correct way to do this is to create a LogSourceSecurityGroup, and allow ingress only from that security group. Then, add that security group to any EC2 instance, etc that should be able to communicate with the log server.
I know it's late so you already figure it out, but I just run into this same issue and I was able to fix it. You need to create a "Security Group Ingress" resource that will add the rule to an existing security group, so it would be like:
{
"LogServerSecurityGroup": {
"Type": "AWS::EC2::SecurityGroup",
"Properties": {
"GroupDescription": "XYZ security group",
"VpcId": "vpc-abcxyz"
}
},
"LogServerSecurityGroupIngress" : {
"Type" : "AWS::EC2::SecurityGroupIngress",
"Properties" : {
"GroupId" : {"Ref" : "LogServerSecurityGroup"},
"IpProtocol" : "tcp",
"FromPort" : "1234",
"ToPort" : "1234",
"SourceSecurityGroupId" : {"Ref" : "SourceSecurityGroupIds"}
}
}
}
You can find more information here:
https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-ec2-security-group-ingress.html#cfn-ec2-security-group-ingress-groupid
SecurityGroupIngress parameter above is an array/list. So, define multiple ingress rules there.
e.g:
"SecurityGroupIngress": [
{
"IpProtocol": "tcp",
"FromPort": 1234,
"ToPort": 1234,
"SourceSecurityGroupId": "SG-12345"
},
{
"IpProtocol": "tcp",
"FromPort": 1234,
"ToPort": 1234,
"SourceSecurityGroupId": "SG-abcde"
},
{
"IpProtocol": "tcp",
"FromPort": 1234,
"ToPort": 1234,
"SourceSecurityGroupId": "SG-54321"
}
]

AWS Cloud Formation RDS security group in VPC

I am trying to add ingress rules to a security group for an RDS instance, but when I add them it says The security group 'sg-14820a71' does not exist in default VPC 'vpc-527a8037'.
I have explicitly specified the VPC I want the security group in, it should not be using the default. Any idea on how to make it use the correct VPC?
"Resources": {
"DbVpcSecurityGroup": {
"DependsOn": ["VPC", "BeanstalkSecurityGroup"],
"Type": "AWS::EC2::SecurityGroup",
"Properties": {
"GroupDescription": "Security group for RDS DB Instance.",
"VpcId": {
"Ref": "VPC"
},
"SecurityGroupIngress": [{
"IpProtocol": "tcp",
"FromPort": 3306,
"ToPort": 3306,
"SourceSecurityGroupName": {
"Ref": "BeanstalkSecurityGroup"
}
}]
}
}
}
It work fine with no ingress rules (an creating the manually in the GUI):
"Resources": {
"DbVpcSecurityGroup": {
"DependsOn": ["VPC", "BeanstalkSecurityGroup"],
"Type": "AWS::EC2::SecurityGroup",
"Properties": {
"GroupDescription": "Security group for RDS DB Instance.",
"VpcId": {
"Ref": "VPC"
},
}
}
}
This is the beanstalk security group:
"BeanstalkSecurityGroup": {
"DependsOn": ["VPC"],
"Type": "AWS::EC2::SecurityGroup",
"Properties": {
"GroupDescription": "Allow the Elastic Beanstalk instances to access the NAT device",
"VpcId": {
"Ref": "VPC"
},
}
}
While working in VPC, we should always use Security Group ID instead of Security Group Name.
So in this case of CloudFormation, use SourceSecurityGroupOwnerId instead of SourceSecurityGroupName

How do I create an Ec2 Instance with a Public IP automatically **without** declaring an Elastic IP in cloudformation?

In AWS Cloudformation, is there any way to declare an EC2 instance in VPC with a Public IP without the need to declare an Elastic IP and attach to it?
In AWS::AutoScaling::LaunchConfiguration, you could add a property "AssociatePublicIpAddress" to say the instances will accept a Public IP automatically. I am looking for the equivalent for AWS::EC2::Instance
Below is my cloudformation snippet for creating an EC2 instance. I can't any doc that mentions how to add a public IP without having to declare an Elastic IP beforehand.
"MyEc2Instance": {
"Type": "AWS::EC2::Instance",
"Properties": {
"IamInstanceProfile": {
"Ref": "MyEc2InstanceProfile"
},
"ImageId": {
"Fn::FindInMap": [
"MyEc2Box",
{
"Ref": "Region"
},
"ImageId"
]
},
"InstanceType": {
"Fn::FindInMap": [
"MyEc2Box",
{
"Ref": "Region"
},
"InstanceType"
]
},
"KeyName": {
"Ref": "DefaultKeyPair"
},
"Monitoring": "true",
"SecurityGroupIds": [
{
"Ref": "MyEc2SecurityGroup"
}
],
"SubnetId": {
"Ref": "MyBoxSubnet"
},
"Tags": [
{
"Key": "Name",
"Value": "MyBox"
},
]
}
},
Assuming you are starting your instance in a VPC public subnet (i.e. a subnet that has a routing table incl. a rule to send traffic to 0.0.0.0/0 to the Internet Gateway), just define AssociatePublicIpAddress property in the NetworkInterfaces group of your EC2 resource:
"NetworkInterfaces" : [{
"AssociatePublicIpAddress" : "True",
"DeleteOnTermination" : "True",
"SubnetId" : { "Ref" : "PublicSubnet" },
"DeviceIndex" : "0",
"GroupSet" : [ { "Ref" : "SecurityGroup" } ]
}],
See documentation at http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-ec2-network-iface-embedded.html
If you are starting your instance in EC2 Classic networking (non VPC), it will receive a public IP address automatically.
I see that this is an old post but i post the answer anyway it can be helpful.
In the subnet you can set : "MapPublicIpOnLaunch" to True so all the instance of this subnet will have a public IP.
MapPublicIpOnLaunch
Indicates whether instances that are launched in this subnet receive a public IP address. By default, the value is false.
Required: No
Type: Boolean
Update requires: No interruption.

How do I assign a created SecurityGroup to an ELB from CloudFormation?

I've got a CloudFormation script that generates a SecurityGroup and an ELB; I'm trying to reference the SecurityGroup in the ELB creation; here's the resources bit:
"ELBSecurityGroup" : {
"Type" : "AWS::EC2::SecurityGroup",
"Properties" : {
"GroupDescription" : "Security group for the Arena dev stack",
"SecurityGroupIngress" : [
{"IpProtocol" : "tcp", "FromPort" : 80, "ToPort" : 80, "CidrIp" : { "Ref" : "OfficeIp" }}
]
}
},
"ProjectLoadBalancerTest" : {
"Type" : "AWS::ElasticLoadBalancing::LoadBalancer",
"Properties" : {
"AvailabilityZones" : { "Fn::GetAZs" : "" },
"Instances" : [ ],
"Listeners" : [ {
"LoadBalancerPort" : "80",
"InstancePort" : "12345",
"Protocol" : "HTTP"
} ],
"HealthCheck" : {
"Target" : {
"Fn::Join" : [ "", [ "HTTP:", "12345", "/status.json" ] ]
},
"HealthyThreshold" : "2",
"UnhealthyThreshold" : "5",
"Interval" : "60",
"Timeout" : "30"
},
"SecurityGroups" : [
{ "Ref" : "ELBSecurityGroup" }
]
}
}
Unfortunately, this fails with:
Invalid id: "sebelbtest2-ELBSecurityGroup-1F5Z5DIIVQKD1" (expecting "sg-...")
So how can I reference ELBSecurityGroup for use as a property in the ELB creation?
Thanks!
As mytwocents mentioned, the solution is to use Fn::GetAtt. SecurityGroups are now supported by this function: http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/intrinsic-function-reference-getatt.html
This works on the ELB:
...
"SecurityGroups" : [
{ "Fn::GetAtt" : [ "ELBSecurityGroup", "GroupId" ] }
]
...
Note. If you're putting this into a non-default VPC you'll also need to specify the VPC for the security group, and a subnet ID for the ELB.
As my CloudFormation script is all done within a VPC, I figured out what the problem was - I was creating the Security group, but not specifying the VpcId for it.
Security groups appear to be either normal security groups, or VPC security groups; if you do { "Ref": "MySecurityGroup" } on a normal one, you get the security group name, but not the ID. If you do { "Ref": "MySecurityGroup" } on a VPC one, you get back the sg-abcdef id, which is what is required for the ELB security group parameter.
So the full answer is:
"ELBSecurityGroup" : {
"Type" : "AWS::EC2::SecurityGroup",
"Properties" : {
"GroupDescription" : "Security group for the ELB",
"VpcId" : { "Ref" : "VpcId" },
"SecurityGroupIngress" : [
{"IpProtocol" : "tcp", "FromPort" : 80, "ToPort" : 80, "CidrIp" : { "Ref" : "OfficeIp" }}
]
}
},
"MyELB": {
"Type" : "AWS::ElasticLoadBalancing::LoadBalancer",
"Properties" : {
"AvailabilityZones" : { "Fn::GetAZs" : "" },
"Listeners" : [ {
"LoadBalancerPort" : "80",
"InstancePort" : 8000,
"Protocol" : "HTTP"
} ],
"SecurityGroups" : [ { "Ref" : "ELBSecurityGroup" } ]
}
}
This all works perfectly (provided everything you're doing is within your VPC) and in my configuration, will successfully limit access to whatever OfficeIP is set to.
Hmmm…
It appears as though the template is returning the Security Group's name instead of its ID.
Based on the documentation, it would appear that SecurityGroups can only be attached to a load balancer in a VPC.
If it were me, I would do one of two things (or both):
Post my question to the official AWS CloudFormation forum to try and get it answered from someone on that team.
Use the AWS Console, SDKs or CLI tools to set up your environment the way you want, then use the CloudFormer tool to generate a CloudFormation template that matches your environment.
I had the same issue - but decided that since this is just for initial set-up, the elastic load balancer can be managed seperate from cloud formation until they work this bug out. Also, cloud former is not totally reliable especially when you have security groups referencing other security groups.
I was hopeful that a GetAttr function call for the ID would work, but Security Groups are not in the supported list of GetAttr:
http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/intrinsic-function-reference-getatt.html
Try this for your SecurityGroup:
"ELBSecurityGroup" : {
"Type" : "AWS::EC2::SecurityGroup",
"Properties" : {
"GroupDescription" : "Security group for the Arena dev stack",
"SecurityGroupIngress" : [ {
"IpProtocol" : "tcp",
"FromPort" : 80,
"ToPort" : 80,
"SourceSecurityGroupOwnerId" : { "Fn::GetAtt" : [ "ProjectLoadBalancerTest", "SourceSecurityGroup.OwnerAlias" ] },
"SourceSecurityGroupName" : { "Fn::GetAtt" : [ "ProjectLoadBalancerTest", "SourceSecurityGroup.GroupName" ] }
} ]
}
},
and remove "SecurityGroups" from your ELB definition. This will allow traffic from your ELB to your app servers.
It looks like you're also looking to limit ELB traffic to your office IP. That would be better done with something like apache directives.