Beispiel #1
0
def find_gateway(aws_syncr, configuration):
    amazon = configuration['amazon']

    stage = aws_syncr.stage
    gateway = aws_syncr.artifact

    if 'apigateway' not in configuration:
        raise AwsSyncrError(
            "Please define apigateway in your configuration before trying to deploy a gateway"
        )

    if not gateway:
        raise AwsSyncrError(
            "Please specify --artifact for the gateway function to deploy")

    wanted = ['apigateway', gateway]
    if wanted not in configuration:
        raise AwsSyncrError("Couldn't find specified api gateway",
                            available=list(
                                configuration["apigateway"].items.keys()))
    gateway = configuration['apigateway'].items[gateway]

    if not stage:
        raise AwsSyncrError("Please specify --stage",
                            available=list(gateway.stage_names))

    return aws_syncr, amazon, stage, gateway
Beispiel #2
0
    def test(self, aws_syncr, amazon, stage):
        endpoint = aws_syncr.extra.strip()
        if not endpoint or " " not in endpoint:
            options = sorted(
                "{0} {1}".format(m, e)
                for m, e in list(self.available_methods_and_endpoints()))
            raise AwsSyncrError("{0}\n".format(
                dedent("""
            Please specify ' -- <http_method> <endpoint> ' at the end of the command

            For example:

                {0} -- <http_method> <endpoint>

            Where the available options are:

                {1}

            """.format(' '.join(sys.argv), '\n\t\t'.join(options)).strip())))

        method, endpoint = endpoint.split(" ", 1)
        sample_event, desired_output_for_test = self.find_sample_event(
            amazon, method, endpoint)

        self.validate_stage(amazon, stage)
        return amazon.apigateway.test_stage(self.gateway_info(amazon),
                                            self.location, stage, method,
                                            endpoint, sample_event,
                                            desired_output_for_test)
Beispiel #3
0
    def modify_bucket(self, bucket_info, name, permission_document, location, tags):
        current_location = self.client.get_bucket_location(Bucket=name)['LocationConstraint']
        if current_location != location:
            raise AwsSyncrError("Sorry, can't change the location of a bucket!", wanted=location, currently=current_location, bucket=name)

        # Make sure we use the correct endpoint to get info from the bucket
        # So that website buckets don't complain
        bucket_info.meta.client = self.amazon.session.client("s3", location)

        bucket_document = ""
        with self.ignore_missing():
            bucket_document = bucket_info.Policy().policy

        if bucket_document or permission_document:
            if permission_document and not bucket_document:
                with self.catch_boto_400("Couldn't add policy", "Bucket {0} policy".format(name), permission_document, bucket=name):
                    for _ in self.change("+", "bucket_policy", bucket=name, changes=list(Differ.compare_two_documents("{}", permission_document))):
                        bucket_info.Policy().put(Policy=permission_document)

            elif bucket_document and not permission_document:
                with self.catch_boto_400("Couldn't remove policy", "Bucket {0} policy".format(name), permission_document, bucket=name):
                    for _ in self.change("-", "bucket_policy", bucket=name, changes=list(Differ.compare_two_documents(bucket_document, "{}"))):
                        bucket_info.Policy().delete()

            else:
                changes = list(Differ.compare_two_documents(bucket_document, permission_document))
                if changes:
                    with self.catch_boto_400("Couldn't modify policy", "Bucket {0} policy".format(name), permission_document, bucket=name):
                        for _ in self.change("M", "bucket_policy", bucket=name, changes=changes):
                            bucket_info.Policy().put(Policy=permission_document)

        self.modify_tags(bucket_info, name, tags)
Beispiel #4
0
def execute_as(collector):
    """Execute a command (after the --) as an assumed role (specified by --artifact)"""
    # Gonna assume role anyway...
    collector.configuration['amazon']._validated = True

    # Find the arn we want to assume
    account_id = collector.configuration['accounts'][
        collector.configuration['aws_syncr'].environment]
    arn = "arn:aws:iam::{0}:role/{1}".format(
        account_id, collector.configuration['aws_syncr'].artifact)

    # Determine the command to run
    parts = shlex.split(collector.configuration["aws_syncr"].extra)
    if not parts:
        suggestion = " ".join(sys.argv) + " -- /path/to/command_to_run"
        msg = "No command was provided. Try something like:\n\t\t{0}".format(
            suggestion)
        raise AwsSyncrError(msg)

    # Get our aws credentials environment variables from the assumed role
    env = dict(os.environ)
    env.update(
        collector.configuration['amazon'].iam.assume_role_credentials(arn))

    # Turn into the command we want to execute
    os.execvpe(parts[0], parts, env)
Beispiel #5
0
def test_gateway(collector):
    """Specify <method> <endpoint> after -- from the commandline and that gateway endpoint will be requested"""
    collector.configuration['amazon']._validated = True
    configuration = collector.configuration
    aws_syncr = configuration['aws_syncr']
    aws_syncr, amazon, stage, gateway = find_gateway(aws_syncr, configuration)
    if not gateway.test(aws_syncr, amazon, stage):
        raise AwsSyncrError("Failed to test the gateway")
Beispiel #6
0
def test_lambda(collector):
    """Invoke a lambda function with the defined sample_event and compare against desired_output_for_test"""
    amazon = collector.configuration['amazon']
    amazon._validated = True
    aws_syncr = collector.configuration['aws_syncr']
    if not find_lambda_function(aws_syncr, collector.configuration).test(
            aws_syncr, amazon):
        raise AwsSyncrError("Failed to test the lambda")
Beispiel #7
0
def find_lambda_function(aws_syncr, configuration):
    lambda_function = aws_syncr.artifact

    if 'lambda' not in configuration:
        raise AwsSyncrError(
            "Please define lambda functions under the 'lambda' section of your configuration"
        )

    if not lambda_function:
        raise AwsSyncrError(
            "Please specify --artifact for the lambda function to deploy")

    wanted = ['lambda', lambda_function]
    if wanted not in configuration:
        raise AwsSyncrError("Couldn't find specified lambda function",
                            available=list(
                                configuration["lambda"].items.keys()))

    return configuration['lambda'].items[lambda_function]
Beispiel #8
0
def find_certificate_source(configuration, gateway, certificate):
    source = configuration.source_for(['apigateway', gateway, 'domain_names'])
    location = ["apigateway", gateway, 'domain_names']
    domain_names = configuration.get(location, ignore_converters=True)

    for name, domain in domain_names.items():
        if 'zone' in domain:
            zone = MergedOptionStringFormatter(
                configuration,
                '.'.join(location + ['zone']),
                value=domain.get('zone')).format()
            domain_name = "{0}.{1}".format(name, zone)
            if domain_name == certificate:
                if 'certificate' not in domain:
                    domain['certificate'] = {}

                var = domain['certificate']

                class StickyChain(object):
                    def __init__(self):
                        self.lst = []

                    def __add__(self, other):
                        self.lst.extend(other)
                        return self.lst

                    def __contains__(self, item):
                        return item in self.lst

                    def __getitem__(self, index):
                        return self.lst[index]

                chain = StickyChain()

                if isinstance(var, six.string_types):
                    result = MergedOptionStringFormatter(configuration,
                                                         '.'.join(location),
                                                         value=var,
                                                         chain=chain).format()
                    if not isinstance(result, dict) and not isinstance(
                            result, MergedOptions) and (not hasattr(
                                result, 'is_dict') or not result.is_dict):
                        raise AwsSyncrError(
                            "certificate should be pointing at a dictionary",
                            got=result,
                            chain=['.'.join(location)] + chain)

                    location = chain[-1]
                    source = configuration.source_for(location)
                    for info in configuration.storage.get_info(
                            location, ignore_converters=True):
                        location = [str(part) for part in info.path.path]

    return location, source
Beispiel #9
0
def test_all_gateway_endpoints(collector):
    """Do a test on all the available gateway endpoints"""
    collector.configuration['amazon']._validated = True
    configuration = collector.configuration
    aws_syncr = configuration['aws_syncr']
    aws_syncr, amazon, stage, gateway = find_gateway(aws_syncr, configuration)

    failure = False
    for method, resource in gateway.available_methods_and_endpoints():
        combination = "{0} {1}".format(method, resource)
        print(combination)
        print("=" * len(combination))
        aws_syncr.extra = combination
        if not gateway.test(aws_syncr, amazon, stage):
            failure = True
        print("")

    if failure:
        raise AwsSyncrError("Atleast one of the endpoints failed the test")
Beispiel #10
0
    def validate_account(self):
        """Make sure we are able to connect to the right account"""
        self._validating = True
        with self.catch_invalid_credentials():
            log.info("Finding a role to check the account id")
            a_role = list(self.iam.resource.roles.limit(1))
            if not a_role:
                raise AwsSyncrError(
                    "Couldn't find an iam role, can't validate the account...."
                )
            account_id = a_role[0].meta.data['Arn'].split(":", 5)[4]

        chosen_account = self.accounts[self.environment]
        if chosen_account != account_id:
            raise BadCredentials(
                "Don't have credentials for the correct account!",
                wanted=chosen_account,
                got=account_id)

        self._validating = False
        self._validated = True
Beispiel #11
0
 def cname_for(self, gateway_location, record):
     with self.ignore_missing():
         return self.client(gateway_location).get_domain_name(domainName=record)['distributionDomainName']
     raise AwsSyncrError("Please do a sync first!")
Beispiel #12
0
def encrypt_certificate(collector):
    configuration = collector.configuration
    amazon = configuration['amazon']
    aws_syncr = configuration['aws_syncr']
    certificate = aws_syncr.artifact

    available = []

    for gateway_name, gateway in configuration.get(
            'apigateway', {}, ignore_converters=True).items():
        for name, options in gateway.get("domain_names", {}).items():
            if "zone" in options:
                location = '.'.join(
                    ['apigateway', gateway_name, 'domain_names'])
                formatter = MergedOptionStringFormatter(configuration,
                                                        location,
                                                        value=options['zone'])
                available.append(
                    (gateway_name, "{0}.{1}".format(name, formatter.format())))

    if not available:
        raise AwsSyncrError(
            "Please specify apigateway.<gateway_name>.domain_names.<domain_name>.name in the configuration"
        )

    if not certificate:
        raise AwsSyncrError(
            "Please specify certificate to encrypt with --artifact",
            available=[a[1] for a in available])

    if certificate not in [a[1] for a in available]:
        raise AwsSyncrError("Unknown certificate",
                            available=[a[1] for a in available],
                            got=certificate)

    gateway = [name for name, cert in available if cert == certificate][0]
    location, source = find_certificate_source(configuration, gateway,
                                               certificate)

    log.info("Gonna edit {0} in {1}".format(location, source))
    current = MergedOptions.using(yaml.load(open(source)))
    dest = current[location]

    try:
        key_id = input("Which kms key do you want to use? ")
        region = input("What region is this key in? ")
    except EOFError:
        raise UserQuit()

    # Make the filename completion work
    setup_completer()

    # Create the datakey to encrypt with
    data_key = amazon.kms.generate_data_key(region, key_id)
    plaintext_data_key = data_key["Plaintext"]
    encrypted_data_key = base64.b64encode(
        data_key["CiphertextBlob"]).decode('utf-8')

    # Encrypt our secrets
    secrets = {}
    for name, desc in (("body", "certificate's crt file"),
                       ("key", "private key file"), ("chain",
                                                     "certificate chain")):
        location = None
        while not location or not os.path.isfile(location):
            location = os.path.expanduser(
                filename_prompt("Where is the {0}? ".format(desc)))
            if not location or not os.path.isfile(location):
                print("Please give a location to a file that exists!")

        data = open(location).read()
        counter = Counter.new(128)
        encryptor = AES.new(plaintext_data_key[:32],
                            AES.MODE_CTR,
                            counter=counter)
        secrets[name] = base64.b64encode(
            encryptor.encrypt(data)).decode('utf-8')

    # Add in the encrypted values
    dest['body'] = {
        "kms": secrets['body'],
        "location": region,
        "kms_data_key": encrypted_data_key
    }
    dest['key'] = {
        "kms": secrets['key'],
        "location": region,
        "kms_data_key": encrypted_data_key
    }
    dest['chain'] = {
        "kms": secrets['chain'],
        "location": region,
        "kms_data_key": encrypted_data_key
    }

    # And write to the file!
    yaml.dump(current.as_dict(),
              open(source, 'w'),
              explicit_start=True,
              indent=2,
              default_flow_style=False)