From 15ca9b68cbea8c10574536119f725c2187e14da9 Mon Sep 17 00:00:00 2001 From: Ben Vincent Date: Fri, 21 Nov 2025 23:01:54 +1100 Subject: [PATCH] feat: manage externaldns bind - add module to manage externaldns bind for k8s - add infra::dns::externaldns role - add 198.18.19.20 as anycast for k8s external-dns service --- hieradata/country/au/region/syd1.yaml | 2 + hieradata/roles/infra/dns/externaldns.eyaml | 2 + hieradata/roles/infra/dns/externaldns.yaml | 65 +++++++++++++++++++ hieradata/roles/infra/dns/resolver.yaml | 11 ++++ modules/externaldns/manifests/init.pp | 15 +++++ modules/externaldns/manifests/master.pp | 45 +++++++++++++ modules/externaldns/manifests/slave.pp | 36 ++++++++++ site/roles/manifests/infra/dns/externaldns.pp | 11 ++++ 8 files changed, 187 insertions(+) create mode 100644 hieradata/roles/infra/dns/externaldns.eyaml create mode 100644 hieradata/roles/infra/dns/externaldns.yaml create mode 100644 modules/externaldns/manifests/init.pp create mode 100644 modules/externaldns/manifests/master.pp create mode 100644 modules/externaldns/manifests/slave.pp create mode 100644 site/roles/manifests/infra/dns/externaldns.pp diff --git a/hieradata/country/au/region/syd1.yaml b/hieradata/country/au/region/syd1.yaml index 7ecae91..f32bd93 100644 --- a/hieradata/country/au/region/syd1.yaml +++ b/hieradata/country/au/region/syd1.yaml @@ -5,3 +5,5 @@ profiles_dns_upstream_forwarder_unkin: - 198.18.19.15 profiles_dns_upstream_forwarder_consul: - 198.18.19.14 +profiles_dns_upstream_forwarder_k8s: + - 198.18.19.20 diff --git a/hieradata/roles/infra/dns/externaldns.eyaml b/hieradata/roles/infra/dns/externaldns.eyaml new file mode 100644 index 0000000..4181982 --- /dev/null +++ b/hieradata/roles/infra/dns/externaldns.eyaml @@ -0,0 +1,2 @@ +--- +externaldns::externaldns_key_secret: ENC[PKCS7,MIIBmQYJKoZIhvcNAQcDoIIBijCCAYYCAQAxggEhMIIBHQIBADAFMAACAQEwDQYJKoZIhvcNAQEBBQAEggEABqbZiK1NDTU+w2k7orz2HrB0EXwun7hn4pR6TeCHMp2IfrkPxlQT+f1J9c0PqJaAKvnyz+Cx0xNCrlnONqk+J57f48kYKYV+Vw+L0AYHYFj8/TizY5CwLpJS2XKyfRd4iEsWMonvfIYn71t3+YuXm4dkoEqGekW93qCr/KFtjAu0K3e+ypyl4EJqWokiUs7IbcSBNvrjUkP4yR8F/wHVKM1E5yfr+D1+nmMmt7Ob/J+am14492TppE2C7Xadg4us+kdYtuBsv9kTSi1GwwqUDjbeJVmfK3pKHjXdF+PI07AFLzo5bBZTJOzQfQ4SywpH8R5BDQoUCyHiaskB5wrmSDBcBgkqhkiG9w0BBwEwHQYJYIZIAWUDBAEqBBB2LU9ZhefSg9PqqkwnfV65gDBvXuXco0moKCGjHqm5KcojWCK1BoS/+mltlr8kw9grZjN9jxHRLn1FjgBlq418c8w=] diff --git a/hieradata/roles/infra/dns/externaldns.yaml b/hieradata/roles/infra/dns/externaldns.yaml new file mode 100644 index 0000000..6a7aa19 --- /dev/null +++ b/hieradata/roles/infra/dns/externaldns.yaml @@ -0,0 +1,65 @@ +--- +hiera_include: + - externaldns + - frrouting + - exporters::frr_exporter + +externaldns::bind_master_hostname: 'ausyd1nxvm2127.main.unkin.net' +externaldns::k8s_zones: + - 'k8s.syd1.au.unkin.net' + - '200.18.198.in-addr.arpa' +externaldns::slave_servers: + - 'ausyd1nxvm2128.main.unkin.net' + - 'ausyd1nxvm2129.main.unkin.net' +externaldns::externaldns_key_algorithm: 'hmac-sha256' + +# networking +anycast_ip: 198.18.19.20 +systemd::manage_networkd: true +systemd::manage_all_network_files: true +networking::interfaces: + eth0: + type: physical + forwarding: true + dhcp: true + anycast0: + type: dummy + ipaddress: "%{hiera('anycast_ip')}" + netmask: 255.255.255.255 + mtu: 1500 + +# frrouting +exporters::frr_exporter::enable: true +frrouting::ospfd_router_id: "%{facts.networking.ip}" +frrouting::ospfd_redistribute: + - connected +frrouting::ospfd_interfaces: + eth0: + area: 0.0.0.0 + anycast0: + area: 0.0.0.0 +frrouting::daemons: + ospfd: true + +# consul +profiles::consul::client::node_rules: + - resource: service + segment: frr_exporter + disposition: write + +# additional repos +profiles::yum::global::repos: + frr-extras: + name: frr-extras + descr: frr-extras repository + target: /etc/yum.repos.d/frr-extras.repo + baseurl: https://packagerepo.service.consul/frr/el9/extras-daily/%{facts.os.architecture}/os + gpgkey: https://packagerepo.service.consul/frr/el9/extras-daily/%{facts.os.architecture}/os/RPM-GPG-KEY-FRR + mirrorlist: absent + frr-stable: + name: frr-stable + descr: frr-stable repository + target: /etc/yum.repos.d/frr-stable.repo + baseurl: https://packagerepo.service.consul/frr/el9/stable-daily/%{facts.os.architecture}/os + gpgkey: https://packagerepo.service.consul/frr/el9/stable-daily/%{facts.os.architecture}/os/RPM-GPG-KEY-FRR + mirrorlist: absent diff --git a/hieradata/roles/infra/dns/resolver.yaml b/hieradata/roles/infra/dns/resolver.yaml index fc465ae..4549599 100644 --- a/hieradata/roles/infra/dns/resolver.yaml +++ b/hieradata/roles/infra/dns/resolver.yaml @@ -82,6 +82,11 @@ profiles::dns::resolver::zones: - 10.10.16.32 - 10.10.16.33 forward: 'only' + k8s.syd1.au.unkin.net-forward: + domain: 'k8s.syd1.au.unkin.net' + zone_type: 'forward' + forwarders: "%{alias('profiles_dns_upstream_forwarder_k8s')}" + forward: 'only' unkin.net-forward: domain: 'unkin.net' zone_type: 'forward' @@ -172,6 +177,11 @@ profiles::dns::resolver::zones: zone_type: 'forward' forwarders: "%{alias('profiles_dns_upstream_forwarder_unkin')}" forward: 'only' + 200.18.198.in-addr.arpa-forward: + domain: '200.18.198.in-addr.arpa' + zone_type: 'forward' + forwarders: "%{alias('profiles_dns_upstream_forwarder_k8s')}" + forward: 'only' consul-forward: domain: 'consul' zone_type: 'forward' @@ -188,6 +198,7 @@ profiles::dns::resolver::views: - network.unkin.net-forward - prod.unkin.net-forward - consul-forward + - k8s.syd1.au.unkin.net-forward - 13.18.198.in-addr.arpa-forward - 14.18.198.in-addr.arpa-forward - 15.18.198.in-addr.arpa-forward diff --git a/modules/externaldns/manifests/init.pp b/modules/externaldns/manifests/init.pp new file mode 100644 index 0000000..2a08fc7 --- /dev/null +++ b/modules/externaldns/manifests/init.pp @@ -0,0 +1,15 @@ +# ExternalDNS BIND module - automatically configures master or slave +class externaldns ( + Stdlib::Fqdn $bind_master_hostname, + Array[Stdlib::Fqdn] $k8s_zones = [], + Array[Stdlib::Fqdn] $slave_servers = [], + String $externaldns_key_secret = '', + String $externaldns_key_algorithm = 'hmac-sha256', +) { + + if $trusted['certname'] == $bind_master_hostname { + include externaldns::master + } else { + include externaldns::slave + } +} \ No newline at end of file diff --git a/modules/externaldns/manifests/master.pp b/modules/externaldns/manifests/master.pp new file mode 100644 index 0000000..538119c --- /dev/null +++ b/modules/externaldns/manifests/master.pp @@ -0,0 +1,45 @@ +# ExternalDNS BIND master server class +class externaldns::master inherits externaldns { + + include bind + + # Query PuppetDB for slave server IP addresses + $slave_ips = $externaldns::slave_servers.map |$fqdn| { + puppetdb_query("inventory[facts.networking.ip] { certname = '${fqdn}' }")[0]['facts.networking.ip'] + }.filter |$ip| { $ip != undef } + + # Create TSIG key for ExternalDNS authentication + bind::key { 'externaldns-key': + algorithm => $externaldns::externaldns_key_algorithm, + secret => $externaldns::externaldns_key_secret, + } + + # Create ACL for slave servers + if !empty($slave_ips) { + bind::acl { 'dns-slaves': + addresses => $slave_ips, + } + } + + # Create master zones for each Kubernetes domain + $externaldns::k8s_zones.each |$zone| { + bind::zone { $zone: + zone_type => 'master', + dynamic => true, + allow_updates => ['key externaldns-key'], + allow_transfers => empty($slave_ips) ? { + true => [], + false => ['dns-slaves'], + }, + ns_notify => !empty($slave_ips), + also_notify => $slave_ips, + dnssec => false, + } + } + + # Create default view to include the zones + bind::view { 'externaldns': + recursion => false, + zones => $externaldns::k8s_zones, + } +} \ No newline at end of file diff --git a/modules/externaldns/manifests/slave.pp b/modules/externaldns/manifests/slave.pp new file mode 100644 index 0000000..0af7f80 --- /dev/null +++ b/modules/externaldns/manifests/slave.pp @@ -0,0 +1,36 @@ +# ExternalDNS BIND slave server class +class externaldns::slave inherits externaldns { + + include bind + + # Query PuppetDB for master server IP address + $query = "inventory[facts.networking.ip] { certname = '${externaldns::bind_master_hostname}' }" + $master_ip = puppetdb_query($query)[0]['facts.networking.ip'] + + # Create TSIG key for zone transfers (same as master) + bind::key { 'externaldns-key': + algorithm => $externaldns::externaldns_key_algorithm, + secret => $externaldns::externaldns_key_secret, + } + + # Create ACL for master server + bind::acl { 'dns-master': + addresses => [$master_ip], + } + + # Create slave zones for each Kubernetes domain + $externaldns::k8s_zones.each |$zone| { + bind::zone { $zone: + zone_type => 'slave', + masters => [$master_ip], + allow_notify => ['dns-master'], + ns_notify => false, + } + } + + # Create default view to include the zones + bind::view { 'externaldns': + recursion => false, + zones => $externaldns::k8s_zones, + } +} \ No newline at end of file diff --git a/site/roles/manifests/infra/dns/externaldns.pp b/site/roles/manifests/infra/dns/externaldns.pp new file mode 100644 index 0000000..d3b630c --- /dev/null +++ b/site/roles/manifests/infra/dns/externaldns.pp @@ -0,0 +1,11 @@ +# BIND server role for ExternalDNS integration +class roles::infra::dns::externaldns { + if $facts['firstrun'] { + include profiles::defaults + include profiles::firstrun::init + } else { + include profiles::defaults + include profiles::base + include externaldns + } +} \ No newline at end of file