def get_template(region, coreos_ami, dns_suffix):
    cft = CloudFormationTemplate(description='Core OS on EC2 app cluster')

    data = {
        'region': region,
        'coreos_ami': coreos_ami,
        'dns_suffix': dns_suffix
    }

    for Conf in CONFIGs:
        data.update(Conf(data=data).add(cft))
    return cft
Example #2
0
def subnet_az(index):
    azs = availability_zones[options['region']]
    return azs[index % len(azs)]


def resolve_host(hostname):
    ips = dns.resolver.query(hostname, "A")
    # sort these so that we deterministically set up the same route
    # resources (until DNS changes)
    ips = sorted([i.to_text() for i in ips])
    return ips

# VPC

cft = CloudFormationTemplate(description="Release Engineering network configuration")

cft.resources.add(Resource(
    'RelengVPC', 'AWS::EC2::VPC',
    Properties({
        'CidrBlock': subnet_cidr('0.0', 16),
        'Tags': [nametag('Releng Network')],
    })
))

# DHCP options

cft.resources.add(Resource(
    'DHCPOptions', 'AWS::EC2::DHCPOptions',
    Properties({
        # point to the onsite, IT-managed DNS servers
Example #3
0
def make_storage_template():
    cft = CloudFormationTemplate(description="Refinery Platform storage")

    # Parameters
    cft.parameters.add(
        Parameter('StaticBucketName', 'String', {
            'Description': 'Name of S3 bucket for Django static files',
        }))
    cft.parameters.add(
        Parameter(
            'MediaBucketName',
            'String',
            {
                'Description':
                'Name of S3 bucket for Django media files',
                # make names DNS-compliant without periods (".") for
                # compatibility with virtual-hosted-style access and S3
                # Transfer Acceleration
                'AllowedPattern':
                '[a-z0-9\-]+',
                'ConstraintDescription':
                'must only contain lower case letters, numbers, and '
                'hyphens',
            }))
    cft.parameters.add(
        Parameter(
            'IdentityPoolName', 'String', {
                'Default': 'Refinery Platform',
                'Description': 'Name of Cognito identity pool for S3 uploads',
            }))
    cft.parameters.add(
        Parameter(
            'DeveloperProviderName', 'String', {
                'Default':
                'login.refinery',
                'Description':
                '"domain" by which Cognito will refer to users',
                'AllowedPattern':
                '[a-z\-\.]+',
                'ConstraintDescription':
                'must only contain lower case letters, periods, '
                'underscores, and hyphens'
            }))

    # Resources
    cft.resources.add(
        Resource(
            'StaticStorageBucket',
            'AWS::S3::Bucket',
            Properties({
                'BucketName': ref('StaticBucketName'),
                'AccessControl': 'PublicRead',
                'CorsConfiguration': {
                    'CorsRules': [{
                        'AllowedOrigins': ['*'],
                        'AllowedMethods': ['GET'],
                        'AllowedHeaders': ['Authorization'],
                        'MaxAge': 3000,
                    }]
                },
            }),
            DeletionPolicy('Retain'),
        ))
    cft.resources.add(
        Resource(
            'MediaStorageBucket',
            'AWS::S3::Bucket',
            Properties({
                'BucketName': ref('MediaBucketName'),
                'AccessControl': 'PublicRead',
                'CorsConfiguration': {
                    'CorsRules': [{
                        'AllowedOrigins': ['*'],
                        'AllowedMethods': ['POST', 'PUT', 'DELETE'],
                        'AllowedHeaders': ['*'],
                        'ExposedHeaders': ['ETag'],
                        'MaxAge': 3000,
                    }]
                }
            }),
            DeletionPolicy('Retain'),
        ))
    # Cognito Identity Pool for Developer Authenticated Identities Authflow
    # http://docs.aws.amazon.com/cognito/latest/developerguide/authentication-flow.html
    cft.resources.add(
        Resource(
            'IdentityPool', 'AWS::Cognito::IdentityPool',
            Properties({
                'IdentityPoolName': ref('IdentityPoolName'),
                'AllowUnauthenticatedIdentities': False,
                'DeveloperProviderName': ref('DeveloperProviderName'),
            })))
    cft.resources.add(
        Resource(
            'IdentityPoolAuthenticatedRole',
            'AWS::Cognito::IdentityPoolRoleAttachment',
            Properties({
                'IdentityPoolId': ref('IdentityPool'),
                'Roles': {
                    'authenticated': get_att('CognitoS3UploadRole', 'Arn'),
                }
            })))
    upload_role_trust_policy = {
        "Version":
        "2012-10-17",
        "Statement": [{
            "Effect": "Allow",
            "Principal": {
                "Federated": "cognito-identity.amazonaws.com"
            },
            "Action": "sts:AssumeRoleWithWebIdentity",
            "Condition": {
                "StringEquals": {
                    "cognito-identity.amazonaws.com:aud": ref('IdentityPool')
                },
                "ForAnyValue:StringLike": {
                    "cognito-identity.amazonaws.com:amr": "authenticated"
                }
            }
        }]
    }
    upload_access_policy = {
        "Version":
        "2012-10-17",
        "Statement": [{
            "Effect": "Allow",
            "Action": ["cognito-identity:*"],
            "Resource": "*"
        }, {
            "Action": ["s3:PutObject", "s3:AbortMultipartUpload"],
            "Effect": "Allow",
            "Resource": {
                "Fn::Sub":
                "arn:aws:s3:::${MediaStorageBucket}/uploads/"
                "${!cognito-identity.amazonaws.com:sub}/*"
            }
        }]
    }
    cft.resources.add(
        Resource(
            'CognitoS3UploadRole', 'AWS::IAM::Role',
            Properties({
                'AssumeRolePolicyDocument':
                upload_role_trust_policy,
                'Policies': [{
                    'PolicyName': 'AuthenticatedS3UploadPolicy',
                    'PolicyDocument': upload_access_policy,
                }]
            })))

    # Outputs
    cft.outputs.add(
        Output('IdentityPoolId', ref('IdentityPool'),
               {'Fn::Sub': '${AWS::StackName}IdentityPoolId'},
               'Cognito identity pool ID'))

    return cft
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.

from cfn_pyplates.core import CloudFormationTemplate, Resource
from cfn_pyplates.core import Properties, options
from utils import nametag

cft = CloudFormationTemplate(description="Tooltool Infrastructure")

rgn = options['region']

# production

cft.resources.add(Resource(
    'FileBucket', 'AWS::S3::Bucket',
    Properties({
        "AccessControl": "Private",
        "BucketName": "mozilla-releng-%s-tooltool" % (rgn,),
        'Tags': [nametag('Tooltool File Storage - %s' % (rgn,))],
    })
))

# staging

cft.resources.add(Resource(
    'StagingFileBucket', 'AWS::S3::Bucket',
    Properties({
        "AccessControl": "Private",
        "BucketName": "mozilla-releng-staging-%s-tooltool" % (rgn,),
        'Tags': [nametag('Tooltool File Storage - Staging - %s' % (rgn,))],
Example #5
0
def make_storage_template():
    cft = CloudFormationTemplate(description="Refinery Platform storage")
    # Parameters
    cft.parameters.add(
        Parameter('StaticBucketName', 'String', {
            'Description': 'Name of S3 bucket for Django static files',
        }))
    cft.parameters.add(
        Parameter(
            'MediaBucketName',
            'String',
            {
                'Description':
                'Name of S3 bucket for Django media files',
                # make names DNS-compliant without periods (".") for compatibility
                # with virtual-hosted-style access and S3 Transfer Acceleration
                'AllowedPattern':
                '[a-z0-9\-]+',
                'ConstraintDescription':
                'must only contain lower case letters, numbers, and hyphens',
            }))
    # Resources
    cft.resources.add(
        Resource(
            'StaticStorageBucket',
            'AWS::S3::Bucket',
            Properties({
                'BucketName': ref('StaticBucketName'),
                'AccessControl': 'PublicRead',
                'CorsConfiguration': {
                    'CorsRules': [{
                        'AllowedOrigins': ['*'],
                        'AllowedMethods': ['GET'],
                        'AllowedHeaders': ['Authorization'],
                        'MaxAge': 3000,
                    }]
                },
            }),
            DeletionPolicy('Retain'),
        ))
    cft.resources.add(
        Resource(
            'MediaStorageBucket',
            'AWS::S3::Bucket',
            Properties({
                'BucketName': ref('MediaBucketName'),
                'AccessControl': 'PublicRead',
                'CorsConfiguration': {
                    'CorsRules': [{
                        'AllowedOrigins': ['*'],
                        'AllowedMethods': ['POST', 'PUT', 'DELETE'],
                        'AllowedHeaders': ['*'],
                        'ExposedHeaders': ['ETag'],
                        'MaxAge': 3000,
                    }]
                }
            }),
            DeletionPolicy('Retain'),
        ))
    cft.outputs.add(
        Output('MediaBucketName', ref('MediaStorageBucket'),
               {'Fn::Sub': '${AWS::StackName}Media'},
               'Name of S3 bucket for Django media files'))

    return cft
Example #6
0
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
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.

from cfn_pyplates.core import CloudFormationTemplate, Resource
from cfn_pyplates.core import Properties, options
from utils import policy

is_prod = options['prod']

cft = CloudFormationTemplate(
    description="IAM for RelengAPI" if is_prod else "IAM For RelengAPI Staging")

# production

# RelengAPI is configured with an access/secret keypair for these users, but
# that keypair is not created here; it is generated via the console once the
# stack is deployed.

# Note that you cannot control the physical name of the users; they are ugly
# autogenerated names.  See
# http://stackoverflow.com/questions/16185109/how-to-set-user-name-and-group-name-in-iam-using-cloudformation

name = 'RelengAPI' if is_prod else 'RelengAPIStage'


def object_arn(rgn):
    return bucket_arn(rgn) + '/*'


def bucket_arn(rgn):
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.

from cfn_pyplates.core import CloudFormationTemplate, Resource
from cfn_pyplates.core import Properties, options
from utils import nametag
from utils import sgcidr

cft = CloudFormationTemplate(description="IT Resources")

cft.resources.add(Resource(
    'NagiosSG', 'AWS::EC2::SecurityGroup',
    Properties({
        'GroupDescription': 'Nagios Servers',
        'Tags': [nametag('nagios')],
        'VpcId': options['vpcid'],
        'SecurityGroupIngress': [
            sgcidr('10.22.8.128/32', -1, -1),
            sgcidr('10.22.20.0/25', -1, -1),
            sgcidr('10.22.72.136/32', -1, -1),
            sgcidr('10.22.72.155/32', -1, -1),
            sgcidr('10.22.72.158/32', -1, -1),
            sgcidr('10.22.72.159/32', -1, -1),
            sgcidr('10.22.75.5/32', -1, -1),
            sgcidr('10.22.75.6/31', -1, -1),
            sgcidr('10.22.240.0/20', -1, -1),
            sgcidr('10.22.74.22/32', -1, -1),
            sgcidr('10.22.75.30/32', -1, -1),
            sgcidr('10.22.75.36/32', 'tcp', 22),
            sgcidr('10.22.75.136/32', 'udp', 161),
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.

from cfn_pyplates.core import CloudFormationTemplate, Resource
from cfn_pyplates.core import Properties, options
from utils import nametag

cft = CloudFormationTemplate(description="Archiver Infrastructure")

rgn = options['region']

# production

cft.resources.add(Resource(
    'FileBucket', 'AWS::S3::Bucket',
    Properties({
        "AccessControl": "Private",
        "BucketName": "mozilla-releng-%s-archiver" % (rgn,),
        'Tags': [nametag('Archiver Archive Storage - %s' % (rgn,))],
    })
))

# staging

cft.resources.add(Resource(
    'StagingFileBucket', 'AWS::S3::Bucket',
    Properties({
        "AccessControl": "Private",
        "BucketName": "mozilla-releng-staging-%s-archiver" % (rgn,),
        'Tags': [nametag('Archiver Archive Storage - Staging - %s' % (rgn,))],