Example #1
0
def get_metadata_or_fail(metadata_key):
  """
  Call get_metadata; halt with fail() if it raises an exception
  """
  try:
    return http_get_metadata(metadata_key)
  except IOError as error:
    fail("Exception in http_get_metadata {} {}".format(metadata_key, repr(error)))
Example #2
0
    def test_http_get_metadata_non_200_status_code(self, mock_urllib2):
        """
    Test http_get_metadata to retrieve ami-id and get a non 200 status code.

    Args:
      mock_urllib2:  MagicMock, returns back a non 200 status code.

    Returns:
      None

    Raises:
      AssertionError if any of the assert checks fail
    """
        mock_response = Mock(name="Always non-200 Status Code")
        mock_response.getcode.return_value = 400
        mock_urllib2.return_value = mock_response
        with self.assertRaises(IOError) as exception:
            ef_utils.http_get_metadata("ami-id")
        self.assertIn("Non-200 response", str(exception.exception))
Example #3
0
def main():
    global RESOURCES, WHERE

    openlog(LOG_IDENT)

    WHERE = whereami()
    if WHERE == "unknown":
        critical(
            "Cannot determine whether operating context is ec2 or local. Exiting."
        )
    elif WHERE == "local":
        critical(
            "local mode not supported: must run under virtualbox-kvm or in ec2"
        )
    elif WHERE == "ec2":
        # get info needed for logging
        try:
            instance_id = http_get_metadata('instance-id')
            log_info("Instance id: " + instance_id)
        except IOError as error:
            critical("Error retrieving instance-id metadata: " + repr(error))

    log_info("startup")
    log_info("boto3: " + str(boto3.utils.sys.version_info))
    log_info("boto3: " + str(boto3.utils.sys.version))
    # Tailor to operating mode
    if WHERE == "virtualbox-kvm":
        service = gethostname().split(".", 1)[0]
    elif WHERE == "ec2":
        log_info("EC2: setting up S3 client")
        try:
            session = boto3.Session()
            # S3 iteration is easier using the S3 Resource
            RESOURCES["s3"] = session.resource("s3")
        except (botocore.exceptions.BotoCoreError, IOError) as e:
            critical("Error setting up S3 resource " + repr(e))
        service = http_get_instance_role()

    log_info("platform: {} service: {}".format(WHERE, service))

    merge_files("all")
    merge_files("ssh", skip_on_user_group_error=True)
    merge_files(service)

    log_info("exit: success")
    closelog()
Example #4
0
    def test_http_get_metadata_200_status_code(self, mock_urllib2):
        """
    Test http_get_metadata to retrieve an ami-id with 200 success status.

    Args:
      mock_urllib2: MagicMock, returns back 200 and the ami-id value

    Returns:
      None

    Raises:
      AssertionError if any of the assert checks fail
    """
        mock_response = Mock(name="Always 200 Status Code")
        mock_response.getcode.return_value = 200
        mock_response.read.return_value = "ami-12345678"
        mock_urllib2.return_value = mock_response
        response = ef_utils.http_get_metadata("ami-id")
        self.assertEquals(response, "ami-12345678")
Example #5
0
class TestEFVersionResolver(unittest.TestCase):
  """Tests for 'ef_version_resolver.py'"""

  # initialize based on where running
  where = whereami()
  if where == "local":
    session = boto3.Session(profile_name=get_account_alias("proto0"), region_name=EFConfig.DEFAULT_REGION)
  elif where == "ec2":
    region = http_get_metadata("placement/availability-zone/")
    region = region[:-1]
    session = boto3.Session(region_name=region)
  else:
    fail("Can't test in environment: " + where)

  clients = {
    "ec2": session.client("ec2")
  }

  def test_ami_id(self):
    """Does ami-id,data-api resolve to an AMI id"""
    test_string = "ami-id,data-api"
    resolver = EFVersionResolver(TestEFVersionResolver.clients)
    self.assertRegexpMatches(resolver.lookup(test_string), "^ami-[a-f0-9]{8}$")
Example #6
0
  def __init__(self,
               profile=None, region=None,  # set both for user access mode
               lambda_context=None,  # set if target is 'self' and this is a lambda
               target_other=False, env=None, service=None,  # set env & service if target_other=True
               verbose=False
               ):
    """
    Depending on how this is called, access mode (how it logs into AWS) and target (what the
      various context vars report) will vary

    ACCESS MODE - how this logs in to AWS
      user: "******"
        both 'profile' and 'region' are required
        is always "operating on something else" (TARGET is never "self")
      role: "running in AWS EC2 or Lambda with a role credential"
        do not set profile

    TARGET - what context is reported
      self: "this ec2 instance or lambda is initializing itself"
        assumed for ec2 and lambda, unless target_other=True in the constructor
        never an option for "user" access mode
      other: "this local user, ec2 instance, or lambda is configuring something else"
        always "other" if access mode is "user"
        if access mode is "role", set target_other=True in the constructor
        Constructor must also set 'env' and 'service'

      self:
        lambda_context must be provided if this is a lambda; leave it unset for ec2
        INSTANCE_ID = instance ID if EC2, else None
        FUNCTION_NAME = function name if lambda, else None
        ACCOUNT = numeric account this is running in
        ACCOUNT_ALIAS = named alias of the account this is running in
        ROLE = role this is running as
        ENV = the environment this is running in, from role name
        ENV_SHORT = derived from ENV
        ENV_FULL = fully qualified environment, same as ENV unless env is a global env (mgmt.* or global.*)
        SERVICE = the service this is, from role name
        REGION = region this is running in
      something else:
        INSTANCE_ID = None
        FUNCTION_NAME = None
        ACCOUNT = the numeric account I'm logged into (look up)
        ACCOUNT_ALIAS = the alias of the account i'm logged into (look up)
        ROLE = None
        ENV = the target's environment, passed in from the constructor
        ENV_SHORT = derived from ENV
        ENV_FULL = ENV, with ".<ACCOUNT_ALIAS>" as appropriate
        SERVICE = the service name, passed in from the constructor
        REGION = the region I am in (ec2, lambda) or explicitly set (region= in constructor)

    Collects instance's environment for use in templates:
      {{ACCOUNT}}       - AWS account number
                          CloudFormation can use this or the AWS::AccountID pseudo param
      {{ACCOUNT_ALIAS}} - AWS account alias
      {{ENV}}           - environment: mgmt, prod, staging, proto<N>, etc.
      {{ENV_SHORT}}     - env with <N> or account trimmed: mgmt, prod, staging, proto, etc.
      {{ENV_FULL}}      - env fully qualified: prod, staging, proto<N>, mgmt.<account_alias>, etc.
      {{FUNCTION_NAME}} - only for lambdas
      {{INSTANCE_ID}}   - only for ec2
      {{REGION}}        - the region currently being worked in
                          CloudFormation can use this or the AWS::Region pseudo param
      {{ROLE}}          - the role bound to the ec2 instance or lambda; only for ec2 and lambda
                          CloudFormation: compose role name in template by joining other strings
    """
    # instance vars
    self.verbose = False  # print noisy status if True
    # resolved tokens - only look up symbols once per session. Protect internal names by declaring
    self.resolved = {
        "ACCOUNT": None,
        "ACCOUNT_ALIAS": None,
        "ENV": None,
        "ENV_SHORT": None,
        "ENV_FULL": None,
        "FUNCTION_NAME": None,
        "INSTANCE_ID": None,
        "REGION": None,
        "ROLE": None
    }

    # template and parameters are populated by the load() method as each template is processed
    self.template = None
    # parameters that accompany this template, if any
    self.parameters = {}
    # Sets of symbols found in the current template (only)
    # read back with self.symbols() and self.unresolved_symbols()
    self.symbols = set()
    # capture verbosity pref from constructor
    self.verbose = verbose

    # determine ACCESS MODE
    if profile:  # accessing as a user
      target_other = True
      if not region:
        fail("'region' is required with 'profile' for user-mode access")

    where = whereami()

    # require env and service params init() when target is 'other'
    if (target_other or where == "virtualbox-kvm") and (env is None or service is None):
      fail("'env' and 'service' must be set when target is 'other' or running in " + where)

    if target_other or profile:
      self.resolved["REGION"] = region
    # lambda initializing self
    elif lambda_context:
      self.resolved["REGION"] = lambda_context.invoked_function_arn.split(":")[3]
    # ec2 initializing self
    else:
      self.resolved["REGION"] = get_metadata_or_fail("placement/availability-zone/")[:-1]

    # Create clients - if accessing by role, profile should be None
    clients = [
      "cloudformation",
      "cloudfront",
      "cognito-identity",
      "cognito-idp",
      "ec2",
      "elbv2",
      "iam",
      "kms",
      "lambda",
      "route53",
      "s3",
      "sts",
      "waf"
    ]
    try:
      EFTemplateResolver.__CLIENTS = create_aws_clients(self.resolved["REGION"], profile, *clients)
    except RuntimeError as error:
      fail("Exception logging in with Session()", error)

    # Create EFAwsResolver object for interactive lookups
    EFTemplateResolver.__AWSR = EFAwsResolver(EFTemplateResolver.__CLIENTS)
    # Create EFConfigResolver object for ef tooling config lookups
    EFTemplateResolver.__EFCR = EFConfigResolver()
    # Create EFVersionResolver object for version lookups
    EFTemplateResolver.__VR = EFVersionResolver(EFTemplateResolver.__CLIENTS)

    # Set the internal parameter values for aws
    # self-configuring lambda
    if (not target_other) and lambda_context:
      arn_split = lambda_context.invoked_function_arn.split(":")
      self.resolved["ACCOUNT"] = arn_split[4]
      self.resolved["FUNCTION_NAME"] = arn_split[6]
      try:
        lambda_desc = EFTemplateResolver.__CLIENTS["lambda"].get_function()
      except:
        fail("Exception in get_function: ", sys.exc_info())
      self.resolved["ROLE"] = lambda_desc["Configuration"]["Role"]
      env = re.search("^({})-".format(EFConfig.VALID_ENV_REGEX), self.resolved["ROLE"])
      if not env:
        fail("Did not find environment in lambda function name.")
      self.resolved["ENV"] = env.group(1)
      parsed_service = re.search(self.resolved["ENV"] + "-(.*?)-lambda", self.resolved["ROLE"])
      if parsed_service:
        self.resolved["SERVICE"] = parsed_service.group(1)

    # self-configuring EC2
    elif (not target_other) and (not lambda_context):
      self.resolved["INSTANCE_ID"] = get_metadata_or_fail('instance-id')
      try:
        instance_desc = EFTemplateResolver.__CLIENTS["ec2"].describe_instances(InstanceIds=[self.resolved["INSTANCE_ID"]])
      except:
        fail("Exception in describe_instances: ", sys.exc_info())
      self.resolved["ACCOUNT"] = instance_desc["Reservations"][0]["OwnerId"]
      arn = instance_desc["Reservations"][0]["Instances"][0]["IamInstanceProfile"]["Arn"]
      self.resolved["ROLE"] = arn.split(":")[5].split("/")[1]
      env = re.search("^({})-".format(EFConfig.VALID_ENV_REGEX), self.resolved["ROLE"])
      if not env:
        fail("Did not find environment in role name")
      self.resolved["ENV"] = env.group(1)
      self.resolved["SERVICE"] = "-".join(self.resolved["ROLE"].split("-")[1:])

    # target is "other"
    else:
      try:
        if whereami() == "ec2":
          self.resolved["ACCOUNT"] = str(json.loads(http_get_metadata('iam/info'))["InstanceProfileArn"].split(":")[4])
        else:
          self.resolved["ACCOUNT"] = get_account_id(EFTemplateResolver.__CLIENTS["sts"])
      except botocore.exceptions.ClientError as error:
        fail("Exception in get_user()", error)
      self.resolved["ENV"] = env
      self.resolved["SERVICE"] = service

    # ACCOUNT_ALIAS is resolved consistently for access modes and targets other than virtualbox
    try:
      self.resolved["ACCOUNT_ALIAS"] = EFTemplateResolver.__CLIENTS["iam"].list_account_aliases()["AccountAliases"][0]
    except botocore.exceptions.ClientError as error:
      fail("Exception in list_account_aliases", error)

    # ENV_SHORT is resolved the same way for all access modes and targets
    self.resolved["ENV_SHORT"] = self.resolved["ENV"].strip(".0123456789")

    # ENV_FULL is resolved the same way for all access modes and targets, depending on previously-resolved values
    if self.resolved["ENV"] in EFConfig.ACCOUNT_SCOPED_ENVS:
      self.resolved["ENV_FULL"] = "{}.{}".format(self.resolved["ENV"], self.resolved["ACCOUNT_ALIAS"])
    else:
      self.resolved["ENV_FULL"] = self.resolved["ENV"]

    if self.verbose:
      print(repr(self.resolved), file=sys.stderr)
Example #7
0
class TestEFAwsResolver(unittest.TestCase):
    """Tests for `ef_aws_resolver.py`."""

    # initialize based on where running
    where = whereami()
    if where == "local":
        session = boto3.Session(profile_name=context.account_alias,
                                region_name=EFConfig.DEFAULT_REGION)
    elif where == "ec2":
        region = http_get_metadata("placement/availability-zone/")
        region = region[:-1]
        session = boto3.Session(region_name=region)
    else:
        fail("Can't test in environment: " + where)

    clients = {
        "cloudformation": session.client("cloudformation"),
        "cloudfront": session.client("cloudfront"),
        "ec2": session.client("ec2"),
        "iam": session.client("iam"),
        "route53": session.client("route53"),
        "waf": session.client("waf")
    }

    ## Test coverage of ec2:eni/eni-id is disabled because the we are not presently creating
    ## ENI fixtures and this test does not at present generate an ENI for testing this lookup function
    ## Why are these retained here? The lookup function is still valid, and useful. We just can't test it at the moment
    #  def test_ec2_eni_eni_id(self):
    #    """Does ec2:eni/eni-id,eni-proto3-dnsproxy-1a resolve to an ENI ID"""
    #    test_string = "ec2:eni/eni-id,eni-proto3-dnsproxy-1a"
    #    resolver = EFAwsResolver(TestEFAwsResolver.clients)
    #    self.assertRegexpMatches(resolver.lookup(test_string), "^eni-[a-f0-9]{8}$")

    #  def test_ec2_eni_eni_id_none(self):
    #    """Does ec2:eni/eni-id,cant_possibly_match return None"""
    #    test_string = "ec2:eni/eni-id,cant_possibly_match"
    #    resolver = EFAwsResolver(TestEFAwsResolver.clients)
    #    self.assertIsNone(resolver.lookup(test_string))

    #  def test_ec2_eni_eni_id_default(self):
    #    """Does ec2:eni/eni-id,cant_possibly_match,DEFAULT return default value"""
    #    test_string = "ec2:eni/eni-id,cant_possibly_match,DEFAULT"
    #    resolver = EFAwsResolver(TestEFAwsResolver.clients)
    #    self.assertRegexpMatches(resolver.lookup(test_string), "^DEFAULT$")

    def test_ec2_elasticip_elasticip_id(self):
        """Does ec2:elasticip/elasticip-id,ElasticIpMgmtCingest1 resolve to elastic IP allocation ID"""
        test_string = "ec2:elasticip/elasticip-id,ElasticIpMgmtCingest1"
        resolver = EFAwsResolver(TestEFAwsResolver.clients)
        self.assertRegexpMatches(resolver.lookup(test_string),
                                 "^eipalloc-[a-f0-9]{8}$")

    def test_ec2_elasticip_elasticip_id_none(self):
        """Does ec2:elasticip/elasticip-id,cant_possibly_match return None"""
        test_string = "ec2:elasticip/elasticip-id,cant_possibly_match"
        resolver = EFAwsResolver(TestEFAwsResolver.clients)
        self.assertIsNone(resolver.lookup(test_string))

    def test_ec2_elasticip_elasticip_id_default(self):
        """Does ec2:elasticip/elasticip-id,cant_possibly_match,DEFAULT return default value"""
        test_string = "ec2:elasticip/elasticip-id,cant_possibly_match,DEFAULT"
        resolver = EFAwsResolver(TestEFAwsResolver.clients)
        self.assertRegexpMatches(resolver.lookup(test_string), "^DEFAULT$")

    def test_ec2_elasticip_elasticip_ipaddress(self):
        """Does ec2:elasticip/elasticip-ipaddress,ElasticIpMgmtCingest1 resolve to elastic IP address"""
        test_string = "ec2:elasticip/elasticip-ipaddress,ElasticIpMgmtCingest1"
        resolver = EFAwsResolver(TestEFAwsResolver.clients)
        self.assertRegexpMatches(
            resolver.lookup(test_string),
            "^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$")

    def test_ec2_elasticip_elasticip_ipaddress_none(self):
        """Does ec2:elasticip/elasticip-ipaddress,cant_possibly_match return None"""
        test_string = "ec2:elasticip/elasticip-ipaddress,cant_possibly_match"
        resolver = EFAwsResolver(TestEFAwsResolver.clients)
        self.assertIsNone(resolver.lookup(test_string))

    def test_ec2_elasticip_elasticip_ipaddress_default(self):
        """Does ec2:elasticip/elasticip-ipaddress,cant_possibly_match,DEFAULT return default value"""
        test_string = "ec2:elasticip/elasticip-ipaddress,cant_possibly_match,DEFAULT"
        resolver = EFAwsResolver(TestEFAwsResolver.clients)
        self.assertRegexpMatches(resolver.lookup(test_string), "^DEFAULT$")

    def test_ec2_route_table_main_route_table_id(self):
        """Does ec2:route-table/main-route-table-id,vpc-<env> resolve to route table ID"""
        test_string = "ec2:route-table/main-route-table-id,vpc-" + context.env
        resolver = EFAwsResolver(TestEFAwsResolver.clients)
        self.assertRegexpMatches(resolver.lookup(test_string),
                                 "^rtb-[a-f0-9]{8}$")

    def test_ec2_route_table_main_route_table_id_none(self):
        """Does ec2:route-table/main-route-table-id,cant_possibly_match return None"""
        test_string = "ec2:route-table/main-route-table-id,cant_possibly_match"
        resolver = EFAwsResolver(TestEFAwsResolver.clients)
        self.assertIsNone(resolver.lookup(test_string))

    def test_ec2_route_table_main_route_table_id_default(self):
        """Does ec2:route-table/main-route-table-id,cant_possibly_match,DEFAULT return default value"""
        test_string = "ec2:route-table/main-route-table-id,cant_possibly_match,DEFAULT"
        resolver = EFAwsResolver(TestEFAwsResolver.clients)
        self.assertRegexpMatches(resolver.lookup(test_string), "^DEFAULT$")

    def test_ec2_security_group_security_group_id(self):
        """Does ec2:security-group/security-group-id,staging-core-ec2 resolve to a security group id"""
        test_string = "ec2:security-group/security-group-id,staging-core-ec2"
        resolver = EFAwsResolver(TestEFAwsResolver.clients)
        self.assertRegexpMatches(resolver.lookup(test_string),
                                 "^sg-[a-f0-9]{8}$")

    def test_ec2_security_group_security_group_id_none(self):
        """Does ec2:security-group/security-group-id,cant_possibly_match return None"""
        test_string = "ec2:security-group/security-group-id,cant_possibly_match"
        resolver = EFAwsResolver(TestEFAwsResolver.clients)
        self.assertIsNone(resolver.lookup(test_string))

    def test_ec2_security_group_security_group_id_default(self):
        """Does ec2:security-group/security-group-id,cant_possibly_match,DEFAULT return default value"""
        test_string = "ec2:security-group/security-group-id,cant_possibly_match,DEFAULT"
        resolver = EFAwsResolver(TestEFAwsResolver.clients)
        self.assertRegexpMatches(resolver.lookup(test_string), "^DEFAULT$")

    def test_ec2_subnet_subnet_id(self):
        """Does ec2:subnet/subnet-id,subnet-staging-a resolve to a subnet ID"""
        test_string = "ec2:subnet/subnet-id,subnet-staging-a"
        resolver = EFAwsResolver(TestEFAwsResolver.clients)
        self.assertRegexpMatches(resolver.lookup(test_string),
                                 "^subnet-[a-f0-9]{8}$")

    def test_ec2_subnet_subnet_id_none(self):
        """Does ec2:subnet/subnet-id,cant_possibly_match return None"""
        test_string = "ec2:subnet/subnet-id,cant_possibly_match"
        resolver = EFAwsResolver(TestEFAwsResolver.clients)
        self.assertIsNone(resolver.lookup(test_string))

    def test_ec2_subnet_subnet_id_default(self):
        """Does ec2:subnet/subnet-id,cant_possibly_match,DEFAULT return default value"""
        test_string = "ec2:subnet/subnet-id,cant_possibly_match,DEFAULT"
        resolver = EFAwsResolver(TestEFAwsResolver.clients)
        self.assertRegexpMatches(resolver.lookup(test_string), "^DEFAULT$")

    def test_ec2_vpc_availabilityzones(self):
        """Does ec2:vpc/availabilityzones,vpc-staging resolve to correctly-delimited string of AZ(s)"""
        test_string = "ec2:vpc/availabilityzones,vpc-staging"
        resolver = EFAwsResolver(TestEFAwsResolver.clients)
        self.assertRegexpMatches(
            resolver.lookup(test_string),
            "^us-west-2(a|b)(\", \"us-west-2(a|b)){0,1}$")

    def test_ec2_vpc_availabilityzones_none(self):
        """Does ec2:vpc/availabilityzones,cant_possibly_match return None"""
        test_string = "ec2:vpc/availabilityzones,cant_possibly_match"
        resolver = EFAwsResolver(TestEFAwsResolver.clients)
        self.assertIsNone(resolver.lookup(test_string))

    def test_ec2_vpc_availabilityzones_default(self):
        """Does ec2:vpc/availabilityzones,cant_possibly_match,DEFAULT return default value"""
        test_string = "ec2:vpc/availabilityzones,cant_possibly_match,DEFAULT"
        resolver = EFAwsResolver(TestEFAwsResolver.clients)
        self.assertRegexpMatches(resolver.lookup(test_string), "^DEFAULT$")

    def test_ec2_vpc_cidrblock(self):
        """Does ec2:vpc/cidrblock,vpc-staging resolve to a CIDR block"""
        test_string = "ec2:vpc/cidrblock,vpc-staging"
        resolver = EFAwsResolver(TestEFAwsResolver.clients)
        self.assertRegexpMatches(
            resolver.lookup(test_string),
            "^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\/\d{2}$")

    def test_ec2_vpc_cidrblock_none(self):
        """Does ec2:vpc/cidrblock,cant_possibly_match return None"""
        test_string = "ec2:vpc/cidrblock,cant_possibly_match"
        resolver = EFAwsResolver(TestEFAwsResolver.clients)
        self.assertIsNone(resolver.lookup(test_string))

    def test_ec2_vpc_cidrblock_default(self):
        """Does ec2:vpc/cidrblock,cant_possibly_match,DEFAULT return default value"""
        test_string = "ec2:vpc/cidrblock,cant_possibly_match,DEFAULT"
        resolver = EFAwsResolver(TestEFAwsResolver.clients)
        self.assertRegexpMatches(resolver.lookup(test_string), "^DEFAULT$")

    def test_ec2_vpc_subnets(self):
        """Does ec2:vpc/subnets,vpc-staging resolve to correctly-delimited string of AZ(s)"""
        test_string = "ec2:vpc/subnets,vpc-staging"
        resolver = EFAwsResolver(TestEFAwsResolver.clients)
        self.assertRegexpMatches(
            resolver.lookup(test_string),
            "^subnet-[a-f0-9]{8}(\", \"subnet-[a-f0-9]{8}){0,1}$")

    def test_ec2_vpc_subnets_none(self):
        """Does ec2:vpc/subnets,cant_possibly_match return None"""
        test_string = "ec2:vpc/subnets,cant_possibly_match"
        resolver = EFAwsResolver(TestEFAwsResolver.clients)
        self.assertIsNone(resolver.lookup(test_string))

    def test_ec2_vpc_subnets_default(self):
        """Does ec2:vpc/subnets,cant_possibly_match,DEFAULT return default value"""
        test_string = "ec2:vpc/subnets,cant_possibly_match,DEFAULT"
        resolver = EFAwsResolver(TestEFAwsResolver.clients)
        self.assertRegexpMatches(resolver.lookup(test_string), "^DEFAULT$")

    def test_ec2_vpc_vpc_id(self):
        """Does ec2:vpc/vpc-id,vpc-staging resolve to VPC ID"""
        test_string = "ec2:vpc/vpc-id,vpc-staging"
        resolver = EFAwsResolver(TestEFAwsResolver.clients)
        self.assertRegexpMatches(resolver.lookup(test_string),
                                 "^vpc-[a-f0-9]{8}$")

    def test_ec2_vpc_vpc_id_none(self):
        """Does ec2:vpc/vpc-id,cant_possibly_match return None"""
        test_string = "ec2:vpc/vpc-id,cant_possibly_match"
        resolver = EFAwsResolver(TestEFAwsResolver.clients)
        self.assertIsNone(resolver.lookup(test_string))

    def test_ec2_vpc_vpc_id_default(self):
        """Does ec2:vpc/vpc-id,cant_possibly_match,DEFAULT return default value"""
        test_string = "ec2:vpc/vpc-id,cant_possibly_match,DEFAULT"
        resolver = EFAwsResolver(TestEFAwsResolver.clients)
        self.assertRegexpMatches(resolver.lookup(test_string), "^DEFAULT$")

    def test_waf_rule_id(self):
        """Does waf:rule-id,global-OfficeCidr resolve to WAF ID"""
        test_string = "waf:rule-id,global-OfficeCidr"
        resolver = EFAwsResolver(TestEFAwsResolver.clients)
        self.assertRegexpMatches(
            resolver.lookup(test_string),
            "^[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$")

    def test_waf_rule_id_none(self):
        """Does waf:rule-id,cant_possibly_match return None"""
        test_string = "waf:rule-id,cant_possibly_match"
        resolver = EFAwsResolver(TestEFAwsResolver.clients)
        self.assertIsNone(resolver.lookup(test_string))

    def test_waf_rule_id_default(self):
        """Does waf:rule-id,cant_possibly_match,DEFAULT return default value"""
        test_string = "waf:rule-id,cant_possibly_match,DEFAULT"
        resolver = EFAwsResolver(TestEFAwsResolver.clients)
        self.assertRegexpMatches(resolver.lookup(test_string), "^DEFAULT$")

    def test_waf_web_acl_id(self):
        """Does waf:web-acl-id,staging-StaticAcl resolve to Web ACL ID"""
        test_string = "waf:web-acl-id,staging-StaticAcl"
        resolver = EFAwsResolver(TestEFAwsResolver.clients)
        self.assertRegexpMatches(
            resolver.lookup(test_string),
            "^[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$")

    def test_waf_web_acl_id_none(self):
        """Does waf:web-acl-id,cant_possibly_match return None"""
        test_string = "waf:web-acl-id,cant_possibly_match"
        resolver = EFAwsResolver(TestEFAwsResolver.clients)
        self.assertIsNone(resolver.lookup(test_string))

    def test_waf_web_acl_id_default(self):
        """Does waf:web-acl-id,cant_possibly_match,DEFAULT return default value"""
        test_string = "waf:web-acl-id,cant_possibly_match,DEFAULT"
        resolver = EFAwsResolver(TestEFAwsResolver.clients)
        self.assertRegexpMatches(resolver.lookup(test_string), "^DEFAULT$")

    def test_route53_private_hosted_zone_id(self):
        """Does route53:private-hosted-zone-id,cx-proto0.com. resolve to zone ID"""
        test_string = "route53:private-hosted-zone-id,cx-proto0.com."
        resolver = EFAwsResolver(TestEFAwsResolver.clients)
        self.assertRegexpMatches(resolver.lookup(test_string),
                                 "^[A-Z0-9]{13,14}$")

    def test_route53_private_hosted_zone_id_none(self):
        """Does route53:private-hosted-zone-id,cant_possibly_match return None"""
        test_string = "route53:private-hosted-zone-id,cant_possibly_match"
        resolver = EFAwsResolver(TestEFAwsResolver.clients)
        self.assertIsNone(resolver.lookup(test_string))

    def test_route53_private_hosted_zone_id_default(self):
        """Does route53:private-hosted-zone-id,cant_possibly_match,DEFAULT return default value"""
        test_string = "route53:private-hosted-zone-id,cant_possibly_match,DEFAULT"
        resolver = EFAwsResolver(TestEFAwsResolver.clients)
        self.assertRegexpMatches(resolver.lookup(test_string), "^DEFAULT$")

    def test_route53_public_hosted_zone_id(self):
        """Does route53:hosted-zone-id,cx-proto0.com. resolve to zone ID"""
        test_string = "route53:public-hosted-zone-id,cx-proto0.com."
        resolver = EFAwsResolver(TestEFAwsResolver.clients)
        self.assertRegexpMatches(resolver.lookup(test_string),
                                 "^[A-Z0-9]{13,14}$")

    def test_route53_public_hosted_zone_id_none(self):
        """Does route53:public-hosted-zone-id,cant_possibly_match return None"""
        test_string = "route53:public-hosted-zone-id,cant_possibly_match"
        resolver = EFAwsResolver(TestEFAwsResolver.clients)
        self.assertIsNone(resolver.lookup(test_string))

    def test_route53_public_hosted_zone_id_default(self):
        """Does route53:public-hosted-zone-id,cant_possibly_match,DEFAULT return default value"""
        test_string = "route53:public-hosted-zone-id,cant_possibly_match,DEFAULT"
        resolver = EFAwsResolver(TestEFAwsResolver.clients)
        self.assertRegexpMatches(resolver.lookup(test_string), "^DEFAULT$")

    def test_cloudfront_domain_name(self):
        """Does cloudfront:domain-name,static.cx-proto0.com resolve to a Cloudfront FQDN"""
        test_string = "cloudfront:domain-name,static.cx-proto0.com"
        resolver = EFAwsResolver(TestEFAwsResolver.clients)
        self.assertRegexpMatches(resolver.lookup(test_string),
                                 "^[a-z0-9]{13,14}.cloudfront.net$")

    def test_cloudfront_domain_name_none(self):
        """Does cloudfront:domain-name,cant_possibly_match return None"""
        test_string = "cloudfront:domain-name,cant_possibly_match"
        resolver = EFAwsResolver(TestEFAwsResolver.clients)
        self.assertIsNone(resolver.lookup(test_string))

    def test_cloudfront_domain_name_default(self):
        """Does cloudfront:domain-name,cant_possibly_match,DEFAULT return default value"""
        test_string = "cloudfront:domain-name,cant_possibly_match,DEFAULT"
        resolver = EFAwsResolver(TestEFAwsResolver.clients)
        self.assertRegexpMatches(resolver.lookup(test_string), "^DEFAULT$")

    def test_cloudfront_origin_access_identity_oai_id(self):
        """Does cloudfront:origin-access-identity/oai-id,static.cx-proto0.com resolve to oai ID"""
        test_string = "cloudfront:origin-access-identity/oai-id,static.cx-proto0.com"
        resolver = EFAwsResolver(TestEFAwsResolver.clients)
        self.assertRegexpMatches(resolver.lookup(test_string),
                                 "^[A-Z0-9]{13,14}$")

    def test_cloudfront_origin_access_identity_oai_id_none(self):
        """Does cloudfront:origin-access-identity/oai-id,cant_possibly_match return None"""
        test_string = "cloudfront:origin-access-identity/oai-id,cant_possibly_match"
        resolver = EFAwsResolver(TestEFAwsResolver.clients)
        self.assertIsNone(resolver.lookup(test_string))

    def test_cloudfront_origin_access_identity_oai_id_default(self):
        """Does cloudfront:origin-access-identity/oai-id,cant_possibly_match,DEFAULT return default value"""
        test_string = "cloudfront:origin-access-identity/oai-id,cant_possibly_match,DEFAULT"
        resolver = EFAwsResolver(TestEFAwsResolver.clients)
        self.assertRegexpMatches(resolver.lookup(test_string), "^DEFAULT$")

    def test_cloudfront_origin_access_identity_oai_canonical_user_id(self):
        """Does cloudfront:origin-access-identity/oai-canonical-user-id,static.cx-proto0.com resolve to oai ID"""
        test_string = "cloudfront:origin-access-identity/oai-canonical-user-id,static.cx-proto0.com"
        resolver = EFAwsResolver(TestEFAwsResolver.clients)
        self.assertRegexpMatches(resolver.lookup(test_string),
                                 "^[a-z0-9]{96}$")

    def test_cloudfront_origin_access_identity_oai_canonical_user_id_none(
            self):
        """Does cloudfront:origin-access-identity/oai-canonical-user-id,cant_possibly_match return None"""
        test_string = "cloudfront:origin-access-identity/oai-canonical-user-id,cant_possibly_match"
        resolver = EFAwsResolver(TestEFAwsResolver.clients)
        self.assertIsNone(resolver.lookup(test_string))

    def test_cloudfront_origin_access_identity_oai_canonical_user_id_default(
            self):
        """Does cloudfront:origin-access-identity/oai-canonical-user-id,cant_possibly_match,DEFAULT return default value"""
        test_string = "cloudfront:origin-access-identity/oai-canonical-user-id,cant_possibly_match,DEFAULT"
        resolver = EFAwsResolver(TestEFAwsResolver.clients)
        self.assertRegexpMatches(resolver.lookup(test_string), "^DEFAULT$")
Example #8
0
def main():
  global CONTEXT, CLIENTS, AWS_RESOLVER

  CONTEXT = handle_args_and_set_context(sys.argv[1:])
  if not (CONTEXT.devel or getenv("JENKINS_URL", False)):
    try:
      pull_repo()
    except RuntimeError as error:
      fail("Error checking or pulling repo", error)
  else:
    print("Not refreshing repo because --devel was set or running on Jenkins")

  # sign on to AWS and create clients and get account ID
  try:
    # If running in EC2, always use instance credentials. One day we'll have "lambda" in there too, so use "in" w/ list
    if CONTEXT.whereami == "ec2":
      CLIENTS = create_aws_clients(EFConfig.DEFAULT_REGION, None, "ec2", "iam", "kms")
      CONTEXT.account_id = str(json.loads(http_get_metadata('iam/info'))["InstanceProfileArn"].split(":")[4])
    else:
      # Otherwise, we use local user creds based on the account alias
      CLIENTS = create_aws_clients(EFConfig.DEFAULT_REGION, CONTEXT.account_alias, "ec2", "iam", "kms", "sts")
      CONTEXT.account_id = get_account_id(CLIENTS["sts"])
  except RuntimeError:
    fail("Exception creating AWS clients in region {} with profile {}".format(
      EFConfig.DEFAULT_REGION, CONTEXT.account_alias))
  # Instantiate an AWSResolver to lookup AWS resources
  AWS_RESOLVER = EFAwsResolver(CLIENTS)

  # Show where we're working
  if not CONTEXT.commit:
    print("=== DRY RUN ===\nUse --commit to create roles and security groups\n=== DRY RUN ===")
  print("env: {}".format(CONTEXT.env))
  print("env_full: {}".format(CONTEXT.env_full))
  print("env_short: {}".format(CONTEXT.env_short))
  print("aws account profile: {}".format(CONTEXT.account_alias))
  print("aws account number: {}".format(CONTEXT.account_id))

  # Step through all services in the service registry
  for CONTEXT.service in CONTEXT.service_registry.iter_services():
    service_name = CONTEXT.service[0]
    target_name = "{}-{}".format(CONTEXT.env, service_name)
    sr_entry = CONTEXT.service[1]
    service_type = sr_entry['type']
    print_if_verbose("service: {} in env: {}".format(service_name, CONTEXT.env))

    # Is this service_type handled by this tool?
    if service_type not in SUPPORTED_SERVICE_TYPES:
      print_if_verbose("unsupported service type: {}".format(service_type))
      continue
    # Is the env valid for this service?
    if CONTEXT.env_full not in CONTEXT.service_registry.valid_envs(service_name):
      print_if_verbose("env: {} not valid for service {}".format(CONTEXT.env_full, service_name))
      continue
    # Is the service_type allowed in 'global'?
    if CONTEXT.env == "global" and service_type not in GLOBAL_SERVICE_TYPES:
      print_if_verbose("env: {} not valid for service type {}".format(CONTEXT.env, service_type))
      continue

    # 1. CONDITIONALLY MAKE ROLE AND/OR INSTANCE PROFILE FOR THE SERVICE
    # If service gets a role, create with either a custom or default AssumeRole policy document
    conditionally_create_role(target_name, sr_entry)
    # Instance profiles and security groups are not allowed in the global scope
    if CONTEXT.env != "global":
      conditionally_create_profile(target_name, service_type)

      # 2. SECURITY GROUP(S) FOR THE SERVICE : only some types of services get security groups
      conditionally_create_security_groups(CONTEXT.env, service_name, service_type)

    # 3. KMS KEY FOR THE SERVICE : only some types of services get kms keys
    conditionally_create_kms_key(target_name, service_type)

    # 4. ATTACH AWS MANAGED POLICIES TO ROLE
    conditionally_attach_aws_managed_policies(target_name, sr_entry)

    # 5. ATTACH CUSTOMER MANAGED POLICIES TO ROLE
    conditionally_attach_customer_managed_policies(target_name, sr_entry)

    # 6. INLINE SERVICE'S POLICIES INTO ROLE
    # only eligible service types with "policies" sections in the service registry get policies
    conditionally_inline_policies(target_name, sr_entry)

  print("Exit: success")