diff --git a/.terraform.lock.hcl b/.terraform.lock.hcl index 874aa6c..e83bfba 100644 --- a/.terraform.lock.hcl +++ b/.terraform.lock.hcl @@ -136,6 +136,24 @@ provider "registry.opentofu.org/hetznercloud/hcloud" { ] } +provider "registry.opentofu.org/kristofferahl/healthchecksio" { + version = "1.6.3" + constraints = "~> 1.6.0" + hashes = [ + "h1:UG8BANZc208Tjw/Byraf+1W7waDCusGmolAxeiGZ9eQ=", + "zh:12cd30c19472bcecbd81f9fb6555f055d8a665477a1b2aadd20a9d43b95df0cf", + "zh:174bbe2e2f49132fb4fb9c2b5abaa5743ba49c29f970daae32b7774c8eb259fd", + "zh:214c06082f93bbd11919f73e5bdded8f916c990a2c2c46103c827e070120a6ea", + "zh:21db5fa48fc61cd88e72d34414063e15d99677d871bdb4d231800c8f7daf6bc1", + "zh:39b8d77b8849b3bdd0167ecd465d1980dd42bc1f3cbb9136c9cf8281d36d446a", + "zh:5b81782d766a41272767b6804520eb8d07e82986f5d90818b3f000b26e4efdf2", + "zh:8c045b836f4e6d86518f12c4b113db43715bad412f7f3cde7afbcc87a21bbfe1", + "zh:9c3c6cedd21298d78c93c8a76d8a144a519664bd9def34c5b4a830f2aeeabe6d", + "zh:9f1e6a1aba1d21b90c6bf4520c4b5b5d2fce20bd988852a6429acbc6365fa151", + "zh:a5d15ed31ac4bc194bc7a32e96e902d7b322ab032c1637c67b16dbee968d1fae", + ] +} + provider "registry.opentofu.org/ovh/ovh" { version = "0.48.0" constraints = "~> 0.48.0" diff --git a/Makefile b/Makefile index 0c635e7..6491fa4 100644 --- a/Makefile +++ b/Makefile @@ -22,12 +22,17 @@ PWD := $(shell pwd) | yq -y '{opentofu: with_entries(.value |= .value)}' \ > ./inventory/group_vars/all/opentofu.yaml -outputs: ./dns/hosts.json ./dns/services.json ./inventory/group_vars/all/opentofu.yaml +output: ./dns/hosts.json ./dns/services.json ./inventory/group_vars/all/opentofu.yaml ./types-dnscontrol.d.ts: dnscontrol write-types +tofu: + tofu apply + echo "\n=====\n" + $(MAKE) output + dns: ./types-dnscontrol.d.ts ./dns/hosts.json ./dns/services.json dnscontrol push diff --git a/inventory/serguzim.net.yml b/inventory/serguzim.net.yml index 563f930..d717402 100644 --- a/inventory/serguzim.net.yml +++ b/inventory/serguzim.net.yml @@ -8,38 +8,28 @@ all: local-dev: ansible_connection: local - node001: - ansible_host: node001.vpn.serguzim.net - ansible_port: "{{ vault_node001.ansible_port }}" - ansible_user: "{{ vault_node001.ansible_user }}" - interactive_user: "{{ vault_node001.interactive_user }}" - host_vpn: - domain: node001.vpn.serguzim.net - ip: 100.64.0.1 - host_backup: - hc_uid: "{{ vault_node001.backup.hc_uid }}" - uptime_kuma_token: "{{ vault_node001.backup.uptime_kuma_token }}" - node002: - ansible_host: node002.vpn.serguzim.net + ansible_host: "{{ opentofu.hosts.node002.fqdn_vpn }}" ansible_port: "{{ vault_node002.ansible_port }}" ansible_user: "{{ vault_node002.ansible_user }}" interactive_user: "{{ vault_node002.interactive_user }}" host_vpn: - domain: node002.vpn.serguzim.net - ip: 100.64.0.2 + domain: "{{ opentofu.hosts.node002.fqdn_vpn }}" + ip: "{{ opentofu.hosts.node002.ipv4_address_vpn }}" host_backup: - hc_uid: "{{ vault_node002.backup.hc_uid }}" + hc_uid: "{{ opentofu.healthchecksio.backup.node002.id }}" + hc_url: "{{ opentofu.healthchecksio.backup.node002.ping_url }}" uptime_kuma_token: "{{ vault_node002.backup.uptime_kuma_token }}" node003: - ansible_host: node003.vpn.serguzim.net + ansible_host: "{{ opentofu.hosts.node003.fqdn_vpn }}" ansible_port: "{{ vault_node003.ansible_port }}" ansible_user: "{{ vault_node003.ansible_user }}" interactive_user: "{{ vault_node003.interactive_user }}" host_vpn: - domain: node003.vpn.serguzim.net - ip: 100.110.16.30 + domain: "{{ opentofu.hosts.node003.fqdn_vpn }}" + ip: "{{ opentofu.hosts.node003.ipv4_address_vpn }}" host_backup: - hc_uid: "{{ vault_node003.backup.hc_uid }}" + hc_uid: "{{ opentofu.healthchecksio.backup.node003.id }}" + hc_url: "{{ opentofu.healthchecksio.backup.node003.ping_url }}" uptime_kuma_token: "{{ vault_node003.backup.uptime_kuma_token }}" diff --git a/main.tf b/main.tf index 5054fd4..8dbc4cb 100644 --- a/main.tf +++ b/main.tf @@ -42,6 +42,8 @@ module "infrastructure" { hcloud_token = var.hcloud_token + healthchecksio_api_key = var.healthchecksio_api_key + ovh_application_key = var.ovh_application_key ovh_application_secret = var.ovh_application_secret ovh_consumer_key = var.ovh_consumer_key diff --git a/modules/infrastructure/healthchecksio.tf b/modules/infrastructure/healthchecksio.tf new file mode 100644 index 0000000..748a1f1 --- /dev/null +++ b/modules/infrastructure/healthchecksio.tf @@ -0,0 +1,32 @@ +data "healthchecksio_channel" "email" { + kind = "email" +} + +data "healthchecksio_channel" "signal" { + kind = "signal" +} + +data "healthchecksio_channel" "ntfy" { + kind = "ntfy" +} + +resource "healthchecksio_check" "backup" { + for_each = var.hosts + + name = "backup@${each.value.hostname}" + desc = "A check for the backup on ${each.value.hostname}" + + tags = [ + "backup", + each.value.hostname, + ] + + channels = [ + data.healthchecksio_channel.email.id, + data.healthchecksio_channel.signal.id, + data.healthchecksio_channel.ntfy.id, + ] + + timeout = 86400 + grace = 1800 +} diff --git a/modules/infrastructure/main.tf b/modules/infrastructure/main.tf index e34ce79..a184c5a 100644 --- a/modules/infrastructure/main.tf +++ b/modules/infrastructure/main.tf @@ -8,6 +8,10 @@ terraform { source = "hetznercloud/hcloud" version = "~> 1.45.0" } + healthchecksio = { + source = "kristofferahl/healthchecksio" + version = "~> 1.6.0" + } ovh = { source = "ovh/ovh" version = "~> 0.48.0" @@ -34,6 +38,10 @@ provider "hcloud" { token = var.hcloud_token } +provider "healthchecksio" { + api_key = var.healthchecksio_api_key +} + provider "ovh" { endpoint = "ovh-eu" application_key = var.ovh_application_key diff --git a/modules/infrastructure/output.tf b/modules/infrastructure/output.tf index 8e3d2b1..075f7e5 100644 --- a/modules/infrastructure/output.tf +++ b/modules/infrastructure/output.tf @@ -1,17 +1,38 @@ output "hosts" { value = { - for subdomain in distinct([for record in ovh_domain_zone_record.server_records : record.subdomain]) : - subdomain => { - "hostname" = subdomain - "fqdn" = "${subdomain}.${ovh_domain_zone_record.server_records["${subdomain}:ipv4"].zone}" + for key, host in var.hosts : + key => { + "hostname" = host.hostname + "fqdn" = "${host.hostname}.serguzim.net" + "fqdn_vpn" = "${host.hostname}.vpn.serguzim.net" "ipv4_address" = try( - ovh_domain_zone_record.server_records["${subdomain}:ipv4"].target, + local.server_addresses_separated["${key}:ipv4"].address, null ) "ipv6_address" = try( - ovh_domain_zone_record.server_records["${subdomain}:ipv6"].target, + local.server_addresses_separated["${key}:ipv6"].address, null ) + + ipv4_address_vpn = try( + local.tailscale_host_addresses_separated["${key}:ipv4"].address, + null + ) + ipv6_address_vpn = try( + local.tailscale_host_addresses_separated["${key}:ipv6"].address, + null + ) + } + } +} + +output "healthchecksio" { + value = { + backup = { + for key, check in healthchecksio_check.backup : key => { + "id" = check.id + "ping_url" = check.ping_url + } } } } diff --git a/modules/infrastructure/ovh.tf b/modules/infrastructure/ovh.tf index ffc11ef..ff2957d 100644 --- a/modules/infrastructure/ovh.tf +++ b/modules/infrastructure/ovh.tf @@ -1,8 +1,9 @@ locals { server_addresses = flatten([ [ - for host in contabo_instance.nodes : [ + for key, host in contabo_instance.nodes : [ { + key = key hostname = host.display_name ipv4_address = host.ip_config[0].v4[0].ip ipv6_address = host.ip_config[0].v6[0].ip @@ -10,8 +11,9 @@ locals { ] ], [ - for host in hcloud_server.nodes : [ + for key, host in hcloud_server.nodes : [ { + key = key hostname = host.name ipv4_address = host.ipv4_address ipv6_address = host.ipv6_address @@ -20,34 +22,32 @@ locals { ] ]) - server_addresses_separated = flatten([ - for host in local.server_addresses : [ - { + server_addresses_separated = merge([ + for host in local.server_addresses : { + "${host.key}:ipv4" = { hostname = host.hostname - key = "${host.hostname}:ipv4" address = host.ipv4_address }, - { + "${host.key}:ipv6" = { hostname = host.hostname - key = "${host.hostname}:ipv6" address = host.ipv6_address }, - ] - ]) + } + ]...) - tailscale_host_addresses = flatten([ - for host in data.tailscale_devices.nodes.devices : [ - for index, address in host.addresses : { - hostname = host.hostname - key = "${host.hostname}:${index}" - address = address - } - ] - ]) + tailscale_host_addresses_separated = merge([ + for host in data.tailscale_devices.nodes.devices : { + for address in host.addresses : + "${host.hostname}:${strcontains(address, ":") ? "ipv6" : "ipv4"}" => { + hostname = host.hostname + address = address + } + } + ]...) } resource "ovh_domain_zone_record" "server_records" { - for_each = { for entry in local.server_addresses_separated: entry.key => entry } + for_each = local.server_addresses_separated zone = "serguzim.net" subdomain = each.value.hostname fieldtype = strcontains(each.value.address, ":") ? "AAAA" : "A" @@ -56,7 +56,7 @@ resource "ovh_domain_zone_record" "server_records" { } resource "ovh_domain_zone_record" "tailscale_vpn" { - for_each = { for entry in local.tailscale_host_addresses: entry.key => entry } + for_each = local.tailscale_host_addresses_separated zone = "serguzim.net" subdomain = "${each.value.hostname}.vpn" fieldtype = strcontains(each.value.address, ":") ? "AAAA" : "A" diff --git a/modules/infrastructure/tailscale.tf b/modules/infrastructure/tailscale.tf index a9250bd..64debee 100644 --- a/modules/infrastructure/tailscale.tf +++ b/modules/infrastructure/tailscale.tf @@ -9,3 +9,9 @@ resource "tailscale_tailnet_key" "cloud_init_key" { data "tailscale_devices" "nodes" { name_prefix = "node" } + +locals { + tailscale_devices = { + for host in data.tailscale_devices.nodes.devices : host.hostname => host + } +} diff --git a/modules/infrastructure/variables.tf b/modules/infrastructure/variables.tf index 7728ad6..9cc6dbc 100644 --- a/modules/infrastructure/variables.tf +++ b/modules/infrastructure/variables.tf @@ -20,6 +20,11 @@ variable "hcloud_token" { } +variable "healthchecksio_api_key" { + sensitive = true +} + + variable "ovh_application_key" { sensitive = true } diff --git a/output.tf b/output.tf index 03c8cae..a99a181 100644 --- a/output.tf +++ b/output.tf @@ -7,6 +7,10 @@ output "authentik_data" { sensitive = true } +output "healthchecksio" { + value = module.infrastructure.healthchecksio +} + output "postgresql_data" { value = module.services.postgresql_data sensitive = true diff --git a/roles/backup/vars/main.yml b/roles/backup/vars/main.yml index aa554d9..06dd8a6 100644 --- a/roles/backup/vars/main.yml +++ b/roles/backup/vars/main.yml @@ -13,7 +13,7 @@ backup_msg_success: "Backup successful" backup_curl_base: 'curl -L -m 10 --retry 5' backup_hc_curl_base: '{{ backup_curl_base }} -X POST -H "Content-Type: text/plain"' backup_uk_curl_base: '{{ backup_curl_base }}' -backup_hc_url: 'https://hc-ping.com/{{ host_backup.hc_uid }}' +backup_hc_url: '{{ host_backup.hc_url }}' backup_uk_url: 'https://status.serguzim.me/api/push/{{ host_backup.uptime_kuma_token }}' backup_hc_command_start: '{{ backup_hc_curl_base }} --data "{{ backup_msg_start }}" {{ backup_hc_url }}/start' diff --git a/secrets.auto.tfvars.example b/secrets.auto.tfvars.example index 41ee2b5..8e4fbe3 100644 --- a/secrets.auto.tfvars.example +++ b/secrets.auto.tfvars.example @@ -14,6 +14,8 @@ contabo_pass = "" hcloud_token = "" +healthchecksio_api_key = "" + ovh_application_key = "" ovh_application_secret = "" ovh_consumer_key = "" diff --git a/variables.tf b/variables.tf index b1908fc..6e87e35 100644 --- a/variables.tf +++ b/variables.tf @@ -50,6 +50,11 @@ variable "hcloud_token" { } +variable "healthchecksio_api_key" { + sensitive = true +} + + variable "ovh_application_key" { sensitive = true }