# -*- coding: utf-8 -*-"""**List of AWS Managed Policy**AWS provides a lot of pre-baked, commonly used AWS Managed IAM Policy to usewithout writing your delicate IAM Policy. However, looking up the exactARN of those policy is very annoying. **this helper allows you to auto completethe AWS Managed Policy ARN**.Example:.. code-block:: python import ctf ctf.helpers.iam.AwsManagedPolicy.AmazonEC2FullAccess # auto complete here**List of AWS Service principal**:IAM role requires to define the **trusted entity**. It is something like this:: { "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Principal": { "Service": "{service_name}.amazonaws.com" }, "Action": "sts:AssumeRole" } ] }However, it is painful to find the valid value for certain AWS Service.``cottonformation`` **aim to enum those value so developer can easily use itsupported by auto-complete**.Example:.. code-block:: python import ctf ctf.helpers.iam.AssumeRolePolicyBuilder( ctf.helpers.iam.ServicePrincipal.awslambda() # auto complete here ).build()There are three source you can use to get list of AWS Service principal- A community maintained github gist: https://gist.github.com/shortjared/4c1e3fe52bdfa47522cfe5b41e5d6f22- The aws cli github repository: https://github.com/aws/aws-cli/tree/develop/awscli/examples- The botocore data set: https://raw.githubusercontent.com/boto/botocore/develop/botocore/data/endpoints.jsonConfirmed by AWS, even AWS engineer doesn't know the full list. There's no suchthings Personally I prefer to use the botocore data set because it is maintained by AWS."""importattrimporttypingfrom._iam_aws_service_principalimport_ServicePrincipalMixinfrom._iam_aws_managed_policyimport_AwsManagedPolicyfrom..regeximportServiceEnum
[docs]classAwsManagedPolicy(_AwsManagedPolicy):""" Helper class to visit the valid AWS Managed IAM Policy ARN. You can view the full list of aws managed policy in the console here https://console.aws.amazon.com/iam/home?region=us-east-1#/policies or use aws cli ``aws iam list-policies --scope AWS --max-items 1000`` to find policy name and ARN value in the response """
_TAB=" "*4def_find_all_service_name_list_from_botocore()->list:# pragma: no coverimportrequestsfrombs4importBeautifulSoupservice_name_list=list()path_prefix="/boto/botocore/tree/master/botocore/data/"url="https://github.com/boto/botocore/tree/master/botocore/data"res=requests.get(url)soup=BeautifulSoup(res.text,"html.parser")div=soup.find("div",class_="js-details-container Details")foraindiv.find_all("a"):href=a.attrs["href"]ifhref.startswith(path_prefix):service_name=href.replace(path_prefix,"").split("/")[0]service_name_list.append(service_name)returnservice_name_listdef_find_all_service_name_list_from_gist()->typing.List[str]:# pragma: no cover""" :return: """importrequestsurl="https://gist.githubusercontent.com/shortjared/4c1e3fe52bdfa47522cfe5b41e5d6f22/raw/01238496b3105299c291e8123ae0b4c2be5addac/list.txt"service_name_list=[service_principal[:-14]forservice_principalinrequests.get(url).text.strip().split("\n")]returnservice_name_listdef_generate_aws_service_principal_code(service_name_list:typing.List[str]):# pragma: no coverfrompathlib_mateimportPathClsasPathlines=["# -*- coding: utf-8 -*-","","class _ServicePrincipalMixin:",]special_case_mapper={"lambda":"awslambda","codedeploy_${AWS::Region}":"codedeploy",}forservice_nameinservice_name_list:attr_name=service_name.replace("-","_").replace(".","_")attr_name=special_case_mapper.get(attr_name,attr_name)lines.append(f"{_TAB}@classmethod")lines.append(f"{_TAB}def {attr_name}(cls): return cls._build(\"{service_name}.amazonaws.com\") # pragma: no cover")lines.append("")code="\n".join(lines)Path(__file__).change(new_basename="_iam_aws_service_principal.py").write_text(code)@attr.sclass_AwsPrincipal:defto_dict(self)->dict:raiseNotImplementedError
[docs]@attr.sclassSamlPrincipal(_AwsPrincipal):""" TODO """
[docs]classAssumeRolePolicyBuilder:""" Helper class to build IAM trusted entity / assume role policy. """def__init__(self,*args:typing.Union[_AwsPrincipal,str]):self.principal_list:typing.List[typing.Union[_AwsPrincipal,str]]=argsdefbuild(self):service_principal_list:typing.List[str]=list()statements:typing.List[dict]=list()forprincipalinself.principal_list:ifisinstance(principal,ServicePrincipal):service_principal_list.append(principal.service_principal)elifisinstance(principal,AccountPrincipal):statements.append(principal.to_dict())elifisinstance(principal,str):# pragma: no coverifServiceEnum.is_aws_account_id(principal):statements.append(AccountPrincipal(account_id=principal).to_dict())elifServiceEnum.is_aws_service_domain(principal):service_principal_list.append(principal)else:# pragma: no coverraiseNotImplementedErrorelse:# pragma: no coverraiseNotImplementedErrorstatements.append({"Effect":"Allow","Principal":{"Service":service_principal_list},"Action":"sts:AssumeRole"})return{"Version":"2012-10-17","Statement":statements,}
def_find_all_aws_managed_policies()->typing.List[typing.Tuple[str,str]]:# pragma: no coverimportboto3aws_profile="sanhe"iam_client=boto3.session.Session(profile_name=aws_profile,region_name="us-east-1").client("iam")res=iam_client.list_policies(Scope="AWS",MaxItems=1000)data=list()forpolicy_dctinres["Policies"]:name=policy_dct["PolicyName"].replace("-","_")arn=policy_dct["Arn"]data.append((name,arn))returndatadef_generate_aws_managed_policy_code(aws_managed_policy_data:typing.List[typing.Tuple[str,str]]):# pragma: no coverfrompathlib_mateimportPathClsasPathlines=["# -*- coding: utf-8 -*-","","class _AwsManagedPolicy:",]forname,arninaws_managed_policy_data:line=f"{_TAB}{name} = \"{arn}\""lines.append(line)code="\n".join(lines)Path(__file__).change(new_basename="_iam_aws_managed_policy.py").write_text(code)