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:
Andreas Olsson 2018-08-18 21:56:02 +02:00 committed by René Moser
parent 47b527224a
commit 8691c7ced7
3 changed files with 283 additions and 10 deletions

View file

@ -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)

View 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

View file

@ -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