Skip to content

Commit

Permalink
DNS replacements, record metadata
Browse files Browse the repository at this point in the history
Broken USTAR link
"exposed master" DNS cluster variation
  • Loading branch information
msaladna committed Sep 3, 2021
1 parent 0a6fbe5 commit 9345b8f
Show file tree
Hide file tree
Showing 4 changed files with 243 additions and 32 deletions.
143 changes: 141 additions & 2 deletions docs/admin/DNS.md
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,7 @@ Or more succinctly to reapply the DMARC record across all domains after updating
'rr' => 'TXT',
'parameter' => ''
]));

(new \Opcenter\Dns\Bulk())->add(new \Opcenter\Dns\Record('_dummy_zone.com', [
'name' => '_dmarc',
'rr' => 'TXT',
Expand All @@ -173,7 +173,7 @@ A second parameter, `$where`, is a closure to test whether to apply the record f
<?php
include __DIR__ . '/lib/CLI/cmd.php';

(new \Opcenter\Dns\Bulk())->remove(new \Opcenter\Dns\Record('_dummy_zone.com', [
(new \Opcenter\Dns\Bulk)->remove(new \Opcenter\Dns\Record('_dummy_zone.com', [
'name' => '_dmarc',
'rr' => 'TXT',
'parameter' => ''
Expand All @@ -183,6 +183,145 @@ A second parameter, `$where`, is a closure to test whether to apply the record f
});
```

### Replacements

Previously record modifications were done using two separate tasks. This can create problems if a record isn't removed or if it doesn't exist in the first place. A third operation, `replace()` calls `dns:modify-record()` to make the operation atomic.

```php
<?php
include __DIR__ . '/lib/CLI/cmd.php';

(new \Opcenter\Dns\Bulk)->replace(
// replace A records named "test"
new \Opcenter\Dns\Record('_dummy_zone.com', [
'name' => 'test',
'rr' => 'A'
]),
// with the IP address 1.2.3.4
new \Opcenter\Dns\Record(
'_dummy_zone.com', [
'parameter' => '1.2.3.4',
'rr' => 'A'
])
);
```

::: tip Always specify rr
During record canonicalization, the `rr` attribute must be present. This helps ApisCP determine what type of record it is and what treatment *if any* is necessary to standardize it.
:::

#### Replacing metadata

Many records contain more than just a single parameter. For example, an `MX` record is comprised of a numeric **priority** and target **hostname**. `Record` objects can be altered by specifying their parsed attributes rather than relying on less clear space-delimited placement.

The following example updates both the `rname` and `ttl` [fields](https://en.wikipedia.org/wiki/SOA_record) for all domains that utilize the "[powerdns](dns/PowerDNS.md)" DNS driver. An explicit `false` return in the closure skips modifying any record matched from the first parameter.

```php
<?php
include __DIR__ . '/lib/CLI/cmd.php';
$handler = new \Opcenter\Dns\Bulk();

// empty all NS records on the apex
// "_dummy_zone.com" has no effect, but used for completeness with the API
$handler->replace(new \Opcenter\Dns\Record('_dummy_zone.com', [
'name' => '',
'rr' => 'SOA'
]), function (\apnscpFunctionInterceptor $afi, \Opcenter\Dns\Record $r) {
// update "rname" parameter
$r->setMeta('rname', 'ns1.mydomain.com');
// update negative cache TTL
$r->setMeta('ttl', 300);

// likewise we can statically set the parameter as such
// $r['parameter'] = 'ns1.mydomain.com. noc.mydomain.com. 2021090229 3600 1800 604800 300';

// return false to skip processing the record`
return $afi->dns_get_provider() === 'powerdns';
});
```

#### Record metadata

| Resource record | Metadata | Example |
| --------------- | -------------- | ------------------------------------------------------------ |
| **CAA** | | 128 issue "letsencrypt.org" |
| | flags | 128 |
| | tag | issue |
| | data | letsencrypt.org |
| **CERT** | | IPGP 0 0 1457446EFDE098E5C934B69C7DC208ADDE26C2B797 |
| | type | IPGP |
| | key_tag | 0 |
| | algorithm | 0 |
| | data | 1457446EFDE098E5C934B69C7DC208ADDE26C2B797 |
| **DNSKEY** | | 257 3 5 2018hiZsq1jkCS3osdcAksvcd3oSC0f43OI= |
| | flags | 257 |
| | protocol | 3 |
| | algorithm | 5 |
| | data | 2018hiZsq1jkCS3osdcAksvcd3oSC0f43OI= |
| **DS** | | 25924 5 1 0AC4F2E44C582AE809208098F7BE2C44AB947DCC |
| | key_tag | 25924 |
| | algorithm | 5 |
| | digest_type | 1 |
| | data | 0AC4F2E44C582AE809208098F7BE2C44AB947DCC |
| **LOC** | | 33 46 23.6424 N 84 23 42.59 W 293m 0.00m 10000m 10m |
| | lat_degrees | 33 |
| | lat_minutes | 46 |
| | lat_seconds | 23.6524 |
| | lat_direction | N |
| | long_degrees | 84 |
| | long_minutes | 23 |
| | long_seconds | 42.59 |
| | long_direction | W |
| | altitude | 293m |
| | size | 0.00m |
| | precision_horz | 10000m |
| | precision_vert | 10m |
| **MX** | | 10 mail.example.com |
| | priority | 10 |
| | data | mail.example.com |
| **NAPTR** | | 100 10 "U" "E2U+sip" "!^.\*$!sip:customer-service@example.com!" . |
| | order | 100 |
| | preference | 10 |
| | flags | U |
| | service | E2U+sip |
| | regex | !^.\*$!sip:customer-service@example.com! |
| | data | . |
| **SMIMEA** | | 3 0 0 3082036E30820...BE14DA |
| | usage | 3 |
| | selector | 0 |
| | matching_type | 0 |
| | data | 3082036E30820...BE14DA |
| **SOA** | | master.example.com. hostmater.example.com. 1 3600 1800 86400 600 |
| | mname | master.example.com |
| | rname | hostmaster.example.com |
| | serial | 1 |
| | refresh | 3600 |
| | retry | 1800 |
| | expire | 86400 |
| | ttl | 600 |
| **SRV** | | 10 50 5611 my.jabberserver.com |
| | service | *embedded in hostname* |
| | protocol | *embedded in hostname* |
| | name | *embedded in hostname* |
| | priority | 10 |
| | weight | 50 |
| | port | 5611 |
| | data | my.jabberserver.com |
| **SSHFP** | | 1 1 0ac4f2e44c582ae809208098f7be2c44ab947dcc |
| | algorithm | 1 |
| | type | 1 |
| | data | 0ac4f2e44c582ae809208098f7be2c44ab947dcc |
| **TLSA** | | 3 1 1 6343fbfe4ab1...dd14467ee0a7ab70d |
| | usage | 3 |
| | selector | 1 |
| | matching_type | 1 |
| | data | 6343fbfe4ab1...dd14467ee0a7ab70d |
| **URI** | | 10 1 "ftp://ftp1.example.com/public" |
| | priority | 10 |
| | weight | 1 |
| | data | ftp://ftp1.example.com/public |


## Development

### Custom modules
Expand Down
2 changes: 1 addition & 1 deletion docs/admin/Migrations - cPanel.md
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,7 @@ These changes will be reflected on future imports.

### Decompression oddities

Migration will attempt to use PHP's PharData handler to decompress files. It's based on USTAR, which has [limitations](https://www.gnu.org/software/tar/manual/html_chapter/tar_8.html) that may result in a cPanel backup generated in POSIX.1-2001 standards to fail. Use `--no-builtin` to disable the builtin handler from attempting to read the backup.
Migration will attempt to use PHP's PharData handler to decompress files. It's based on USTAR, which has [limitations](https://www.gnu.org/software/tar/manual/html_node/Formats.html#Formats) that may result in a cPanel backup generated in POSIX.1-2001 standards to fail. Use `--no-builtin` to disable the builtin handler from attempting to read the backup.

### Empty MySQL credentials

Expand Down
127 changes: 98 additions & 29 deletions docs/admin/dns/PowerDNS.md
Original file line number Diff line number Diff line change
Expand Up @@ -122,46 +122,115 @@ Bootstrapper will avoid overwriting certain configurations unless explicitly ask

Be sure to skip down to the [Remote API access](#remote-api-access) section to configure the hidden master endpoint.

#### Exposed master

In the above, a hidden master is used which obscures the server that handles updates. If we promote **primary nameserver** to master, which removes a hidden master setup, then the diagram changes slightly.

![AXFR cluster layout](./powerdns-axfr-exposed-cluster.svg)

On the **primary nameserver** NS1, *assuming 1.2.3.5 is a slave nameserver with the nameserver names ns1.domain.com and ns2.domain.com respectively*, add the following configuration:

```bash
cpcmd scope:set cp.bootstrapper powerdns_enabled true
cpcmd scope:set cp.bootstrapper powerdns_zone_type master
cpcmd scope:set cp.bootstrapper powerdns_custom_config '["allow-axfr-ips":"1.2.3.5","also-notify":"1.2.3.5","master":"yes"]'
cpcmd scope:set cp.bootstrapper powerdns_webserver_enable true
cpcmd scope:set cp.bootstrapper powerdns_nameservers '[ns1.domain.com,ns2.domain.com]'
env BSARGS="--extra-vars=force=yes" upcp -sb software/powerdns
```

On the **slave(s)**, *assuming the master is 1.2.3.4 with the hostname ns1.domain.com*, add the following configuration:

```bash
cpcmd scope:set cp.bootstrapper powerdns_enabled true
cpcmd scope:set cp.bootstrapper powerdns_zone_type slave
cpcmd scope:set cp.bootstrapper powerdns_custom_config '["allow-notify-from":"1.2.3.4","slave":"yes","superslave":"yes"]'
cpcmd scope:set cp.bootstrapper powerdns_webserver_enable false
cpcmd scope:set cp.bootstrapper powerdns_nameservers '[ns1.domain.com,ns2.domain.com]'
cpcmd scope:set cp.bootstrapper powerdns_supermaster '[ip:1.2.3.4,nameserver:ns1.domain.com,account:master]'
cpcmd scope:set cp.bootstrapper powerdns_api_uri 'https://ns1.domain.com/dns/api/v1'
env BSARGS="--extra-vars=force=yes" upcp -sb software/powerdns
```

Lastly, on the **hosting nodes**, *assuming all DNS zone traffic is sent to primary nameserver at ns1.domain.com (IP address 1.2.3.4) with the API key from `/etc/pdns/pdns.conf` of `abc1234`*, configure each to use the same API key and endpoint discussed below.

```bash
cpcmd scope:set cp.bootstrapper powerdns_api_uri 'https://ns1.domain.com/dns/api/v1'
cpcmd scope:set cp.bootstrapper powerdns_nameservers '[ns1.domain.com,ns2.domain.com]'
cpcmd scope:set cp.bootstrapper powerdns_api_key 'abc1234'
cpcmd scope:set cp.bootstrapper powerdns_zone_type 'master'
env BSARGS="--extra-vars=force=yes" upcp -sb software/powerdns
```

#### Updating NS records

PowerDNS is configured by default to use "127.0.0.1" for its NS records. In a working environment this is never the right option, but helps bridge learning. [Bulk update](../DNS.md#bulk-record-management) can be used to clear all NS records on the apex, then provision NS records as set via `powerdns_nameservers`.

Assuming your new DNS records are `ns1.yourserver.com` and `ns2.yourserver.com`, the following suffices:

```php
include __DIR__ . '/lib/CLI/cmd.php';

$handler = new \Opcenter\Dns\Bulk();

// empty all NS records on the apex
// "_dummy_zone.com" has no effect, but used for completeness with the API
$handler->remove(new \Opcenter\Dns\Record('_dummy_zone.com', [
'name' => '',
'rr' => 'NS',
'parameter' => ''
]), function (\apnscpFunctionInterceptor $afi, \Opcenter\Dns\Record $r) {
return $afi->dns_get_provider() === 'powerdns';
});

$handler->add(new \Opcenter\Dns\Record('_dummy_zone.com', [
'name' => '',
'rr' => 'NS',
'parameter' => 'ns1.yourserver.com'
]), function (\apnscpFunctionInterceptor $afi, \Opcenter\Dns\Record $r) {
return $afi->dns_get_provider() === 'powerdns';
});

$handler->add(new \Opcenter\Dns\Record('_dummy_zone.com', [
'name' => '',
'rr' => 'NS',
'parameter' => 'ns2.yourserver.com'
]), function (\apnscpFunctionInterceptor $afi, \Opcenter\Dns\Record $r) {
return $afi->dns_get_provider() === 'powerdns';
});
include __DIR__ . '/lib/CLI/cmd.php';

$handler = new \Opcenter\Dns\Bulk();

// empty all NS records on the apex
// "_dummy_zone.com" has no effect, but used for completeness with the API
$handler->remove(new \Opcenter\Dns\Record('_dummy_zone.com', [
'name' => '',
'rr' => 'NS',
'parameter' => ''
]), function (\apnscpFunctionInterceptor $afi, \Opcenter\Dns\Record $r) {
return $afi->dns_get_provider() === 'powerdns';
});

$handler->add(new \Opcenter\Dns\Record('_dummy_zone.com', [
'name' => '',
'rr' => 'NS',
'parameter' => 'ns1.yourserver.com'
]), function (\apnscpFunctionInterceptor $afi, \Opcenter\Dns\Record $r) {
return $afi->dns_get_provider() === 'powerdns';
});

$handler->add(new \Opcenter\Dns\Record('_dummy_zone.com', [
'name' => '',
'rr' => 'NS',
'parameter' => 'ns2.yourserver.com'
]), function (\apnscpFunctionInterceptor $afi, \Opcenter\Dns\Record $r) {
return $afi->dns_get_provider() === 'powerdns';
});
```

Save the script in `/usr/local/apnscp/update.php` and run `env DEBUG=1 apnscp_php /usr/local/apnscp/update.php` to replace all NS records for domains that use PowerDNS.

#### Updating SOA records
**New in 3.2.26**

[SOA](https://en.wikipedia.org/wiki/SOA_record) controls several important replication attributes of a DNS zone. Changing primary nameservers or modifying zone defaults would necessitate updating SOA records. This can be done in a similar fashion using `Opcenter\Dns\Bulk`. For convenience `replace()` can accept a `Record` object or closure, which will be called with the matching record. If a closure is used, the closure may return `false` to skip modifying the record otherwise the second parameter, a `Record` object, is used as replacement.

It is the responsibility of the closure to modify the `Record` resource;

```php
include __DIR__ . '/lib/CLI/cmd.php';
$handler = new \Opcenter\Dns\Bulk();

// PowerDNS doesn't let us delete the record; only replace() works
// "_dummy_zone.com" has no effect, but used for completeness with the API
$handler->replace(new \Opcenter\Dns\Record('_dummy_zone.com', [
'name' => '',
'rr' => 'SOA'
]), function (\apnscpFunctionInterceptor $afi, \Opcenter\Dns\Record $r) {
// update "rname" parameter
$r->setMeta('rname', 'ns1.foobar.com');
// update negative cache TTL
$r->setMeta('ttl', 300);

// return false to skip processing the record`
return $afi->dns_get_provider() === 'powerdns';
});
```

More examples are available in [DNS.md](../DNS.md#bulk-record-management).

#### Periodic maintenance

Sometimes you may want to force a zone update - if changing public nameservers - or prune expired domains since AXFR-based clusters do not afford automated zone removals. These snippets come from [hopefully.online](https://hopefully.online/powerdns-master-slave-cluster):
Expand Down
3 changes: 3 additions & 0 deletions docs/admin/dns/powerdns-axfr-exposed-cluster.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

0 comments on commit 9345b8f

Please sign in to comment.