-
Notifications
You must be signed in to change notification settings - Fork 0
/
stack.json
163 lines (163 loc) · 7.55 KB
/
stack.json
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
{
"AWSTemplateFormatVersion": "2010-09-09",
"Description": "AWS CloudFormation Template to setup AWS Certificate Manager services and automate certificates provision.",
"Resources": {
"Acm": {
"Type" : "AWS::CertificateManager::Certificate",
"Properties": {
"DomainName": {"Ref": "Domain"},
"DomainValidationOptions": [{
"DomainName": {"Ref": "Domain"},
"ValidationDomain": {"Ref": "Domain"}
}],
"ValidationMethod": "DNS"
}
},
"LambdaValidateAcmDomainExecutionRole": {
"Type": "AWS::IAM::Role",
"Properties": {
"AssumeRolePolicyDocument": {
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {"Service": ["lambda.amazonaws.com"]},
"Action": ["sts:AssumeRole"]
}
]
},
"ManagedPolicyArns": [
"arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole",
"arn:aws:iam::aws:policy/AWSCertificateManagerReadOnly"
],
"Path": "/"
}
},
"LambdaValidateAcmDomain": {
"Type": "AWS::Lambda::Function",
"Properties": {
"Description": "Validate the ACM certificate domain by maintaining DNS records.",
"Code": { "ZipFile": { "Fn::Join": [ "\n", [
"_H='host'",
"_G='type'",
"_F='acm'",
"_E='domains'",
"_D='get'",
"_C='records'",
"_B='/'",
"_A='.'",
"import time,os,json,boto3,requests",
"import cfnresponse",
"print('Loading function')",
"def lambda_handler(event,context):",
" try:",
" domain=os.getenv('DOMAIN');ns=os.getenv('DOMAIN_NAMESERVER');ns_username=os.getenv('DOMAIN_NAMESERVER_USERNAME');ns_credential=os.getenv('DOMAIN_NAMESERVER_CREDENTIAL');requesttype=event['RequestType'];print('request type: {}, domain: {}, nameserver: {}'.format(requesttype,domain,ns))",
" if requesttype in['Create','Update']:",
" api_cls_name=API_CLS_NAME.get(ns);api_cls=globals().get(api_cls_name);api=api_cls(ns_username,ns_credential);cert_arn=wait_call(120,3,get_acm_cert_arn_by_domain,domain);acm=boto3.client(_F);cert=acm.describe_certificate(CertificateArn=cert_arn)['Certificate']",
" for item in cert['DomainValidationOptions']:",
" status=item['ValidationStatus'];record=item['ResourceRecord'];type=record['Type'];name=record['Name'].rstrip(_A);value=record['Value'].rstrip(_A);host=get_host_from_domain(name);root=get_root_from_domain(name)",
" if status=='PENDING_VALIDATION':api.create_record(domain=root,type=type,host=host,answer=value)",
" elif requesttype=='Delete':0",
" cfnresponse.send(event,context,cfnresponse.SUCCESS,{})",
" except Exception as e:print(e);cfnresponse.send(event,context,cfnresponse.FAILED,{'error':str(e)})",
"def wait_call(timeout,delay,func,*args,**kwargs):",
" starter=time.time()",
" while time.time()-starter<timeout:",
" ret=func(*args,**kwargs)",
" if ret is not None:return ret",
" else:time.sleep(delay)",
"def get_acm_cert_arn_by_domain(domain):",
" acm=boto3.client(_F)",
" for cert in acm.list_certificates()['CertificateSummaryList']:",
" if cert['DomainName']==domain:return cert['CertificateArn']",
"def get_host_from_domain(domain):items=domain.split(_A);bound=len(items)-2;return _A.join(items[:bound])",
"def get_root_from_domain(domain):items=domain.split(_A);bound=len(items)-2;return _A.join(items[bound:])",
"API_CLS_NAME={'name.com':'NameNsApi'}",
"class BaseNsApi:",
" def __init__(self,user,credential,*args,**kwargs):super(BaseNsApi,self).__init__(*args,**kwargs);self.user=user;self.credential=credential",
" def call_api(self,url,method=_D,data=None):response=getattr(requests,method)(url,auth=(self.user,self.credential),json=data);return json.loads(response.text)or response",
" def list_records(self,*args,**kwargs):0",
" def create_record(self,*args,**kwargs):0",
" def delete_records(self,*args,**kwargs):0",
"class NameNsApi(BaseNsApi):",
" api_base_url='https://api.name.com/v4'",
" def list_records(self,domain,type,host):url=_B.join([self.api_base_url,_E,domain,_C]);records=self.call_api(url,method=_D)or{};return[item for item in records.get(_C,[])if item.get(_G)==type and item.get(_H)==host]",
" def create_record(self,domain,type,host,answer,ttl=300):url=_B.join([self.api_base_url,_E,domain,_C]);data={_H:host,_G:type,'answer':answer,'ttl':ttl};return self.call_api(url,method='post',data=data)",
" def delete_records(self,domain,type,host):",
" records=self.list_records(domain,type,host)",
" for item in records:url=_B.join([self.api_base_url,_E,domain,_C,str(item.get('id'))]);self.call_api(url,method='delete')",
" return records",
" @property",
" def is_accessible(self):",
" try:url=_B.join([self.api_base_url,'hello']);return self.call_api(url,method=_D).get('username')==self.user",
" except:return False"
] ] } },
"Environment": {
"Variables": {
"DOMAIN": {"Ref": "Domain"},
"DOMAIN_NAMESERVER": {"Ref": "DomainNameServer"},
"DOMAIN_NAMESERVER_USERNAME": {"Ref": "DomainNameServerUsername"},
"DOMAIN_NAMESERVER_CREDENTIAL": {"Ref": "DomainNameServerCredential"}
}
},
"Handler": "index.lambda_handler",
"Runtime": "python3.12",
"Timeout": "120",
"Role": {"Fn::GetAtt": ["LambdaValidateAcmDomainExecutionRole", "Arn"]},
"Layers": [{"Ref": "LambdaLayerRequestsArn"}]
}
},
"ValidateAcmDomain": {
"Type": "AWS::CloudFormation::CustomResource",
"Condition": "EnableAcmAutoValidation",
"Properties": {
"ServiceToken": {"Fn::GetAtt": ["LambdaValidateAcmDomain", "Arn"]}
}
}
},
"Conditions": {
"EnableAcmAutoValidation": {
"Fn::And": [
{"Fn::Not": [{"Fn::Equals":["", {"Ref": "DomainNameServer"}]}]},
{"Fn::Not": [{"Fn::Equals":["", {"Ref": "DomainNameServerUsername"}]}]},
{"Fn::Not": [{"Fn::Equals":["", {"Ref": "DomainNameServerCredential"}]}]}
]
}
},
"Parameters": {
"Domain": {
"Type": "String",
"Default": "",
"Description": "Domain name for the SSL certificate. Example: www.example.com or *.example.com."
},
"DomainNameServer": {
"Type": "String",
"Default": "",
"Description": "Domain Nameserver for Domain. Supported Nameserver: 'name.com'."
},
"DomainNameServerUsername": {
"Type": "String",
"Default": "",
"Description": "User identity for the Domain Nameserver API service."
},
"DomainNameServerCredential": {
"Type": "String",
"Default": "",
"NoEcho": "true",
"Description": "User credential/token for the Domain Nameserver API service."
},
"LambdaLayerRequestsArn": {
"Type": "String",
"Default": "",
"Description": "Lambda layer ARN for LambdaLayerRequests."
}
},
"Outputs": {
"AcmArn": {
"Value": {"Ref": "Acm"}
},
"LambdaArn": {
"Value": {"Fn::GetAtt": ["LambdaValidateAcmDomain", "Arn"]}
}
}
}