88fcb97ad1
Replaces the exported-resources → puppet DNS master zone-file flow with per-host RFC2136 dynamic updates against the k8s **bind-authoritative** write endpoint (198.18.200.9). The master no longer manages zone files. ## Design Each node assembles its DNS records into a local concat file; a systemd `.path` unit watches it and runs `dns-update` (nsupdate) on change — exactly the watch-a-file model requested. ## Changes - **profiles::dns::updater** (new): concat records file + TSIG key file + `dns-update` script + `dns-update.service` (oneshot) + `dns-update.path` (watcher). The script sends only the delta since last run and deletes removed records, grouped per zone. - **profiles::dns::record**: writes a local concat fragment (`zone|name|type|ttl|value`) instead of exporting `@@concat::fragment` to the master. - **profiles::dns::base**: includes `profiles::dns::updater` (all nodes). - **hiera**: `profiles::dns::updater` server/key_name/algorithm in common.yaml. ## Inert until keyed The updater does nothing until `profiles::dns::updater::key_secret` (TSIG) is set in eyaml — records are assembled but not applied, so nodes are safe before the key exists. ## Prerequisites (k8s side, separate) 1. The `bind-authoritative` zones must set `dynamicUpdate: true` + an `updateKeyRef` (a client-update BindTSIGKey) so they accept these updates. 2. The TSIG key must be shared: the operator-generated key value goes into eyaml here (or the planned Vault-sync feature bridges it). ## Validated puppet parser/epp validate, puppet-lint, and a functional test of the generated per-zone nsupdate message (replace + delete-removed). Reviewed-on: #475 Co-authored-by: Ben Vincent <ben@unkin.net> Co-committed-by: Ben Vincent <ben@unkin.net>
90 lines
2.7 KiB
Puppet
90 lines
2.7 KiB
Puppet
# profiles::dns::base
|
|
class profiles::dns::base (
|
|
Array $search = [],
|
|
Array $nameservers = ['198.18.13.12', '198.18.13.13'],
|
|
Optional[Enum[
|
|
'all',
|
|
'region',
|
|
'country'
|
|
]] $use_ns = undef,
|
|
String $primary_interface = $facts['networking']['primary'],
|
|
Optional[String] $ns_role = undef,
|
|
){
|
|
|
|
# install bind_utils (provides nsupdate)
|
|
include bind::updater
|
|
|
|
# assemble the host's DNS records and nsupdate them to the authoritative server
|
|
include profiles::dns::updater
|
|
|
|
# if ns_role is set, find all hosts matching that enc_role
|
|
$nameserver_array = $ns_role ? {
|
|
undef => $nameservers,
|
|
default => $use_ns ? {
|
|
'all' => puppetdb_query(
|
|
"facts[certname,value] {
|
|
name = 'networking' and
|
|
certname in nodes[certname] { facts.enc_role = '${ns_role}' }
|
|
}"
|
|
).map |$fact| { $fact['value']['ip'] },
|
|
'region' => puppetdb_query(
|
|
"facts[certname,value] {
|
|
name = 'networking' and
|
|
certname in nodes[certname] {
|
|
facts.enc_role = '${ns_role}' and facts.region = '${facts['region']}'
|
|
}
|
|
}"
|
|
).map |$fact| { $fact['value']['ip'] },
|
|
'country' => puppetdb_query(
|
|
"facts[certname,value] {
|
|
name = 'networking' and
|
|
certname in nodes[certname] {
|
|
facts.enc_role = '${ns_role}' and facts.country = '${facts['country']}'
|
|
}
|
|
}"
|
|
).map |$fact| { $fact['value']['ip'] },
|
|
}
|
|
}
|
|
|
|
# if nameservers not returned from puppetdb, use default
|
|
$use_nameservers = empty($nameserver_array) ? {
|
|
true => $nameservers,
|
|
false => $nameserver_array,
|
|
}
|
|
|
|
# if search is undef, fallback to domainname from facts
|
|
if $search == [] {
|
|
$search_array = [$::facts['networking']['domain']]
|
|
}else{
|
|
$search_array = $search
|
|
}
|
|
|
|
# include resolvconf class
|
|
class { 'profiles::dns::resolvconf':
|
|
nameservers => sort($use_nameservers),
|
|
search_domains => sort($search_array),
|
|
}
|
|
|
|
# export dns records for client
|
|
$facts['networking']['interfaces'].each | $interface, $data | {
|
|
|
|
# exclude those without ipv4 address, lo, docker0 and anycast addresses
|
|
if $data['ip'] and $interface != 'lo' and $interface != 'docker0' and $interface !~ /^anycast[0-9]$/ and $interface !~ /^cilium_/ {
|
|
|
|
# use defaults for the primary_interface
|
|
if $interface == $primary_interface {
|
|
profiles::dns::client {"${facts['networking']['fqdn']}-${interface}":
|
|
interface => $interface,
|
|
}
|
|
|
|
# update secondary interfaces
|
|
}else{
|
|
profiles::dns::client {"${facts['networking']['fqdn']}-${interface}":
|
|
interface => $interface,
|
|
hostname => "${facts['networking']['hostname']}-${interface}",
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|