def _get_resources(self): resources = [ Resource( "CoreOSSecurityGroup", "AWS::EC2::SecurityGroup", Properties({ "VpcId": { "Ref": "VPC" }, "GroupDescription": "CoreOS SecurityGroup", "SecurityGroupIngress": [{ "CidrIp": "10.0.0.0/16", "IpProtocol": "udp", "FromPort": "0", "ToPort": "65535" }, { "CidrIp": "10.0.0.0/16", "IpProtocol": "icmp", "FromPort": "-1", "ToPort": "-1" }], "Tags": [{ "Key": "Name", "Value": "CoreOSSecurityGroup" }] })), Resource( "CoreOSSecurityGroup2380Ingress", "AWS::EC2::SecurityGroupIngress", Properties({ "GroupId": { "Ref": "CoreOSSecurityGroup" }, "IpProtocol": "tcp", "FromPort": "2380", "ToPort": "2380", # "SourceSecurityGroupId": {"Ref": "CoreOSSecurityGroup"} # TODO not working for now because need to use fleetctl locally to load units "CidrIp": "10.0.0.0/16" }), attributes=[DependsOn("CoreOSSecurityGroup")]), Resource( "CoreOSSecurityGroup2379Ingress", "AWS::EC2::SecurityGroupIngress", Properties({ "GroupId": { "Ref": "CoreOSSecurityGroup" }, "IpProtocol": "tcp", "FromPort": "2379", "ToPort": "2379", # "SourceSecurityGroupId": {"Ref": "CoreOSSecurityGroup"} # TODO not working for now because need to use fleetctl locally to load units "CidrIp": "10.0.0.0/16" }), attributes=[DependsOn("CoreOSSecurityGroup")]) ] resources += self._get_etcd_cluster_resources() resources += self._get_coreos_resources() return resources
def _get_resources(self): resources = self._get_autoscale( 'SSHBastion', extra_props_autoscale={ "VPCZoneIdentifier": [{ "Ref": subnet_name } for subnet_name in self.data['public_subnets']], }, extra_props_launch={ "AssociatePublicIpAddress": "true", "BlockDeviceMappings": [{ "DeviceName": "/dev/xvda", "Ebs": { "VolumeSize": 10 } }] }, extra_attrs_launch=[DependsOn("GatewayToInternet")], extra_security_groups=['SSHBastionSecurityGroup']) return resources
cft.resources.add(Resource( 'VpcRouteTable', 'AWS::EC2::RouteTable', Properties({ 'VpcId': ref('RelengVPC'), 'Tags': [nametag('Releng VPC Route Table')], }) )) cft.resources.add(Resource( 'VPCToScl3', 'AWS::EC2::Route', Properties({ 'DestinationCidrBlock': '0.0.0.0/0', 'GatewayId': ref('Scl3VPNGateway'), 'RouteTableId': ref('VpcRouteTable'), }), DependsOn(['Scl3VPNConnection', 'Scl3VPCGatewayAttachment']), )) for n, cidr in enumerate(igw_routed_cidrs, 1): cft.resources.add(Resource( 'VPCToCidr{}'.format(n), 'AWS::EC2::Route', Properties({ 'DestinationCidrBlock': cidr, 'GatewayId': ref('IGW'), 'RouteTableId': ref('VpcRouteTable'), }), DependsOn(['IGW', 'IGWAttachment']), )) for hostname in igw_routed_hosts: camelcaps = ''.join(a.title() for a in re.split('[^a-z0-9]', hostname))
def build_template(args): """ Build a CloudFormation template allowing for secure CloudTrail log aggregation and fine grained access control to SNS topics for notifications of new CloudTrail logs The reason that we create IAM roles for each client AWS account in order to enable clients to read their own CloudTrail logs, instead of merely delegating access to them in an S3 bucket policy is that "Bucket owner account can delegate permissions to users in its own account, but it cannot delegate permissions to other AWS accounts, because cross-account delegation is not supported." : http://docs.aws.amazon.com/AmazonS3/latest/dev/example-walkthroughs-managing-access-example4.html As a consequence we *can* delegate bucket permissions to client AWS accounts but we *can not* delegate object permissions (the log files themselves) to client AWS accounts. Example config : AccountRootARNs: - arn:aws:iam::012345678901:root # Sales - arn:aws:iam::123456789012:root # HR - arn:aws:iam::234567890123:root # Marketing CloudTrailLogConsumers: - arn:aws:iam::345678901234:user/security_team # Security team user - TrustedARN: arn:aws:iam::456789012343:root # CloudCo Third Party TrustingARNs: - arn:aws:iam::012345678901:root # Sales - arn:aws:iam::234567890123:root # Marketing - TrustedARN: arn:aws:iam::567890123434:root # Other.com Third Party TrustingARNs: - arn:aws:iam::123456789012:root # HR ForeignAccountStatusSubscribers: - arn:aws:iam::345678901234:root # Security Team """ config = args.config account_root_arns = ( config['AccountRootARNs'] if 'AccountRootARNs' in config and isinstance(config['AccountRootARNs'], list) else []) cft = CloudFormationTemplate( description="AWS CloudTrail Storage Account S3 Storage Bucket") # Create the bucket cft.resources.add( Resource("S3Bucket", "AWS::S3::Bucket", {"BucketName": args.bucketname}, DeletionPolicy("Retain"))) # Build the s3 bucket policy statement list bucket_policy_statements = [] # Allow the CloudTrail system to GetBucketAcl on the CloudTrail storage # bucket bucket_policy_statements.append({ "Sid": "AWSCloudTrailAclCheck", "Effect": "Allow", "Principal": { "Service": "cloudtrail.amazonaws.com" }, "Action": ["s3:GetBucketAcl"], "Resource": join("", "arn:aws:s3:::", ref("S3Bucket")) }) # Allow each account to read it's own logs for account_arn in account_root_arns: account_id = get_account_id_from_arn(account_arn) cft.resources.add( Resource( "CloudTrailLogReaderRole%s" % account_id, "AWS::CloudFormation::Stack", { "TemplateURL": "https://s3.amazonaws.com/infosec-cloudformation-templates/manage_iam_role.json", "Parameters": { "RoleName": "CloudTrailLogReader%s" % account_id, "TrustedEntities": get_consumer_arns( account_arn, config) }, "TimeoutInMinutes": "5" })) cft.resources.add( Resource( "CloudTrailLogReaderPolicy%s" % account_id, "AWS::IAM::Policy", { "PolicyName": "CloudTrailLogReaderPolicy%s" % account_id, "PolicyDocument": { "Version": "2012-10-17", "Statement": [{ "Effect": "Allow", "Action": "s3:GetObject", "Resource": join("", "arn:aws:s3:::", ref("S3Bucket"), "/AWSLogs/%s/*" % account_id) }] }, "Roles": ["CloudTrailLogReader%s" % account_id] }, DependsOn("CloudTrailLogReaderRole%s" % account_id))) cft.resources.add( Resource( "ReadCloudTrailBucket", "AWS::IAM::ManagedPolicy", { "Description": "ReadCloudTrailBucket", "PolicyDocument": { "Version": "2012-10-17", "Statement": [{ "Effect": "Allow", "Action": ["s3:ListAllMyBuckets", "s3:GetBucketLocation"], "Resource": "*" }, { "Effect": "Allow", "Action": [ "s3:GetBucketAcl", "s3:ListBucket", "s3:GetBucketTagging" ], "Resource": join("", "arn:aws:s3:::", ref("S3Bucket")) }] }, "Roles": [ "CloudTrailLogReader%s" % get_account_id_from_arn(account_arn) for account_arn in account_root_arns ] }, DependsOn([ "CloudTrailLogReaderRole%s" % get_account_id_from_arn(account_arn) for account_arn in account_root_arns ]))) bucket_policy_statements.append({ # "Sid":"AWSCloudTrailWrite%s" % get_account_id_from_arn(account_arn), "Effect": "Allow", "Principal": { "Service": "cloudtrail.amazonaws.com" }, "Action": ["s3:PutObject"], "Resource": join("", "arn:aws:s3:::", ref("S3Bucket"), "/AWSLogs/*"), "Condition": { "StringEquals": { "s3:x-amz-acl": "bucket-owner-full-control" } } }) # Apply the bucket policy to the bucket cft.resources.add( Resource( "BucketPolicy", "AWS::S3::BucketPolicy", { "Bucket": ref("S3Bucket"), "PolicyDocument": { "Id": "BucketPolicyDocument", "Version": "2012-10-17", "Statement": bucket_policy_statements } })) # Create a single SNS Topic that each AWS account can publish to to report # on the CloudFormation progress cft.resources.add( Resource( "ForeignAccountStatusTopic", "AWS::SNS::Topic", { "DisplayName": "Topic for foreign accounts to publish status information to", "TopicName": "ForeignAccountStatus" })) cft.resources.add( Resource( "ForeignAccountStatusTopicPolicy", "AWS::SNS::TopicPolicy", { "Topics": [ref("ForeignAccountStatusTopic")], "PolicyDocument": { "Version": "2012-10-17", "Id": "ForeignAccountStatusPolicy", "Statement": [{ "Sid": "ForeignAccountStatusPublisher", "Effect": "Allow", "Principal": { "AWS": account_root_arns }, "Action": "SNS:Publish", "Resource": ref("ForeignAccountStatusTopic"), }, { "Sid": "ForeignAccountStatusSubscriber", "Effect": "Allow", "Principal": { "AWS": config['ForeignAccountStatusSubscribers'] }, "Action": [ "SNS:GetTopicAttributes", "SNS:ListSubscriptionsByTopic", "SNS:Subscribe" ], "Resource": ref("ForeignAccountStatusTopic"), }] } })) # Create SNS Topics for each AWS account and grant those accounts rights # to publish and subscribe to those topics for account_arn in account_root_arns: account_id = get_account_id_from_arn(account_arn) cft.resources.add( Resource( "Topic%s" % account_id, "AWS::SNS::Topic", { "DisplayName": "Mozilla CloudTrail Logs Topic for Account %s" % account_id, "TopicName": "MozillaCloudTrailLogs%s" % account_id })) # http://docs.aws.amazon.com/sns/latest/dg/AccessPolicyLanguage_UseCases_Sns.html#AccessPolicyLanguage_UseCase4_Sns cft.resources.add( Resource( "TopicPolicy%s" % account_id, "AWS::SNS::TopicPolicy", { "Topics": [ref("Topic%s" % account_id)], "PolicyDocument": { "Version": "2012-10-17", "Id": "AWSCloudTrailSNSPolicy%s" % account_id, "Statement": [{ "Sid": "CloudTrailSNSPublish%s" % account_id, "Effect": "Allow", "Principal": { "Service": "cloudtrail.amazonaws.com" }, "Action": "SNS:Publish", "Resource": ref("Topic%s" % account_id) }, { "Sid": "CloudTrailSNSSubscribe%s" % account_id, "Effect": "Allow", "Principal": { "AWS": account_arn }, "Action": [ "SNS:GetTopicAttributes", "SNS:ListSubscriptionsByTopic", "SNS:Subscribe" ], "Resource": join(":", "arn:aws:sns", ref("AWS::Region"), ref("AWS::AccountId"), "MozillaCloudTrailLogs%s" % account_id) }] } })) return cft
def _get_resources(self): return [ Resource( "NATSecurityGroup", "AWS::EC2::SecurityGroup", Properties({ "VpcId": { "Ref": "VPC" }, "GroupDescription": "NAT Instance Security Group", "SecurityGroupIngress": [{ "IpProtocol": "icmp", "FromPort": "-1", "ToPort": "-1", "CidrIp": "10.0.0.0/16" }, { "IpProtocol": "tcp", "FromPort": "0", "ToPort": "65535", "CidrIp": "10.0.0.0/16" }], "Tags": [{ "Key": "Name", "Value": "NATSecurityGroup" }] })), Resource("NATInstance", "AWS::EC2::Instance", Properties({ "ImageId": { "Fn::FindInMap": ["RegionMap", { "Ref": "AWS::Region" }, "nat"] }, "InstanceType": { "Ref": "NATInstanceType" }, "BlockDeviceMappings": [{ "DeviceName": "/dev/xvda", "Ebs": { "VolumeSize": 10 } }], "NetworkInterfaces": [{ "GroupSet": [{ "Ref": "NATSecurityGroup" }, { "Ref": "SSHFromBastionSecurityGroup" }], "SubnetId": { "Ref": self.data['public_subnets'][0] }, "AssociatePublicIpAddress": "true", "DeviceIndex": "0", "DeleteOnTermination": "true" }], "SourceDestCheck": "false", "Tags": [{ "Key": "Name", "Value": "NATHost" }, { "Key": "Role", "Value": "NAT" }], "UserData": { "Fn::Base64": { "Fn::Join": ["", self.get_user_cloud_config()] } } }), attributes=[DependsOn("GatewayToInternet")]) ]
def _get_private_subnets(self): resources = [ Resource( "PrivateRouteTable", "AWS::EC2::RouteTable", Properties({ "VpcId": { "Ref": "VPC" }, "Tags": [{ "Key": "Name", "Value": "PrivateRouteTable" }] })), Resource("PrivateInternetRoute", "AWS::EC2::Route", Properties({ "RouteTableId": { "Ref": "PrivateRouteTable" }, "DestinationCidrBlock": "0.0.0.0/0", "InstanceId": { "Ref": "NATInstance" } }), attributes=[DependsOn("NATInstance")]), ] for subnet_name in self.data['private_subnets']: table_association_name = "%sRouteTableAssociation" % subnet_name resources += [ Resource( subnet_name, "AWS::EC2::Subnet", Properties({ "VpcId": { "Ref": "VPC" }, "AvailabilityZone": { "Fn::FindInMap": ["SubnetConfig", subnet_name, "AZ"] }, "CidrBlock": { "Fn::FindInMap": ["SubnetConfig", subnet_name, "CIDR"] }, "Tags": [{ "Key": "Application", "Value": { "Ref": "AWS::StackId" } }, { "Key": "Network", "Value": subnet_name }] })), Resource( table_association_name, "AWS::EC2::SubnetRouteTableAssociation", Properties({ "SubnetId": { "Ref": subnet_name }, "RouteTableId": { "Ref": "PrivateRouteTable" } })), ] return resources