1
0
mirror of https://github.com/github/octodns.git synced 2024-05-11 05:55:00 +00:00

Option to handle Cloudflare proxied records

This change imports records that are marked as proxied so that they can be synced to other DNS providers as described in
[this support acticle](https://support.cloudflare.com/hc/en-us/articles/115000830351-How-to-configure-DNS-for-CNAME-partial-setup-when-managing-DNS-externally).
Records that use this functionality will be ignored by this provider and not be synced back to Cloudflare as we don't know the origin record values that would be required.

This change does not allow you to enable, disable or configure the CDN itself as that would require a lot of metadata to be handled by OctoDNS.
The intention of this change is to allow users to run a multi-DNS provider setup without sending any traffic to their origin directly.

See also github/octodns#45
This commit is contained in:
Paul van Brouwershaven
2018-01-31 09:57:36 +01:00
parent 507e748b98
commit b5e7af0398
2 changed files with 162 additions and 13 deletions

View File

@@ -14,14 +14,14 @@ from ..record import Record, Update
from .base import BaseProvider
class CloudflareAuthenticationError(Exception):
class CloudflareError(Exception):
def __init__(self, data):
try:
message = data['errors'][0]['message']
except (IndexError, KeyError):
message = 'Authentication error'
super(CloudflareAuthenticationError, self).__init__(message)
super(CloudflareError, self).__init__(message)
class CloudflareProvider(BaseProvider):
@@ -34,6 +34,12 @@ class CloudflareProvider(BaseProvider):
email: dns-manager@example.com
# The api key (required)
token: foo
# Import CDN enabled records as CNAME to {}.cdn.cloudflare.net. Records
# ending at .cdn.cloudflare.net. will be ignored when this provider is
# not used as the source and the cdn option is enabled.
#
# See: https://support.cloudflare.com/hc/en-us/articles/115000830351
cdn: false
'''
SUPPORTS_GEO = False
# TODO: support SRV
@@ -43,9 +49,10 @@ class CloudflareProvider(BaseProvider):
MIN_TTL = 120
TIMEOUT = 15
def __init__(self, id, email, token, *args, **kwargs):
def __init__(self, id, email, token, cdn=False, *args, **kwargs):
self.log = getLogger('CloudflareProvider[{}]'.format(id))
self.log.debug('__init__: id=%s, email=%s, token=***', id, email)
self.log.debug('__init__: id=%s, email=%s, token=***, cdn=%s', id,
email, cdn)
super(CloudflareProvider, self).__init__(id, *args, **kwargs)
sess = Session()
@@ -53,6 +60,7 @@ class CloudflareProvider(BaseProvider):
'X-Auth-Email': email,
'X-Auth-Key': token,
})
self.cdn = cdn
self._sess = sess
self._zones = None
@@ -65,8 +73,8 @@ class CloudflareProvider(BaseProvider):
resp = self._sess.request(method, url, params=params, json=data,
timeout=self.TIMEOUT)
self.log.debug('_request: status=%d', resp.status_code)
if resp.status_code == 403:
raise CloudflareAuthenticationError(resp.json())
if resp.status_code == 400 or resp.status_code == 403:
raise CloudflareError(resp.json())
resp.raise_for_status()
return resp.json()
@@ -88,6 +96,18 @@ class CloudflareProvider(BaseProvider):
return self._zones
def _data_for_cdn(self, name, _type, records):
self.log.info('CDN rewrite for %s', records[0]['name'])
_type = "CNAME"
if name == "":
_type = "ALIAS"
return {
'ttl': records[0]['ttl'],
'type': _type,
'value': '{}.cdn.cloudflare.net.'.format(records[0]['name']),
}
def _data_for_multiple(self, _type, records):
return {
'ttl': records[0]['ttl'],
@@ -170,8 +190,8 @@ class CloudflareProvider(BaseProvider):
return self._zone_records[zone.name]
def populate(self, zone, target=False, lenient=False):
self.log.debug('populate: name=%s, target=%s, lenient=%s', zone.name,
target, lenient)
self.log.debug('populate: name=%s, cdn=%s, target=%s, lenient=%s',
zone.name, self.cdn, target, lenient)
before = len(zone.records)
records = self.zone_records(zone)
@@ -186,12 +206,18 @@ class CloudflareProvider(BaseProvider):
for name, types in values.items():
for _type, records in types.items():
# Cloudflare supports ALIAS semantics with root CNAMEs
if _type == 'CNAME' and name == '':
_type = 'ALIAS'
# rewrite Cloudflare proxied records
if self.cdn and records[0]['proxied']:
data = self._data_for_cdn(name, _type, records)
else:
# Cloudflare supports ALIAS semantics with root CNAMEs
if _type == 'CNAME' and name == '':
_type = 'ALIAS'
data_for = getattr(self, '_data_for_{}'.format(_type))
data = data_for(_type, records)
data_for = getattr(self, '_data_for_{}'.format(_type))
data = data_for(_type, records)
record = Record.new(zone, name, data, source=self,
lenient=lenient)
zone.add_record(record)
@@ -250,6 +276,12 @@ class CloudflareProvider(BaseProvider):
if _type == 'ALIAS':
_type = 'CNAME'
# If this is a record to enable to Cloudflare CDN don't update as we
# don't know the original values.
if (self.cdn and _type == 'CNAME' and
record.value.endswith('.cdn.cloudflare.net.')):
raise StopIteration
contents_for = getattr(self, '_contents_for_{}'.format(_type))
for content in contents_for(record):
content.update({