@jaggr2/cdk-cf-dns 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/API.md +411 -0
- package/README.md +194 -0
- package/lib/certificate.d.ts +41 -0
- package/lib/certificate.js +83 -0
- package/lib/certificate.js.map +1 -0
- package/lib/handler/acm.d.ts +12 -0
- package/lib/handler/acm.js +131 -0
- package/lib/handler/acm.js.map +1 -0
- package/lib/handler/cloudflare.d.ts +133 -0
- package/lib/handler/cloudflare.js +264 -0
- package/lib/handler/cloudflare.js.map +1 -0
- package/lib/handler/index.d.ts +11 -0
- package/lib/handler/index.js +114 -0
- package/lib/handler/index.js.map +1 -0
- package/lib/index.d.ts +4 -0
- package/lib/index.js +21 -0
- package/lib/index.js.map +1 -0
- package/lib/provider.d.ts +75 -0
- package/lib/provider.js +234 -0
- package/lib/provider.js.map +1 -0
- package/lib/record.d.ts +225 -0
- package/lib/record.js +297 -0
- package/lib/record.js.map +1 -0
- package/lib/zone.d.ts +72 -0
- package/lib/zone.js +90 -0
- package/lib/zone.js.map +1 -0
- package/package.json +61 -0
- package/src/certificate.ts +74 -0
- package/src/handler/acm.ts +160 -0
- package/src/handler/cloudflare.ts +305 -0
- package/src/handler/deps.lock.json +3 -0
- package/src/handler/index.ts +145 -0
- package/src/index.ts +4 -0
- package/src/provider.ts +223 -0
- package/src/record.ts +433 -0
- package/src/zone.ts +115 -0
package/API.md
ADDED
|
@@ -0,0 +1,411 @@
|
|
|
1
|
+
# API Reference
|
|
2
|
+
|
|
3
|
+
The public API surface of `@jaggr2/cdk-cf-dns`.
|
|
4
|
+
|
|
5
|
+
## Table of contents
|
|
6
|
+
* **Enums**
|
|
7
|
+
* [CloudflareRecordType](#enumscloudflarerecordtype)
|
|
8
|
+
* **Interfaces**
|
|
9
|
+
* [CloudflareAaaaRecordProps](#interfacescloudflareaaaarecordprops)
|
|
10
|
+
* [CloudflareARecordProps](#interfacescloudflarearecordprops)
|
|
11
|
+
* [CloudflareCaaRecordProps](#interfacescloudflarecaarecordprops)
|
|
12
|
+
* [CloudflareCnameRecordProps](#interfacescloudflarecnamerecordprops)
|
|
13
|
+
* [CloudflareMxRecordProps](#interfacescloudflaremxrecordprops)
|
|
14
|
+
* [CloudflareRecordProps](#interfacescloudflarerecordprops)
|
|
15
|
+
* [CloudflareTxtRecordProps](#interfacescloudflaretxtrecordprops)
|
|
16
|
+
* [CloudflareValidatedCertificateProps](#interfacescloudflarevalidatedcertificateprops)
|
|
17
|
+
* [CloudflareZoneAttributes](#interfacescloudflarezoneattributes)
|
|
18
|
+
* [ICloudflareZone](#interfacesicloudflarezone)
|
|
19
|
+
* **Classes**
|
|
20
|
+
* [CloudflareAaaaRecord](#classescloudflareaaaarecord)
|
|
21
|
+
* [CloudflareARecord](#classescloudflarearecord)
|
|
22
|
+
* [CloudflareCaaRecord](#classescloudflarecaarecord)
|
|
23
|
+
* [CloudflareCertificateProvider](#classescloudflarecertificateprovider)
|
|
24
|
+
* [CloudflareCnameRecord](#classescloudflarecnamerecord)
|
|
25
|
+
* [CloudflareDnsProvider](#classescloudflarednsprovider)
|
|
26
|
+
* [CloudflareMxRecord](#classescloudflaremxrecord)
|
|
27
|
+
* [CloudflareRecord](#classescloudflarerecord)
|
|
28
|
+
* [CloudflareTtl](#classescloudflarettl)
|
|
29
|
+
* [CloudflareTxtRecord](#classescloudflaretxtrecord)
|
|
30
|
+
* [CloudflareValidatedCertificate](#classescloudflarevalidatedcertificate)
|
|
31
|
+
* [CloudflareZone](#classescloudflarezone)
|
|
32
|
+
|
|
33
|
+
---
|
|
34
|
+
|
|
35
|
+
## Enums
|
|
36
|
+
|
|
37
|
+
### CloudflareRecordType
|
|
38
|
+
|
|
39
|
+
The DNS record types supported by this library.
|
|
40
|
+
|
|
41
|
+
| Name | Description |
|
|
42
|
+
|------|-------------|
|
|
43
|
+
| `A` | An IPv4 address record. |
|
|
44
|
+
| `AAAA` | An IPv6 address record. |
|
|
45
|
+
| `CNAME` | A canonical name record. |
|
|
46
|
+
| `TXT` | A text record. |
|
|
47
|
+
| `MX` | A mail exchanger record. |
|
|
48
|
+
| `NS` | A name server record. |
|
|
49
|
+
| `SRV` | A service locator record. |
|
|
50
|
+
| `CAA` | A certification authority authorization record. |
|
|
51
|
+
| `PTR` | A pointer record. |
|
|
52
|
+
| `URI` | A uniform resource identifier record. |
|
|
53
|
+
|
|
54
|
+
---
|
|
55
|
+
|
|
56
|
+
## Interfaces
|
|
57
|
+
|
|
58
|
+
### CloudflareAaaaRecordProps
|
|
59
|
+
|
|
60
|
+
Properties for an AAAA record.
|
|
61
|
+
|
|
62
|
+
Extends: `Omit<CloudflareRecordProps, "type" | "data" | "priority">`
|
|
63
|
+
|
|
64
|
+
| Name | Type | Description |
|
|
65
|
+
|------|------|-------------|
|
|
66
|
+
| `content` | `string` | The IPv6 address. |
|
|
67
|
+
| `proxied?` | `boolean` | Whether to proxy the record through Cloudflare. |
|
|
68
|
+
|
|
69
|
+
### CloudflareARecordProps
|
|
70
|
+
|
|
71
|
+
Properties for an A record.
|
|
72
|
+
|
|
73
|
+
Extends: `Omit<CloudflareRecordProps, "type" | "data" | "priority">`
|
|
74
|
+
|
|
75
|
+
| Name | Type | Description |
|
|
76
|
+
|------|------|-------------|
|
|
77
|
+
| `content` | `string` | The IPv4 address. |
|
|
78
|
+
| `proxied?` | `boolean` | Whether to proxy the record through Cloudflare. |
|
|
79
|
+
|
|
80
|
+
### CloudflareCaaRecordProps
|
|
81
|
+
|
|
82
|
+
Properties for a CAA record.
|
|
83
|
+
|
|
84
|
+
Extends: `Omit<CloudflareRecordProps, "type" | "data" | "priority" | "proxied">`
|
|
85
|
+
|
|
86
|
+
| Name | Type | Description |
|
|
87
|
+
|------|------|-------------|
|
|
88
|
+
| `content` | `string` | The CAA value. |
|
|
89
|
+
|
|
90
|
+
### CloudflareCnameRecordProps
|
|
91
|
+
|
|
92
|
+
Properties for a CNAME record.
|
|
93
|
+
|
|
94
|
+
Extends: `Omit<CloudflareRecordProps, "type" | "data" | "priority">`
|
|
95
|
+
|
|
96
|
+
| Name | Type | Description |
|
|
97
|
+
|------|------|-------------|
|
|
98
|
+
| `content` | `string` | The canonical name. |
|
|
99
|
+
| `proxied?` | `boolean` | Whether to proxy the record through Cloudflare. |
|
|
100
|
+
|
|
101
|
+
### CloudflareMxRecordProps
|
|
102
|
+
|
|
103
|
+
Properties for an MX record.
|
|
104
|
+
|
|
105
|
+
Extends: `Omit<CloudflareRecordProps, "type" | "data" | "proxied">`
|
|
106
|
+
|
|
107
|
+
| Name | Type | Description |
|
|
108
|
+
|------|------|-------------|
|
|
109
|
+
| `content` | `string` | The mail exchanger host. |
|
|
110
|
+
| `priority` | `number` | The MX priority. |
|
|
111
|
+
|
|
112
|
+
### CloudflareRecordProps
|
|
113
|
+
|
|
114
|
+
Properties for a Cloudflare DNS record.
|
|
115
|
+
|
|
116
|
+
| Name | Type | Description |
|
|
117
|
+
|------|------|-------------|
|
|
118
|
+
| `zone` | `ICloudflareZone` | The Cloudflare zone the record belongs to. |
|
|
119
|
+
| `recordName?` | `string` | Record name. If it does not end in the zone name and `zone.zoneName` is set, it is treated as relative and the zone name is appended. Use `'@'` or omit for the zone apex. |
|
|
120
|
+
| `type` | `CloudflareRecordType` | The record type. |
|
|
121
|
+
| `content?` | `string` | Record value. Mutually exclusive with `data`. |
|
|
122
|
+
| `data?` | `Record<string, unknown>` | Structured value for SRV/CAA/URI records. Mutually exclusive with `content`. |
|
|
123
|
+
| `ttl?` | `cdk.Duration` | The time-to-live for the record. |
|
|
124
|
+
| `proxied?` | `boolean` | Whether to proxy the record through Cloudflare. Only valid for A, AAAA and CNAME. |
|
|
125
|
+
| `priority?` | `number` | Record priority. Required for MX, SRV and URI. |
|
|
126
|
+
| `comment?` | `string` | A free-form comment attached to the record. |
|
|
127
|
+
| `tags?` | `string[]` | Tags attached to the record. |
|
|
128
|
+
| `adoptExisting?` | `boolean` | If a record with the same name+type already exists in Cloudflare, adopt and manage it instead of failing the deployment. |
|
|
129
|
+
| `removalPolicy?` | `cdk.RemovalPolicy` | If RETAIN, the record is left in Cloudflare when the stack resource is deleted. |
|
|
130
|
+
|
|
131
|
+
### CloudflareTxtRecordProps
|
|
132
|
+
|
|
133
|
+
Properties for a TXT record.
|
|
134
|
+
|
|
135
|
+
Extends: `Omit<CloudflareRecordProps, "type" | "data" | "priority" | "proxied">`
|
|
136
|
+
|
|
137
|
+
| Name | Type | Description |
|
|
138
|
+
|------|------|-------------|
|
|
139
|
+
| `content` | `string` | The text value. Values longer than 255 characters are automatically chunked. |
|
|
140
|
+
|
|
141
|
+
### CloudflareValidatedCertificateProps
|
|
142
|
+
|
|
143
|
+
Properties for a Cloudflare-validated ACM certificate.
|
|
144
|
+
|
|
145
|
+
| Name | Type | Description |
|
|
146
|
+
|------|------|-------------|
|
|
147
|
+
| `domainName` | `string` | The primary domain name the certificate covers. |
|
|
148
|
+
| `subjectAlternativeNames?` | `string[]` | Additional domain names the certificate should cover. |
|
|
149
|
+
| `zone` | `ICloudflareZone` | The Cloudflare zone where the DNS validation CNAMEs are written. |
|
|
150
|
+
|
|
151
|
+
### CloudflareZoneAttributes
|
|
152
|
+
|
|
153
|
+
Properties for referencing an existing Cloudflare zone.
|
|
154
|
+
|
|
155
|
+
| Name | Type | Description |
|
|
156
|
+
|------|------|-------------|
|
|
157
|
+
| `zoneId` | `string` | The Cloudflare Zone ID, e.g. `"abc123..."`. This may be a plain string or a CDK token (for example resolved from SSM at deploy time). |
|
|
158
|
+
| `apiToken` | `cdk.aws_secretsmanager.ISecret` | The Secrets Manager secret holding the Cloudflare API token. The secret may contain the token as a raw string or as a JSON blob with an `apiToken` key. Only the secret ARN ever appears in the CloudFormation template. |
|
|
159
|
+
| `zoneName?` | `string` | The apex domain, e.g. `"example.com"`. Enables relative record names. |
|
|
160
|
+
|
|
161
|
+
### ICloudflareZone
|
|
162
|
+
|
|
163
|
+
A reference to a Cloudflare zone that already exists in the Cloudflare account.
|
|
164
|
+
|
|
165
|
+
`CloudflareZone.fromZoneId()` is the only supported way to obtain one; this
|
|
166
|
+
library deliberately does not create zones. Zones are assumed to be managed in
|
|
167
|
+
the Cloudflare dashboard (or elsewhere) and referenced here by their Zone ID.
|
|
168
|
+
|
|
169
|
+
| Name | Type | Description |
|
|
170
|
+
|------|------|-------------|
|
|
171
|
+
| `zoneId` | `string` | The Cloudflare Zone ID, e.g. `"abc123..."`. This may be a plain string or a CDK token (for example resolved from SSM at deploy time). |
|
|
172
|
+
| `zoneName?` | `string` | The apex domain, e.g. `"example.com"`. When set, `recordName` values that do not already end in the zone name are treated as relative and this suffix is appended, matching `aws-cdk-lib/aws-route53` ergonomics. |
|
|
173
|
+
| `apiToken` | `cdk.aws_secretsmanager.ISecret` | The Secrets Manager secret holding the Cloudflare API token. The secret may contain the token as a raw string or as a JSON blob with an `apiToken` key. Only the secret ARN ever appears in the CloudFormation template. |
|
|
174
|
+
|
|
175
|
+
---
|
|
176
|
+
|
|
177
|
+
## Classes
|
|
178
|
+
|
|
179
|
+
### CloudflareAaaaRecord
|
|
180
|
+
|
|
181
|
+
An AAAA record.
|
|
182
|
+
|
|
183
|
+
Extends: `CloudflareRecord`
|
|
184
|
+
|
|
185
|
+
|
|
186
|
+
#### Constructor
|
|
187
|
+
|
|
188
|
+
| Name | Description |
|
|
189
|
+
|------|-------------|
|
|
190
|
+
| `new CloudflareAaaaRecord(scope: Construct, id: string, props: CloudflareAaaaRecordProps)` | |
|
|
191
|
+
|
|
192
|
+
### CloudflareARecord
|
|
193
|
+
|
|
194
|
+
An A record.
|
|
195
|
+
|
|
196
|
+
Extends: `CloudflareRecord`
|
|
197
|
+
|
|
198
|
+
|
|
199
|
+
#### Constructor
|
|
200
|
+
|
|
201
|
+
| Name | Description |
|
|
202
|
+
|------|-------------|
|
|
203
|
+
| `new CloudflareARecord(scope: Construct, id: string, props: CloudflareARecordProps)` | |
|
|
204
|
+
|
|
205
|
+
### CloudflareCaaRecord
|
|
206
|
+
|
|
207
|
+
A CAA record.
|
|
208
|
+
|
|
209
|
+
Extends: `CloudflareRecord`
|
|
210
|
+
|
|
211
|
+
|
|
212
|
+
#### Constructor
|
|
213
|
+
|
|
214
|
+
| Name | Description |
|
|
215
|
+
|------|-------------|
|
|
216
|
+
| `new CloudflareCaaRecord(scope: Construct, id: string, props: CloudflareCaaRecordProps)` | |
|
|
217
|
+
|
|
218
|
+
### CloudflareCertificateProvider
|
|
219
|
+
|
|
220
|
+
The shared custom-resource provider that writes ACM DNS validation records
|
|
221
|
+
into Cloudflare.
|
|
222
|
+
|
|
223
|
+
Like `CloudflareDnsProvider`, one instance exists per stack. It additionally
|
|
224
|
+
grants its handler `acm:DescribeCertificate` on all certificates.
|
|
225
|
+
|
|
226
|
+
Extends: `Construct`
|
|
227
|
+
|
|
228
|
+
|
|
229
|
+
#### Constructor
|
|
230
|
+
|
|
231
|
+
| Name | Description |
|
|
232
|
+
|------|-------------|
|
|
233
|
+
| `new CloudflareCertificateProvider(scope: Construct, id: string)` | *(private)* |
|
|
234
|
+
|
|
235
|
+
#### Properties
|
|
236
|
+
|
|
237
|
+
| Name | Description |
|
|
238
|
+
|------|-------------|
|
|
239
|
+
| `serviceToken: string (readonly)` | The custom-resource service token that the certificate construct uses as its `serviceToken`. |
|
|
240
|
+
|
|
241
|
+
#### Methods
|
|
242
|
+
|
|
243
|
+
| Name | Description |
|
|
244
|
+
|------|-------------|
|
|
245
|
+
| `static getOrCreate(scope: Construct): CloudflareCertificateProvider` | Gets (or lazily creates) the provider for the stack of `scope`. |
|
|
246
|
+
| `grantSecretRead(secret: ISecret): void` | Grants the provider's handler `secretsmanager:GetSecretValue` on the given secret. Repeated grants of the same secret are deduplicated. |
|
|
247
|
+
|
|
248
|
+
### CloudflareCnameRecord
|
|
249
|
+
|
|
250
|
+
A CNAME record.
|
|
251
|
+
|
|
252
|
+
Extends: `CloudflareRecord`
|
|
253
|
+
|
|
254
|
+
|
|
255
|
+
#### Constructor
|
|
256
|
+
|
|
257
|
+
| Name | Description |
|
|
258
|
+
|------|-------------|
|
|
259
|
+
| `new CloudflareCnameRecord(scope: Construct, id: string, props: CloudflareCnameRecordProps)` | |
|
|
260
|
+
|
|
261
|
+
### CloudflareDnsProvider
|
|
262
|
+
|
|
263
|
+
The shared custom-resource provider that performs the Cloudflare DNS API
|
|
264
|
+
calls.
|
|
265
|
+
|
|
266
|
+
One instance exists per stack (a singleton keyed by `Stack`), so a stack with
|
|
267
|
+
many records still has exactly one Lambda handler. All records route their
|
|
268
|
+
custom-resource events through this handler.
|
|
269
|
+
|
|
270
|
+
Extends: `Construct`
|
|
271
|
+
|
|
272
|
+
|
|
273
|
+
#### Constructor
|
|
274
|
+
|
|
275
|
+
| Name | Description |
|
|
276
|
+
|------|-------------|
|
|
277
|
+
| `new CloudflareDnsProvider(scope: Construct, id: string)` | *(private)* |
|
|
278
|
+
|
|
279
|
+
#### Properties
|
|
280
|
+
|
|
281
|
+
| Name | Description |
|
|
282
|
+
|------|-------------|
|
|
283
|
+
| `serviceToken: string (readonly)` | The custom-resource service token that records use as their `serviceToken`. |
|
|
284
|
+
|
|
285
|
+
#### Methods
|
|
286
|
+
|
|
287
|
+
| Name | Description |
|
|
288
|
+
|------|-------------|
|
|
289
|
+
| `static getOrCreate(scope: Construct): CloudflareDnsProvider` | Gets (or lazily creates) the provider for the stack of `scope`. |
|
|
290
|
+
| `grantSecretRead(secret: ISecret): void` | Grants the provider's handler `secretsmanager:GetSecretValue` on the given secret. Repeated grants of the same secret are deduplicated. |
|
|
291
|
+
|
|
292
|
+
### CloudflareMxRecord
|
|
293
|
+
|
|
294
|
+
An MX record.
|
|
295
|
+
|
|
296
|
+
Extends: `CloudflareRecord`
|
|
297
|
+
|
|
298
|
+
|
|
299
|
+
#### Constructor
|
|
300
|
+
|
|
301
|
+
| Name | Description |
|
|
302
|
+
|------|-------------|
|
|
303
|
+
| `new CloudflareMxRecord(scope: Construct, id: string, props: CloudflareMxRecordProps)` | |
|
|
304
|
+
|
|
305
|
+
### CloudflareRecord
|
|
306
|
+
|
|
307
|
+
A Cloudflare DNS record managed as a CloudFormation resource.
|
|
308
|
+
|
|
309
|
+
The construct synthesises a `Custom::CloudflareDnsRecord` custom resource whose
|
|
310
|
+
Lambda handler calls the Cloudflare API. The API token is resolved at runtime
|
|
311
|
+
from Secrets Manager; only the secret ARN ever appears in the template.
|
|
312
|
+
|
|
313
|
+
Extends: `Construct`
|
|
314
|
+
|
|
315
|
+
|
|
316
|
+
#### Constructor
|
|
317
|
+
|
|
318
|
+
| Name | Description |
|
|
319
|
+
|------|-------------|
|
|
320
|
+
| `new CloudflareRecord(scope: Construct, id: string, props: CloudflareRecordProps)` | |
|
|
321
|
+
|
|
322
|
+
#### Properties
|
|
323
|
+
|
|
324
|
+
| Name | Description |
|
|
325
|
+
|------|-------------|
|
|
326
|
+
| `recordId: string (readonly)` | Cloudflare's record ID, from `GetAtt`. This is the value you can use to locate the record in the Cloudflare dashboard. |
|
|
327
|
+
| `domainName: string (readonly)` | The fully-qualified name actually written to Cloudflare (e.g. `app.example.com`). |
|
|
328
|
+
|
|
329
|
+
### CloudflareTtl
|
|
330
|
+
|
|
331
|
+
TTL helpers for Cloudflare records.
|
|
332
|
+
|
|
333
|
+
|
|
334
|
+
#### Properties
|
|
335
|
+
|
|
336
|
+
| Name | Description |
|
|
337
|
+
|------|-------------|
|
|
338
|
+
| `static AUTO: cdk.Duration (readonly)` | Cloudflare's automatic TTL (a value of `1` on the wire). |
|
|
339
|
+
|
|
340
|
+
### CloudflareTxtRecord
|
|
341
|
+
|
|
342
|
+
A TXT record.
|
|
343
|
+
|
|
344
|
+
Extends: `CloudflareRecord`
|
|
345
|
+
|
|
346
|
+
|
|
347
|
+
#### Constructor
|
|
348
|
+
|
|
349
|
+
| Name | Description |
|
|
350
|
+
|------|-------------|
|
|
351
|
+
| `new CloudflareTxtRecord(scope: Construct, id: string, props: CloudflareTxtRecordProps)` | |
|
|
352
|
+
|
|
353
|
+
### CloudflareValidatedCertificate
|
|
354
|
+
|
|
355
|
+
An ACM certificate whose DNS validation records are written into Cloudflare
|
|
356
|
+
automatically, removing the manual copy-paste step.
|
|
357
|
+
|
|
358
|
+
CloudFormation does not expose the validation `ResourceRecord` as a
|
|
359
|
+
certificate attribute, so a custom resource calls `acm:DescribeCertificate`,
|
|
360
|
+
polls until the records are populated (they are absent for a few seconds
|
|
361
|
+
after creation), and writes each unique CNAME into Cloudflare.
|
|
362
|
+
|
|
363
|
+
Note: if the certificate is attached to CloudFront it must live in
|
|
364
|
+
`us-east-1`; this construct does not solve cross-region certificates.
|
|
365
|
+
|
|
366
|
+
Extends: `Construct`
|
|
367
|
+
|
|
368
|
+
|
|
369
|
+
#### Constructor
|
|
370
|
+
|
|
371
|
+
| Name | Description |
|
|
372
|
+
|------|-------------|
|
|
373
|
+
| `new CloudflareValidatedCertificate(scope: Construct, id: string, props: CloudflareValidatedCertificateProps)` | |
|
|
374
|
+
|
|
375
|
+
#### Properties
|
|
376
|
+
|
|
377
|
+
| Name | Description |
|
|
378
|
+
|------|-------------|
|
|
379
|
+
| `certificate: acm.Certificate (readonly)` | The underlying ACM certificate. |
|
|
380
|
+
|
|
381
|
+
### CloudflareZone
|
|
382
|
+
|
|
383
|
+
A reference to an existing Cloudflare zone.
|
|
384
|
+
|
|
385
|
+
Zones are not created by this library; they must already exist in the
|
|
386
|
+
Cloudflare account. Use `CloudflareZone.fromZoneId()` to reference one.
|
|
387
|
+
|
|
388
|
+
Extends: `Construct`
|
|
389
|
+
|
|
390
|
+
|
|
391
|
+
#### Constructor
|
|
392
|
+
|
|
393
|
+
| Name | Description |
|
|
394
|
+
|------|-------------|
|
|
395
|
+
| `new CloudflareZone(scope: Construct, id: string, attrs: CloudflareZoneAttributes)` | *(private)* |
|
|
396
|
+
|
|
397
|
+
#### Properties
|
|
398
|
+
|
|
399
|
+
| Name | Description |
|
|
400
|
+
|------|-------------|
|
|
401
|
+
| `zoneId: string (readonly)` | |
|
|
402
|
+
| `zoneName?: string (readonly)` | |
|
|
403
|
+
| `apiToken: cdk.aws_secretsmanager.ISecret (readonly)` | |
|
|
404
|
+
|
|
405
|
+
#### Methods
|
|
406
|
+
|
|
407
|
+
| Name | Description |
|
|
408
|
+
|------|-------------|
|
|
409
|
+
| `static fromZoneId(scope: Construct, id: string, attrs: CloudflareZoneAttributes): ICloudflareZone` | Reference an existing Cloudflare zone by its Zone ID. |
|
|
410
|
+
|
|
411
|
+
*This document is generated by `npm run docs`; do not edit by hand.*
|
package/README.md
ADDED
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
# @jaggr2/cdk-cf-dns
|
|
2
|
+
|
|
3
|
+
Manage Cloudflare DNS records as CloudFormation resources from AWS CDK, so that
|
|
4
|
+
Route 53 hosted zones (and their $0.50/zone/month charge) are no longer needed
|
|
5
|
+
when Cloudflare is already authoritative for the domain.
|
|
6
|
+
|
|
7
|
+
The developer experience is modelled on `aws-cdk-lib/aws-route53`: reference a
|
|
8
|
+
zone, add records, and let the stack manage them. A single shared Lambda custom
|
|
9
|
+
resource performs the Cloudflare API calls — one Lambda per stack, no matter how
|
|
10
|
+
many records you define.
|
|
11
|
+
|
|
12
|
+
```ts
|
|
13
|
+
const zone = CloudflareZone.fromZoneId(this, 'Zone', {
|
|
14
|
+
zoneId: 'abc123...',
|
|
15
|
+
apiToken: secretsmanager.Secret.fromSecretNameV2(this, 'CfToken', 'cloudflare/dns-token'),
|
|
16
|
+
zoneName: 'example.com',
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
new CloudflareRecord(this, 'AppCname', {
|
|
20
|
+
zone,
|
|
21
|
+
recordName: 'app', // relative -> app.example.com
|
|
22
|
+
type: CloudflareRecordType.CNAME,
|
|
23
|
+
content: distribution.distributionDomainName,
|
|
24
|
+
proxied: true,
|
|
25
|
+
});
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## Install
|
|
29
|
+
|
|
30
|
+
```
|
|
31
|
+
npm install @jaggr2/cdk-cf-dns
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
`aws-cdk-lib` and `constructs` are **peer dependencies** — you must have them
|
|
35
|
+
installed in your own project (any CDK v2 app already does):
|
|
36
|
+
|
|
37
|
+
```
|
|
38
|
+
npm install aws-cdk-lib constructs
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
You also need **esbuild** as a dev dependency, because the library's Lambda
|
|
42
|
+
handler is bundled with `NodejsFunction` at synthesis time:
|
|
43
|
+
|
|
44
|
+
```
|
|
45
|
+
npm install --save-dev esbuild
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
The handler itself has zero runtime npm dependencies — it uses the AWS SDK v3
|
|
49
|
+
that is built into the Lambda runtime and Cloudflare's API over `fetch`.
|
|
50
|
+
|
|
51
|
+
## Cloudflare API token setup
|
|
52
|
+
|
|
53
|
+
1. Log in to the Cloudflare dashboard.
|
|
54
|
+
2. Go to **My Profile → API Tokens → Create Token**.
|
|
55
|
+
3. Use the **Edit zone DNS** template.
|
|
56
|
+
4. Scope the token to the single zone you intend to manage (do not select
|
|
57
|
+
"All zones" unless you really need it).
|
|
58
|
+
5. Create the token, then store it in Secrets Manager so only its ARN ever
|
|
59
|
+
reaches CloudFormation:
|
|
60
|
+
|
|
61
|
+
```
|
|
62
|
+
aws secretsmanager create-secret --name cloudflare/dns-token --secret-string 'cf_token_here'
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
The token grants DNS write access to that zone; scope it as narrowly as possible.
|
|
66
|
+
The token itself never appears in your CDK template, an environment variable, or
|
|
67
|
+
a log line — the Lambda resolves it from Secrets Manager at runtime.
|
|
68
|
+
|
|
69
|
+
## Finding your Zone ID
|
|
70
|
+
|
|
71
|
+
Cloudflare dashboard → select the domain → **Overview** → scroll to the right
|
|
72
|
+
rail → **API** section shows the **Zone ID** (a 32-character hex string).
|
|
73
|
+
|
|
74
|
+
## Usage
|
|
75
|
+
|
|
76
|
+
### CNAME to a CloudFront distribution
|
|
77
|
+
|
|
78
|
+
```ts
|
|
79
|
+
const zone = CloudflareZone.fromZoneId(this, 'Zone', {
|
|
80
|
+
zoneId: 'abc123...',
|
|
81
|
+
zoneName: 'example.com',
|
|
82
|
+
apiToken: secretsmanager.Secret.fromSecretNameV2(this, 'CfToken', 'cloudflare/dns-token'),
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
new CloudflareCnameRecord(this, 'AppCname', {
|
|
86
|
+
zone,
|
|
87
|
+
recordName: 'app', // -> app.example.com
|
|
88
|
+
content: distribution.distributionDomainName,
|
|
89
|
+
proxied: true, // Cloudflare terminates TLS and caches
|
|
90
|
+
});
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
### Apex A record
|
|
94
|
+
|
|
95
|
+
```ts
|
|
96
|
+
new CloudflareARecord(this, 'Apex', {
|
|
97
|
+
zone, // recordName omitted -> zone apex
|
|
98
|
+
content: '1.2.3.4',
|
|
99
|
+
});
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
### TXT record for domain verification
|
|
103
|
+
|
|
104
|
+
```ts
|
|
105
|
+
new CloudflareTxtRecord(this, 'Verify', {
|
|
106
|
+
zone,
|
|
107
|
+
recordName: '_amazonses.example.com',
|
|
108
|
+
content: 'LONG_VERIFICATION_STRING_...', // >255 chars is chunked automatically
|
|
109
|
+
});
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
### MX records
|
|
113
|
+
|
|
114
|
+
```ts
|
|
115
|
+
new CloudflareMxRecord(this, 'Mx', {
|
|
116
|
+
zone,
|
|
117
|
+
content: 'mail.example.com',
|
|
118
|
+
priority: 10,
|
|
119
|
+
});
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
Every record type also works through the generic `CloudflareRecord` with
|
|
123
|
+
`CloudflareRecordType`, including `SRV`, `CAA` and `URI` (which take a `data`
|
|
124
|
+
object instead of a plain `content` string). Thin subclasses
|
|
125
|
+
(`CloudflareARecord`, `CloudflareAaaaRecord`, `CloudflareCnameRecord`,
|
|
126
|
+
`CloudflareTxtRecord`, `CloudflareMxRecord`, `CloudflareCaaRecord`) narrow the
|
|
127
|
+
type and validate their own required fields.
|
|
128
|
+
|
|
129
|
+
## Proxied records and ACM
|
|
130
|
+
|
|
131
|
+
If you use ACM certificate DNS validation (for example with CloudFront), the
|
|
132
|
+
`_acme-challenge` CNAMEs generated by `CertificateValidation.fromDns()` **must be
|
|
133
|
+
`proxied: false`** — DNS-01 validation requires the raw CNAME to resolve, and a
|
|
134
|
+
proxied record will never pass. Use a plain CNAME record for validation names:
|
|
135
|
+
|
|
136
|
+
```ts
|
|
137
|
+
new CloudflareCnameRecord(this, 'AcmeValidation', {
|
|
138
|
+
zone,
|
|
139
|
+
recordName: record.targetName, // e.g. _acme-challenge.example.com
|
|
140
|
+
content: record.domainName, // the validation value
|
|
141
|
+
proxied: false, // CRITICAL
|
|
142
|
+
ttl: cdk.Duration.seconds(60),
|
|
143
|
+
});
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
## Automated ACM DNS validation
|
|
147
|
+
|
|
148
|
+
`CloudflareValidatedCertificate` removes the manual copy-paste step during ACM
|
|
149
|
+
DNS validation. It creates the certificate and, via a second shared custom
|
|
150
|
+
resource, polls `acm:DescribeCertificate` until the validation CNAMEs are
|
|
151
|
+
populated (they are absent for a few seconds after creation) and writes each one
|
|
152
|
+
into Cloudflare with `proxied: false` and `ttl: 60`. A wildcard SAN and its apex
|
|
153
|
+
share a single validation record.
|
|
154
|
+
|
|
155
|
+
```ts
|
|
156
|
+
const cert = new CloudflareValidatedCertificate(this, 'Cert', {
|
|
157
|
+
domainName: 'example.com',
|
|
158
|
+
subjectAlternativeNames: ['*.example.com'],
|
|
159
|
+
zone,
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
new cloudfront.Distribution(this, 'Dist', {
|
|
163
|
+
defaultBehavior: {
|
|
164
|
+
origin: origin,
|
|
165
|
+
viewerProtocolPolicy: cloudfront.ViewerProtocolPolicy.REDIRECT_TO_HTTPS,
|
|
166
|
+
},
|
|
167
|
+
domainNames: ['example.com'],
|
|
168
|
+
certificate: cert.certificate,
|
|
169
|
+
});
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
The validation records are left in Cloudflare after issuance — they are harmless.
|
|
173
|
+
If the certificate is attached to CloudFront it must live in **us-east-1**.
|
|
174
|
+
|
|
175
|
+
## Limitations
|
|
176
|
+
|
|
177
|
+
- **No zone creation.** Zones must already exist in Cloudflare; this library only
|
|
178
|
+
references them by Zone ID.
|
|
179
|
+
- **No zone-level settings.** Record-only. SSL/TLS modes, page rules, workers, and
|
|
180
|
+
other zone configuration are out of scope.
|
|
181
|
+
- **No page rules** and **no bulk import**.
|
|
182
|
+
- **Out-of-band edits** to records in the Cloudflare dashboard are only reconciled
|
|
183
|
+
on the next stack update (an update re-applies the desired state; a delete of a
|
|
184
|
+
record that only exists out-of-band is ignored).
|
|
185
|
+
- **Deleting a managed record** requires `adoptExisting: true` if it was created
|
|
186
|
+
outside the stack, otherwise the deployment fails with a helpful error.
|
|
187
|
+
|
|
188
|
+
## API
|
|
189
|
+
|
|
190
|
+
See [API.md](./API.md) for the generated reference.
|
|
191
|
+
|
|
192
|
+
## License
|
|
193
|
+
|
|
194
|
+
MIT
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import * as acm from 'aws-cdk-lib/aws-certificatemanager';
|
|
2
|
+
import { Construct } from 'constructs';
|
|
3
|
+
import { ICloudflareZone } from './zone';
|
|
4
|
+
/**
|
|
5
|
+
* Properties for a Cloudflare-validated ACM certificate.
|
|
6
|
+
*/
|
|
7
|
+
export interface CloudflareValidatedCertificateProps {
|
|
8
|
+
/**
|
|
9
|
+
* The primary domain name the certificate covers.
|
|
10
|
+
*/
|
|
11
|
+
readonly domainName: string;
|
|
12
|
+
/**
|
|
13
|
+
* Additional domain names the certificate should cover.
|
|
14
|
+
*
|
|
15
|
+
* @default - no additional names
|
|
16
|
+
*/
|
|
17
|
+
readonly subjectAlternativeNames?: string[];
|
|
18
|
+
/**
|
|
19
|
+
* The Cloudflare zone where the DNS validation CNAMEs are written.
|
|
20
|
+
*/
|
|
21
|
+
readonly zone: ICloudflareZone;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* An ACM certificate whose DNS validation records are written into Cloudflare
|
|
25
|
+
* automatically, removing the manual copy-paste step.
|
|
26
|
+
*
|
|
27
|
+
* CloudFormation does not expose the validation `ResourceRecord` as a
|
|
28
|
+
* certificate attribute, so a custom resource calls `acm:DescribeCertificate`,
|
|
29
|
+
* polls until the records are populated (they are absent for a few seconds
|
|
30
|
+
* after creation), and writes each unique CNAME into Cloudflare.
|
|
31
|
+
*
|
|
32
|
+
* Note: if the certificate is attached to CloudFront it must live in
|
|
33
|
+
* `us-east-1`; this construct does not solve cross-region certificates.
|
|
34
|
+
*/
|
|
35
|
+
export declare class CloudflareValidatedCertificate extends Construct {
|
|
36
|
+
/**
|
|
37
|
+
* The underlying ACM certificate.
|
|
38
|
+
*/
|
|
39
|
+
readonly certificate: acm.Certificate;
|
|
40
|
+
constructor(scope: Construct, id: string, props: CloudflareValidatedCertificateProps);
|
|
41
|
+
}
|