Add DS record type to cloudflare_dns module (#44349)
Cloudflare recently added support for DS records. They are used to delegate DNSSEC trust to a subdomain.
This commit is contained in:
parent
47b527224a
commit
8691c7ced7
3 changed files with 283 additions and 10 deletions
|
@ -35,8 +35,7 @@ options:
|
|||
required: true
|
||||
algorithm:
|
||||
description:
|
||||
- Algorithm number. Required for C(type=SSHFP) when C(state=present).
|
||||
choices: [ 1, 2, 3, 4 ]
|
||||
- Algorithm number. Required for C(type=DS) and C(type=SSHFP) when C(state=present).
|
||||
type: int
|
||||
version_added: 2.7
|
||||
cert_usage:
|
||||
|
@ -47,10 +46,15 @@ options:
|
|||
version_added: 2.7
|
||||
hash_type:
|
||||
description:
|
||||
- Hash type number. Required for C(type=SSHFP) and C(type=TLSA) when C(state=present).
|
||||
- Hash type number. Required for C(type=DS), C(type=SSHFP) and C(type=TLSA) when C(state=present).
|
||||
choices: [ 1, 2 ]
|
||||
type: int
|
||||
version_added: 2.7
|
||||
key_tag:
|
||||
description:
|
||||
- DNSSEC key tag. Needed for C(type=DS) when C(state=present).
|
||||
type: int
|
||||
version_added: 2.7
|
||||
port:
|
||||
description: Service port. Required for C(type=SRV) and C(type=TLSA).
|
||||
priority:
|
||||
|
@ -99,8 +103,8 @@ options:
|
|||
type:
|
||||
description:
|
||||
- The type of DNS record to create. Required if C(state=present)
|
||||
- C(type=SSHFP) and C(type=TLSA) added in Ansible 2.7.
|
||||
choices: [ 'A', 'AAAA', 'CNAME', 'TXT', 'SRV', 'MX', 'NS', 'SPF', 'SSHFP', 'TLSA' ]
|
||||
- C(type=DS), C(type=SSHFP) and C(type=TLSA) added in Ansible 2.7.
|
||||
choices: [ 'A', 'AAAA', 'CNAME', 'TXT', 'SRV', 'MX', 'NS', 'DS', 'SPF', 'SSHFP', 'TLSA' ]
|
||||
value:
|
||||
description:
|
||||
- The record value. Required for C(state=present)
|
||||
|
@ -207,6 +211,16 @@ EXAMPLES = '''
|
|||
selector: 1
|
||||
hash_type: 1
|
||||
value: 6b76d034492b493e15a7376fccd08e63befdad0edab8e442562f532338364bf3
|
||||
|
||||
# Create a DS record for subdomain.example.com
|
||||
- cloudflare_dns:
|
||||
zone: example.com
|
||||
record: subdomain
|
||||
type: DS
|
||||
key_tag: 5464
|
||||
algorithm: 8
|
||||
hash_type: 2
|
||||
value: B4EB5AC4467D2DFB3BAF9FB9961DC1B6FED54A58CDFAA3E465081EC86F89BFAB
|
||||
'''
|
||||
|
||||
RETURN = '''
|
||||
|
@ -227,7 +241,7 @@ record:
|
|||
sample: 2016-03-25T19:09:42.516553Z
|
||||
data:
|
||||
description: additional record data
|
||||
returned: success, if type is SRV, SSHFP or TLSA
|
||||
returned: success, if type is SRV, DS, SSHFP or TLSA
|
||||
type: dictionary
|
||||
sample: {
|
||||
name: "jabber",
|
||||
|
@ -320,6 +334,7 @@ class CloudflareAPI(object):
|
|||
self.algorithm = module.params['algorithm']
|
||||
self.cert_usage = module.params['cert_usage']
|
||||
self.hash_type = module.params['hash_type']
|
||||
self.key_tag = module.params['key_tag']
|
||||
self.port = module.params['port']
|
||||
self.priority = module.params['priority']
|
||||
self.proto = module.params['proto']
|
||||
|
@ -357,6 +372,10 @@ class CloudflareAPI(object):
|
|||
if not self.record.endswith(self.zone):
|
||||
self.record = self.record + '.' + self.zone
|
||||
|
||||
if (self.type == 'DS'):
|
||||
if self.record == self.zone:
|
||||
self.module.fail_json(msg="DS records only apply to subdomains.")
|
||||
|
||||
def _cf_simple_api_call(self, api_call, method='GET', payload=None):
|
||||
headers = {'X-Auth-Email': self.account_email,
|
||||
'X-Auth-Key': self.account_api_token,
|
||||
|
@ -503,7 +522,8 @@ class CloudflareAPI(object):
|
|||
|
||||
def delete_dns_records(self, **kwargs):
|
||||
params = {}
|
||||
for param in ['port', 'proto', 'service', 'solo', 'type', 'record', 'value', 'weight', 'zone', 'algorithm', 'cert_usage', 'hash_type', 'selector']:
|
||||
for param in ['port', 'proto', 'service', 'solo', 'type', 'record', 'value', 'weight', 'zone',
|
||||
'algorithm', 'cert_usage', 'hash_type', 'selector', 'key_tag']:
|
||||
if param in kwargs:
|
||||
params[param] = kwargs[param]
|
||||
else:
|
||||
|
@ -516,6 +536,9 @@ class CloudflareAPI(object):
|
|||
if not (params['value'] is None or params['value'] == ''):
|
||||
content = str(params['weight']) + '\t' + str(params['port']) + '\t' + params['value']
|
||||
search_record = params['service'] + '.' + params['proto'] + '.' + params['record']
|
||||
elif params['type'] == 'DS':
|
||||
if not (params['value'] is None or params['value'] == ''):
|
||||
content = str(params['key_tag']) + '\t' + str(params['algorithm']) + '\t' + str(params['hash_type']) + '\t' + params['value']
|
||||
elif params['type'] == 'SSHFP':
|
||||
if not (params['value'] is None or params['value'] == ''):
|
||||
content = str(params['algorithm']) + '\t' + str(params['hash_type']) + '\t' + params['value']
|
||||
|
@ -545,7 +568,7 @@ class CloudflareAPI(object):
|
|||
def ensure_dns_record(self, **kwargs):
|
||||
params = {}
|
||||
for param in ['port', 'priority', 'proto', 'proxied', 'service', 'ttl', 'type', 'record', 'value', 'weight', 'zone',
|
||||
'algorithm', 'cert_usage', 'hash_type', 'selector']:
|
||||
'algorithm', 'cert_usage', 'hash_type', 'selector', 'key_tag']:
|
||||
if param in kwargs:
|
||||
params[param] = kwargs[param]
|
||||
else:
|
||||
|
@ -607,6 +630,24 @@ class CloudflareAPI(object):
|
|||
search_value = str(params['weight']) + '\t' + str(params['port']) + '\t' + params['value']
|
||||
search_record = params['service'] + '.' + params['proto'] + '.' + params['record']
|
||||
|
||||
if params['type'] == 'DS':
|
||||
for attr in [params['key_tag'], params['algorithm'], params['hash_type'], params['value']]:
|
||||
if (attr is None) or (attr == ''):
|
||||
self.module.fail_json(msg="You must provide key_tag, algorithm, hash_type and a value to create this record type")
|
||||
ds_data = {
|
||||
"key_tag": params['key_tag'],
|
||||
"algorithm": params['algorithm'],
|
||||
"digest_type": params['hash_type'],
|
||||
"digest": params['value'],
|
||||
}
|
||||
new_record = {
|
||||
"type": params['type'],
|
||||
"name": params['record'],
|
||||
'data': ds_data,
|
||||
"ttl": params['ttl'],
|
||||
}
|
||||
search_value = str(params['key_tag']) + '\t' + str(params['algorithm']) + '\t' + str(params['hash_type']) + '\t' + params['value']
|
||||
|
||||
if params['type'] == 'SSHFP':
|
||||
for attr in [params['algorithm'], params['hash_type'], params['value']]:
|
||||
if (attr is None) or (attr == ''):
|
||||
|
@ -686,9 +727,10 @@ def main():
|
|||
argument_spec=dict(
|
||||
account_api_token=dict(required=True, no_log=True, type='str'),
|
||||
account_email=dict(required=True, type='str'),
|
||||
algorithm=dict(required=False, default=None, choices=[1, 2, 3, 4], type='int'),
|
||||
algorithm=dict(required=False, default=None, type='int'),
|
||||
cert_usage=dict(required=False, default=None, choices=[0, 1, 2, 3], type='int'),
|
||||
hash_type=dict(required=False, default=None, choices=[1, 2], type='int'),
|
||||
key_tag=dict(required=False, default=None, type='int'),
|
||||
port=dict(required=False, default=None, type='int'),
|
||||
priority=dict(required=False, default=1, type='int'),
|
||||
proto=dict(required=False, default=None, type='str'),
|
||||
|
@ -700,7 +742,7 @@ def main():
|
|||
state=dict(required=False, default='present', choices=['present', 'absent'], type='str'),
|
||||
timeout=dict(required=False, default=30, type='int'),
|
||||
ttl=dict(required=False, default=1, type='int'),
|
||||
type=dict(required=False, default=None, choices=['A', 'AAAA', 'CNAME', 'TXT', 'SRV', 'MX', 'NS', 'SPF', 'SSHFP', 'TLSA'], type='str'),
|
||||
type=dict(required=False, default=None, choices=['A', 'AAAA', 'CNAME', 'TXT', 'SRV', 'MX', 'NS', 'DS', 'SPF', 'SSHFP', 'TLSA'], type='str'),
|
||||
value=dict(required=False, default=None, aliases=['content'], type='str'),
|
||||
weight=dict(required=False, default=1, type='int'),
|
||||
zone=dict(required=True, default=None, aliases=['domain'], type='str'),
|
||||
|
@ -736,6 +778,13 @@ def main():
|
|||
and (module.params['value'] is None or module.params['value'] == ''))):
|
||||
module.fail_json(msg="For TLSA records the params cert_usage, selector, hash_type and value all need to be defined, or not at all.")
|
||||
|
||||
if module.params['type'] == 'DS':
|
||||
if not ((module.params['key_tag'] is not None and module.params['algorithm'] is not None and module.params['hash_type'] is not None
|
||||
and not (module.params['value'] is None or module.params['value'] == ''))
|
||||
or (module.params['key_tag'] is None and module.params['algorithm'] is None and module.params['hash_type'] is None
|
||||
and (module.params['value'] is None or module.params['value'] == ''))):
|
||||
module.fail_json(msg="For DS records the params key_tag, algorithm, hash_type and value all need to be defined, or not at all.")
|
||||
|
||||
changed = False
|
||||
cf_api = CloudflareAPI(module)
|
||||
|
||||
|
|
223
test/legacy/roles/test_cloudflare_dns/tasks/ds_record.yml
Normal file
223
test/legacy/roles/test_cloudflare_dns/tasks/ds_record.yml
Normal file
|
@ -0,0 +1,223 @@
|
|||
---
|
||||
######## DS record tests #################
|
||||
|
||||
- name: "Test: DS record creation"
|
||||
cloudflare_dns:
|
||||
account_email: "{{ cloudflare_email }}"
|
||||
account_api_token: "{{ cloudflare_api_token }}"
|
||||
zone: "{{ cloudflare_zone }}"
|
||||
record: "{{ cloudflare_dns_record }}"
|
||||
type: DS
|
||||
key_tag: 54592
|
||||
algorithm: 8
|
||||
hash_type: 2
|
||||
value: 6A95688429A7796533165ACEFBE91BD115DAD747AA4E1D5DCA70DF040C68216F
|
||||
ttl: 150
|
||||
register: cloudflare_dns
|
||||
|
||||
- name: "Validate: DS record creation"
|
||||
assert:
|
||||
that:
|
||||
- cloudflare_dns is successful
|
||||
- cloudflare_dns is changed
|
||||
- cloudflare_dns.result.record.content == "54592\t8\t2\t6A95688429A7796533165ACEFBE91BD115DAD747AA4E1D5DCA70DF040C68216F"
|
||||
- cloudflare_dns.result.record.ttl == 150
|
||||
- cloudflare_dns.result.record.type == "DS"
|
||||
- cloudflare_dns.result.record.name == "{{ cloudflare_dns_record }}.{{ cloudflare_zone }}"
|
||||
- cloudflare_dns.result.record.zone_name == "{{ cloudflare_zone }}"
|
||||
- cloudflare_dns.result.record.data.key_tag == 54592
|
||||
- cloudflare_dns.result.record.data.algorithm == 8
|
||||
- cloudflare_dns.result.record.data.digest_type == 2
|
||||
- cloudflare_dns.result.record.data.digest == "6A95688429A7796533165ACEFBE91BD115DAD747AA4E1D5DCA70DF040C68216F"
|
||||
|
||||
- name: "Test: DS record idempotency"
|
||||
cloudflare_dns:
|
||||
account_email: "{{ cloudflare_email }}"
|
||||
account_api_token: "{{ cloudflare_api_token }}"
|
||||
zone: "{{ cloudflare_zone }}"
|
||||
record: "{{ cloudflare_dns_record }}"
|
||||
type: DS
|
||||
key_tag: 54592
|
||||
algorithm: 8
|
||||
hash_type: 2
|
||||
value: 6A95688429A7796533165ACEFBE91BD115DAD747AA4E1D5DCA70DF040C68216F
|
||||
ttl: 150
|
||||
register: cloudflare_dns
|
||||
|
||||
- name: "Validate: DS record idempotency"
|
||||
assert:
|
||||
that:
|
||||
- cloudflare_dns is successful
|
||||
- cloudflare_dns is not changed
|
||||
|
||||
- name: "Test: DS record update"
|
||||
cloudflare_dns:
|
||||
account_email: "{{ cloudflare_email }}"
|
||||
account_api_token: "{{ cloudflare_api_token }}"
|
||||
zone: "{{ cloudflare_zone }}"
|
||||
record: "{{ cloudflare_dns_record }}"
|
||||
type: DS
|
||||
key_tag: 54592
|
||||
algorithm: 8
|
||||
hash_type: 2
|
||||
value: 6A95688429A7796533165ACEFBE91BD115DAD747AA4E1D5DCA70DF040C68216F
|
||||
ttl: 300
|
||||
register: cloudflare_dns
|
||||
|
||||
- name: "Validate: DS record update"
|
||||
assert:
|
||||
that:
|
||||
- cloudflare_dns is successful
|
||||
- cloudflare_dns is changed
|
||||
- cloudflare_dns.result.record.ttl == 300
|
||||
|
||||
- name: "Test: DS record duplicate (create new record)"
|
||||
cloudflare_dns:
|
||||
account_email: "{{ cloudflare_email }}"
|
||||
account_api_token: "{{ cloudflare_api_token }}"
|
||||
zone: "{{ cloudflare_zone }}"
|
||||
record: "{{ cloudflare_dns_record }}"
|
||||
type: DS
|
||||
key_tag: 24397
|
||||
algorithm: 8
|
||||
hash_type: 2
|
||||
value: 8A5E62E004BFA4CF4B42BE9405C39EBA1AC5A91BDE181FB73E935ECC1F3F5556
|
||||
ttl: 300
|
||||
register: cloudflare_dns
|
||||
|
||||
- name: "Validate: DS record duplicate (create new record)"
|
||||
assert:
|
||||
that:
|
||||
- cloudflare_dns is successful
|
||||
- cloudflare_dns is changed
|
||||
- cloudflare_dns.result.record.content == "24397\t8\t2\t8A5E62E004BFA4CF4B42BE9405C39EBA1AC5A91BDE181FB73E935ECC1F3F5556"
|
||||
- cloudflare_dns.result.record.ttl == 300
|
||||
- cloudflare_dns.result.record.type == "DS"
|
||||
- cloudflare_dns.result.record.name == "{{ cloudflare_dns_record }}.{{ cloudflare_zone }}"
|
||||
- cloudflare_dns.result.record.zone_name == "{{ cloudflare_zone }}"
|
||||
- cloudflare_dns.result.record.data.key_tag == 24397
|
||||
- cloudflare_dns.result.record.data.algorithm == 8
|
||||
- cloudflare_dns.result.record.data.digest_type == 2
|
||||
- cloudflare_dns.result.record.data.digest == "8A5E62E004BFA4CF4B42BE9405C39EBA1AC5A91BDE181FB73E935ECC1F3F5556"
|
||||
|
||||
- name: "Test: DS record duplicate (old record present)"
|
||||
cloudflare_dns:
|
||||
account_email: "{{ cloudflare_email }}"
|
||||
account_api_token: "{{ cloudflare_api_token }}"
|
||||
zone: "{{ cloudflare_zone }}"
|
||||
record: "{{ cloudflare_dns_record }}"
|
||||
type: DS
|
||||
key_tag: 54592
|
||||
algorithm: 8
|
||||
hash_type: 2
|
||||
value: 6A95688429A7796533165ACEFBE91BD115DAD747AA4E1D5DCA70DF040C68216F
|
||||
ttl: 300
|
||||
register: cloudflare_dns
|
||||
|
||||
- name: "Validate: DS record duplicate (old record present)"
|
||||
assert:
|
||||
that:
|
||||
- cloudflare_dns is successful
|
||||
- cloudflare_dns is not changed
|
||||
- cloudflare_dns.result.record.content == "54592\t8\t2\t6A95688429A7796533165ACEFBE91BD115DAD747AA4E1D5DCA70DF040C68216F"
|
||||
- cloudflare_dns.result.record.ttl == 300
|
||||
- cloudflare_dns.result.record.type == "DS"
|
||||
- cloudflare_dns.result.record.name == "{{ cloudflare_dns_record }}.{{ cloudflare_zone }}"
|
||||
- cloudflare_dns.result.record.zone_name == "{{ cloudflare_zone }}"
|
||||
- cloudflare_dns.result.record.data.key_tag == 54592
|
||||
- cloudflare_dns.result.record.data.algorithm == 8
|
||||
- cloudflare_dns.result.record.data.digest_type == 2
|
||||
- cloudflare_dns.result.record.data.digest == "6A95688429A7796533165ACEFBE91BD115DAD747AA4E1D5DCA70DF040C68216F"
|
||||
|
||||
- name: "Test: DS record duplicate (make new record solo)"
|
||||
cloudflare_dns:
|
||||
account_email: "{{ cloudflare_email }}"
|
||||
account_api_token: "{{ cloudflare_api_token }}"
|
||||
zone: "{{ cloudflare_zone }}"
|
||||
record: "{{ cloudflare_dns_record }}"
|
||||
type: DS
|
||||
key_tag: 24397
|
||||
algorithm: 8
|
||||
hash_type: 2
|
||||
value: 8A5E62E004BFA4CF4B42BE9405C39EBA1AC5A91BDE181FB73E935ECC1F3F5556
|
||||
ttl: 300
|
||||
solo: true
|
||||
register: cloudflare_dns
|
||||
|
||||
- name: "Validate: DS record duplicate (make new record solo)"
|
||||
assert:
|
||||
that:
|
||||
- cloudflare_dns is successful
|
||||
- cloudflare_dns is changed
|
||||
- cloudflare_dns.result.record.content == "24397\t8\t2\t8A5E62E004BFA4CF4B42BE9405C39EBA1AC5A91BDE181FB73E935ECC1F3F5556"
|
||||
- cloudflare_dns.result.record.ttl == 300
|
||||
- cloudflare_dns.result.record.type == "DS"
|
||||
- cloudflare_dns.result.record.name == "{{ cloudflare_dns_record }}.{{ cloudflare_zone }}"
|
||||
- cloudflare_dns.result.record.zone_name == "{{ cloudflare_zone }}"
|
||||
- cloudflare_dns.result.record.data.key_tag == 24397
|
||||
- cloudflare_dns.result.record.data.algorithm == 8
|
||||
- cloudflare_dns.result.record.data.digest_type == 2
|
||||
- cloudflare_dns.result.record.data.digest == "8A5E62E004BFA4CF4B42BE9405C39EBA1AC5A91BDE181FB73E935ECC1F3F5556"
|
||||
|
||||
- name: "Test: DS record duplicate (old record absent)"
|
||||
cloudflare_dns:
|
||||
account_email: "{{ cloudflare_email }}"
|
||||
account_api_token: "{{ cloudflare_api_token }}"
|
||||
zone: "{{ cloudflare_zone }}"
|
||||
record: "{{ cloudflare_dns_record }}"
|
||||
type: DS
|
||||
key_tag: 54592
|
||||
algorithm: 8
|
||||
hash_type: 2
|
||||
value: 6A95688429A7796533165ACEFBE91BD115DAD747AA4E1D5DCA70DF040C68216F
|
||||
ttl: 300
|
||||
state: absent
|
||||
register: cloudflare_dns
|
||||
|
||||
- name: "Validate: DS record duplicate (old record absent)"
|
||||
assert:
|
||||
that:
|
||||
- cloudflare_dns is successful
|
||||
- cloudflare_dns is not changed
|
||||
|
||||
- name: "Test: DS record deletion"
|
||||
cloudflare_dns:
|
||||
account_email: "{{ cloudflare_email }}"
|
||||
account_api_token: "{{ cloudflare_api_token }}"
|
||||
zone: "{{ cloudflare_zone }}"
|
||||
record: "{{ cloudflare_dns_record }}"
|
||||
type: DS
|
||||
key_tag: 24397
|
||||
algorithm: 8
|
||||
hash_type: 2
|
||||
value: 8A5E62E004BFA4CF4B42BE9405C39EBA1AC5A91BDE181FB73E935ECC1F3F5556
|
||||
ttl: 300
|
||||
state: absent
|
||||
register: cloudflare_dns
|
||||
|
||||
- name: "Validate: DS record deletion"
|
||||
assert:
|
||||
that:
|
||||
- cloudflare_dns is successful
|
||||
- cloudflare_dns is changed
|
||||
|
||||
- name: "Test: DS record deletion succeeded"
|
||||
cloudflare_dns:
|
||||
account_email: "{{ cloudflare_email }}"
|
||||
account_api_token: "{{ cloudflare_api_token }}"
|
||||
zone: "{{ cloudflare_zone }}"
|
||||
record: "{{ cloudflare_dns_record }}"
|
||||
type: DS
|
||||
key_tag: 24397
|
||||
algorithm: 8
|
||||
hash_type: 2
|
||||
value: 8A5E62E004BFA4CF4B42BE9405C39EBA1AC5A91BDE181FB73E935ECC1F3F5556
|
||||
ttl: 300
|
||||
state: absent
|
||||
register: cloudflare_dns
|
||||
|
||||
- name: "Validate: DS record deletion succeeded"
|
||||
assert:
|
||||
that:
|
||||
- cloudflare_dns is successful
|
||||
- cloudflare_dns is not changed
|
|
@ -59,6 +59,7 @@
|
|||
- include: cname_record.yml
|
||||
- include: txt_record.yml
|
||||
- include: ns_record.yml
|
||||
- include: ds_record.yml
|
||||
- include: spf_record.yml
|
||||
- include: mx_record.yml
|
||||
- include: srv_record.yml
|
||||
|
|
Loading…
Reference in a new issue