feat(powerdns-zone): add ansible role for creating an empty zone in powerdns

This commit is contained in:
Johanna Dorothea Reichmann 2021-12-21 13:03:59 +01:00
parent 6ad5ddce32
commit e36b29c3af
No known key found for this signature in database
GPG key ID: 03624C433676E465
3 changed files with 129 additions and 0 deletions

View file

@ -0,0 +1,32 @@
# `famedly.dns.powerdns-zone` ansible role
This role aims to bootstrap an empty zone into powerdns so that it can be
used immediately without needing to be created using powerdns-admin or
API calls.
For this, the contents of the SOA record, the zone name and it's type
need to be known, optionally the associated powerdns account.
## Configuration
A sample configuration is given below for setting up `demo.famedly.de`:
```yaml
- hosts: [dns_authorative]
roles:
- name: powerdns-zone
vars:
powerdns_zone_name: demo.famedly.de
powerdns_zone_type: "{{ 'MASTER' if 'dns_primary' in group_names else 'SLAVE' }}"
# Assuming the primary has a variable called `ipv4`, store that ip in the
# `master` column of the domain table of the secondaries
powerdns_zone_master_ip: "{{ groups.dns_primary[0].ipv4 }}"
powerdns_zone_account: famedlydemo
powerdns_zone_soa_content: "ns0.famedly.de admin.famedly.de 2022010101 10800 3600 604800 3600"
# Database configuration
postgres_zone_database_type: postgres
postgres_zone_database_user: pdns
postgres_zone_database_password: asdoifjqwiejüojsdvinoöioeawjsf
postgres_zone_database_name: pdns
postgres_zone_database_host: 127.0.0.1
```

View file

@ -0,0 +1,16 @@
---
powerdns_zone_name: ~
powerdns_zone_type: ~
powerdns_zone_account: ~
powerdns_zone_metadata:
SOA-EDIT-API: DEFAULT
ALLOW-AXFR-FROM: AUTO-NS
powerdns_zone_master_ip: ~
powerdns_zone_soa_content: ~
powerdns_zone_database_type: postgres
powerdns_zone_database_user: pdns
powerdns_zone_database_password: ~
powerdns_zone_database_name: pdns
powerdns_zone_database_host: ~

View file

@ -0,0 +1,81 @@
---
- name: Configure zone '{{ powerdns_zone_name }}'
postgresql_query:
login_host: "{{ powerdns_zone_database_host }}"
login_user: "{{ powerdns_zone_database_user }}"
login_password: "{{ powerdns_zone_database_password }}"
db: "{{ powerdns_zone_database_name }}"
query: >-
INSERT INTO domains (name, {{ 'master,' if powerdns_zone_master_ip else '' }} type, account)
VALUES ( '{{ powerdns_zone_name }}', {{ "'" + powerdns_zone_master_ip + "'," if powerdns_zone_master_ip else '' }} '{{ powerdns_zone_type }}', '{{ powerdns_zone_account }}' )
ON CONFLICT ( name )
DO UPDATE SET "type" = '{{ powerdns_zone_type }}', "account" = '{{ powerdns_zone_account }}'
WHERE domains.name = '{{ powerdns_zone_name }}' and
(domains.type != '{{ powerdns_zone_type }}'
or domains.account != '{{ powerdns_zone_account }}');
SELECT * FROM domains WHERE name = '{{ powerdns_zone_name }}';
when: powerdns_zone_database_type == 'postgres'
register: zone_upsert_result
- name: Extract internal domain_id for zone
set_fact:
zone_domain_id: "{{ zone_upsert_result.query_result[0].id }}"
# TODO: this breaks encoding arrays, which are encoded by
# using multiple (domain_id, kind) entries with different values
- name: Create unique index (domain_id, kind) on domainmetadata table
postgresql_idx:
login_host: "{{ powerdns_zone_database_host }}"
login_user: "{{ powerdns_zone_database_user }}"
login_password: "{{ powerdns_zone_database_password }}"
db: "{{ powerdns_zone_database_name }}"
table: domainmetadata
columns:
- domain_id
- kind
name: domainmetadata_domain_kind_uniq
type: btree
unique: yes
when: powerdns_zone_database_type == 'postgres'
- name: Configure zone metadata for '{{ powerdns_zone_name }}'
postgresql_query:
login_host: "{{ powerdns_zone_database_host }}"
login_user: "{{ powerdns_zone_database_user }}"
login_password: "{{ powerdns_zone_database_password }}"
db: "{{ powerdns_zone_database_name }}"
query: >-
INSERT INTO domainmetadata (domain_id, kind, content)
VALUES ( '{{ zone_domain_id }}', '{{ item.key }}', '{{ item.value }}' )
ON CONFLICT ( domain_id, kind )
DO UPDATE SET "content" = '{{ item.value }}'
WHERE domainmetadata.domain_id = '{{ zone_domain_id }}' and domainmetadata.kind = '{{ item.key }}' and domainmetadata.content != '{{ item.value }}';
loop: "{{ powerdns_zone_metadata | dict2items }}"
loop_control: { label: "{{ item.key }} = {{ item.value }}" }
when: powerdns_zone_database_type == 'postgres'
- name: Check if SOA record for zone '{{ powerdns_zone_name }}' exists
postgresql_query:
login_host: "{{ powerdns_zone_database_host }}"
login_user: "{{ powerdns_zone_database_user }}"
login_password: "{{ powerdns_zone_database_password }}"
db: "{{ powerdns_zone_database_name }}"
query: >-
SELECT * FROM records
WHERE records.domain_id = '{{ zone_domain_id }}'
and records.name = '{{ powerdns_zone_name }}'
and records.type = 'SOA';
changed_when: false
register: zone_records_type_soa
- name: Configure SOA record for zone '{{ powerdns_zone_name }}'
postgresql_query:
login_host: "{{ powerdns_zone_database_host }}"
login_user: "{{ powerdns_zone_database_user }}"
login_password: "{{ powerdns_zone_database_password }}"
db: "{{ powerdns_zone_database_name }}"
query: >-
INSERT INTO records (domain_id, name, type, content, ttl, prio, disabled, auth)
VALUES ( '{{ zone_domain_id }}', '{{ powerdns_zone_name }}', 'SOA', '{{ powerdns_zone_soa_content }}', 3600, 0, false, true)
when: powerdns_zone_database_type == 'postgres' and zone_records_type_soa.rowcount|int == 0