This commit is contained in:
Florian Maury 2025-01-10 16:10:39 +01:00
parent 0dd3b5bdfe
commit 1b9eeb1288
35 changed files with 1274 additions and 0 deletions

33
main.tf
View file

@ -57,3 +57,36 @@ module "poc" {
} }
admin_ssh_public_key = var.ssh_public_key_admin_netboot_server admin_ssh_public_key = var.ssh_public_key_admin_netboot_server
} }
locals {
castopod_domain = "pod.broken-by-design.fr"
castopod_upstream_port = 8000
}
module "castopod_config" {
source = "./modules/castopod"
base_url = "https://pod.broken-by-design.fr/"
castopod_domain = local.castopod_domain
castopod_upstream_port = local.castopod_upstream_port
ssh_authorized_keys = [
file("/var/home/fmaury/.ssh/fma_ovh_rise2.pub")
]
}
module "caddy_config" {
source = "./modules/caddy_reverse"
vhosts = [
{
domain = local.castopod_domain
upstreams = [
"10.109.0.13:${local.castopod_upstream_port}"
]
}
]
ssh_authorized_keys = [
file("/var/home/fmaury/.ssh/fma_ovh_rise2.pub")
]
}

View file

@ -0,0 +1,60 @@
terraform {
required_providers {
proxmox = {
source = "bpg/proxmox"
version = "~>0.56.1"
}
ignition = {
source = "community-terraform-providers/ignition"
version = "2.3.4"
}
}
required_version = ">=1.6.2"
}
data "ignition_disk" "data" {
device = "/dev/disk/by-path/0000:00:0b.0"
partition {
label = "caddy_config"
number = 0
sizemib = 100
startmib = 0
type_guid = "0FC63DAF-8483-4772-8E79-3D69D8477DE4"
}
partition {
label = "caddy_data"
number = 0
sizemib = 1000
startmib = 0
type_guid = "0FC63DAF-8483-4772-8E79-3D69D8477DE4"
}
}
data "ignition_filesystem" "caddy_config" {
device = "/dev/disk/by-label/caddy_config"
format = "btrfs"
wipe_filesystem = true
label = "caddy_config"
path = "/caddy/config"
mount_options = ["nodev", "noexec", "nosuid"]
}
data "ignition_filesystem" "caddy_data" {
device = "/dev/disk/by-label/caddy_data"
format = "btrfs"
wipe_filesystem = true
label = "caddy_data"
path = "/caddy/data"
mount_options = ["nodev", "noexec", "nosuid"]
}
data "ignition_config" "acme_server" {
disks = [
data.ignition_disk.data.rendered,
]
filesystems = [
data.ignition_filesystem.caddy_config.rendered,
data.ignition_filesystem.caddy_data.rendered,
]
}

View file

@ -0,0 +1,3 @@
output "test" {
value = data.ignition_config.acme_server.rendered
}

View file

@ -0,0 +1,5 @@
variable "fcos_base_vm_id" {
type = number
nullable = false
}

View file

@ -0,0 +1,25 @@
{
skip_install_trust
# acme_ca https://acme-staging-v02.api.letsencrypt.org/directory
log {
output stdout
format json
}
}
%{ for vhost in vhosts ~}
${vhost.domain} {
reverse_proxy {
%{ for upstream in vhost.upstreams ~}
to https://${upstream}
transport http {
tls_server_name ${vhost.domain}
tls_insecure_skip_verify
}
%{ endfor ~}
%{ for hdr in vhost.headers_down ~}
header_down ${hdr.modifier}${hdr.name} "${hdr.value}"
%{ endfor ~}
}
}
%{ endfor ~}

View file

@ -0,0 +1,20 @@
[Unit]
Description = "Caddy Container"
[Container]
ContainerName = caddy
Image = docker.io/caddy:${caddy_version}
Volume = ${caddy_data_volume_name}.volume:/data:z
Volume = ${caddy_config_file_path}:/etc/caddy/Caddyfile:ro,z
Network = ${caddy_network_name}.network
PublishPort = 80:80
PublishPort = 443:443
[Service]
Restart=on-failure
[Install]
WantedBy=default.target

View file

@ -0,0 +1,5 @@
[Unit]
Description = Caddy Network
[Network]
NetworkName = "${caddy_network_name}"

View file

@ -0,0 +1,11 @@
[Unit]
Description = Caddy Data Volume
[Volume]
VolumeName = ${caddy_data_volume_name}
Device=/dev/disk/by-label/${caddy_data_volume_name}
Options=nodev,noexec,nosuid,rootcontext=system_u:object_r:container_file_t:s0
Type=ext4
[Install]
WantedBy=default.target

View file

@ -0,0 +1,151 @@
locals {
data_device_path = "/dev/vdb"
caddy_version = "2.8.4-alpine"
caddy_config_dir_path = "/opt/caddy_config"
caddy_data_volume_name = "caddy_data"
caddy_network_name = "caddy_net"
data_disk = {
device = local.data_device_path
wipeTable = true
partitions = [
{
label = local.caddy_data_volume_name
number = 1
sizeMiB = 512
wipePartitionEntry = true
shouldExist = true
resize = true
},
]
}
caddy_data_filesystem = {
device = "${local.data_device_path}1"
format = "ext4"
label = local.caddy_data_volume_name
}
caddy_data_volume_file = {
path = "/etc/containers/systemd/${local.caddy_data_volume_name}.volume"
user = {id = 0}
group = {id = 0}
mode = 420 # 0644
contents = {
source = format(
"data:text/plain;base64,%s",
base64encode(
templatefile(
"${path.module}/files/caddy_data.volume.tftpl",
{
caddy_data_volume_name = local.caddy_data_volume_name
}
)
)
)
}
}
caddy_config_directory = {
path = local.caddy_config_dir_path
user = {id = 0}
group = {id = 0}
mode = 420 # 0644
}
caddyfile_file = {
path = "${local.caddy_config_dir_path}/Caddyfile"
user = {id = 0}
group = {id = 0}
mode = 420 # 0644
contents = {
source = format(
"data:text/plain;base64,%s",
base64encode(
templatefile(
"${path.module}/files/Caddyfile.tftpl",
{
vhosts = var.vhosts
}
)
)
)
}
}
caddy_network_file = {
path = "/etc/containers/systemd/${local.caddy_network_name}.network"
user = {id = 0}
group = {id = 0}
mode = 420 # 0644
contents = {
source = format(
"data:text/plain;base64,%s",
base64encode(
templatefile(
"${path.module}/files/caddy.network.tftpl",
{
caddy_network_name = local.caddy_network_name
}
)
)
)
}
}
caddy_container_file = {
path = "/etc/containers/systemd/caddy.container"
user = {id = 0}
group = {id = 0}
mode = 420 # 0644
contents = {
source = format(
"data:text/plain;base64,%s",
base64encode(
templatefile(
"${path.module}/files/caddy.container.tftpl",
{
caddy_version = local.caddy_version
caddy_data_volume_name = local.caddy_data_volume_name
caddy_config_file_path = "${local.caddy_config_dir_path}/Caddyfile"
caddy_network_name = local.caddy_network_name
}
)
)
)
}
}
ignition_config = jsonencode({
ignition = {
version = "3.4.0"
}
storage = {
disks = [
local.data_disk,
]
filesystems = [
local.caddy_data_filesystem,
]
files = [
local.caddy_data_volume_file,
local.caddyfile_file,
local.caddy_network_file,
local.caddy_container_file,
]
directories = [
local.caddy_config_directory,
]
}
passwd = {
users = [
{
name = "core"
sshAuthorizedKeys = var.ssh_authorized_keys
}
]
}
})
}

View file

@ -0,0 +1,3 @@
output "config" {
value = local.ignition_config
}

View file

@ -0,0 +1,16 @@
variable "vhosts" {
type = list(object({
domain = string
upstreams = list(string)
headers_down = optional(list(object({
modifier = optional(string, "")
name = string
value = string
})), [])
}))
}
variable "ssh_authorized_keys" {
type = list(string)
nullable = false
}

View file

@ -0,0 +1,20 @@
{
skip_install_trust
# acme_ca https://acme-staging-v02.api.letsencrypt.org/directory
log {
output stdout
format json
}
}
${castopod_domain}:8000 {
handle_path /media/* {
file_server {
root /media
}
}
reverse_proxy {
to ${castopod_container_name}:8000
}
tls internal
}

View file

@ -0,0 +1,6 @@
[Unit]
Description = Caddy Frontend Network
[Network]
NetworkName = "${caddy_frontend_network_name}"
Internal = true

View file

@ -0,0 +1,21 @@
[Unit]
Description = "Caddy Container"
[Container]
ContainerName = "${caddy_container_name}"
Image = "docker.io/caddy/caddy:${caddy_version}"
Volume = ${caddy_config_dir}/Caddyfile:/etc/caddy/Caddyfile:ro
Volume = ${castopod_media_volume_name}.volume:/media:z
Network = ${caddy_frontend_network_name}.network
Network = ${castopod_frontend_network_name}.network
PublishPort=${castopod_upstream_port}:8000
[Service]
Restart=on-failure
ExecStartPre=/usr/bin/chcon -t container_file_t ${caddy_config_dir}/Caddyfile
[Install]
WantedBy=default.target

View file

@ -0,0 +1,6 @@
[Unit]
Description = Castopod Backend Network
[Network]
NetworkName = "${castopod_backend_network_name}"
Internal = true

View file

@ -0,0 +1,5 @@
[Unit]
Description = Castopod Frontend Network
[Network]
NetworkName = "${castopod_frontend_network_name}"

View file

@ -0,0 +1,8 @@
[Unit]
Description = "Castopod Media Volume"
[Volume]
VolumeName = ${castopod_media_volume_name}
Device=/dev/disk/by-label/${castopod_media_volume_name}
Options=nodev,noexec,nosuid,rootcontext=system_u:object_r:container_file_t:s0
Type=ext4

View file

@ -0,0 +1,30 @@
[Unit]
Description = "Castopod Container"
Wants=generate_secrets.service
After=generate_secrets.service
[Container]
ContainerName = "${castopod_container_name}"
Image = "docker.io/castopod/castopod:${castopod_version}"
Volume = ${castopod_media_volume_name}.volume:/var/www/castopod/public/media:z
Network = ${castopod_frontend_network_name}.network
Network = ${castopod_backend_network_name}.network
Environment=CP_DATABASE_HOSTNAME=${mariadb_container_name}
Environment=CP_DATABASE_NAME=${castopod_db_name}
Environment=CP_DATABASE_USERNAME=${castopod_db_user}
Environment=CP_BASEURL=${castopod_base_url}
Environment=CP_CACHE_HANDLER=redis
Environment=CP_REDIS_HOST=${valkey_container_name}
EnvironmentFile=${secrets_path}/castopod-mariadb-password.env
EnvironmentFile=${secrets_path}/castopod-valkey.env
EnvironmentFile=${secrets_path}/castopod-analytics.env
[Service]
Restart=on-failure
[Install]
WantedBy=default.target

View file

@ -0,0 +1,20 @@
[Unit]
Description = "Generate Secrets"
Wants=${secrets_path_escaped}.mount
After=${secrets_path_escaped}.mount
ConditionPathExists=!${secrets_path}/mariadb-root-password.env
ConditionPathExists=!${secrets_path}/mariadb-password.env
ConditionPathExists=!${secrets_path}/castopod-mariadb-password.env
ConditionPathExists=!${secrets_path}/castopod-analytics.env
ConditionPathExists=!${secrets_path}/castopod-valkey.env
ConditionPathExists=!${secrets_path}/valkey-entrypoint.sh
[Service]
Type=oneshot
RemainAfterExit=true
ExecStart=/bin/bash /var/opt/generate_secrets.sh
[Install]
WantedBy=default.target

View file

@ -0,0 +1,22 @@
#!/bin/bash
set -o errexit -o nounset -o pipefail
umask 7177
openssl rand -hex 16 | tr -d '\n' > "${secrets_path}/mariadb-root-password.secret"
(echo -n 'MARIADB_ROOT_PASSWORD=' ; cat ${secrets_path}/mariadb-root-password.secret) > "${secrets_path}/mariadb-root-password.env"
openssl rand -hex 16 | tr -d '\n' > "${secrets_path}/castopod-mariadb.secret"
(echo -n 'MARIADB_PASSWORD=' ; cat "${secrets_path}/castopod-mariadb.secret") > "${secrets_path}/mariadb-password.env"
(echo -n 'CP_DATABASE_PASSWORD=' ; cat "${secrets_path}/castopod-mariadb.secret") > "${secrets_path}/castopod-mariadb-password.env"
(echo -n 'CP_ANALYTICS_SALT=' ; openssl rand -base64 16) > "${secrets_path}/castopod-analytics.env"
openssl rand -hex 16 | tr -d '\n' > "${secrets_path}/castopod-valkey.secret"
(echo -n 'CP_REDIS_PASSWORD=' ; cat "${secrets_path}/castopod-valkey.secret") > "${secrets_path}/castopod-valkey.env"
echo "#!/bin/sh" > '${secrets_path}/valkey-entrypoint.sh'
(echo '/usr/local/bin/docker-entrypoint.sh valkey-server --requirepass "' ; cat "${secrets_path}/castopod-valkey.secret" ; echo '"') >> '${secrets_path}/valkey-entrypoint.sh'
chown 999 '${secrets_path}/valkey-entrypoint.sh'
chmod 700 '${secrets_path}/valkey-entrypoint.sh'
chcon -t container_file_t '${secrets_path}/valkey-entrypoint.sh'

View file

@ -0,0 +1,8 @@
[Unit]
Description = "MariaDB data Volume"
[Volume]
VolumeName = ${mariadb_data_volume_name}
Device=/dev/disk/by-label/${mariadb_data_volume_name}
Options=nodev,noexec,nosuid,rootcontext=system_u:object_r:container_file_t:s0
Type=ext4

View file

@ -0,0 +1,28 @@
[Unit]
Description = "MariaDB Container"
Wants=generate_secrets.service
After=generate_secrets.service
[Container]
ContainerName = "${mariadb_container_name}"
Image = "docker.io/mariadb:${mariadb_version}"
Volume = ${mariadb_data_volume_name}.volume:/var/lib/mysql:z
Network = ${castopod_backend_network_name}.network
EnvironmentFile=${secrets_path}/mariadb-root-password.env
EnvironmentFile=${secrets_path}/mariadb-password.env
Environment=MARIADB_DATABASE=${castopod_db_name}
Environment=MARIADB_USER=${castopod_db_user}
# skips chown that would cause a EPERM on lost+found
User=mysql
Group=mysql
[Service]
Restart=on-failure
[Install]
WantedBy=default.target

View file

@ -0,0 +1,11 @@
[Unit]
Description = Mountpoint for persistent secrets
[Mount]
What=LABEL=${secrets_part_name}
Where=/var/opt/secrets
Type=ext4
Options=nosuid,nodev
[Install]
WantedBy=local-fs.target

View file

@ -0,0 +1,25 @@
[Unit]
Description = "Valkey Container"
Wants=generate_secrets.service
After=generate_secrets.service
[Container]
ContainerName = "${valkey_container_name}"
Image = "docker.io/valkey/valkey:${valkey_version}"
Volume = ${secrets_path}/valkey-entrypoint.sh:/usr/local/bin/valkey-entrypoint.sh:ro
Volume = ${valkey_cache_volume_name}.volume:/data:z
Network = ${castopod_backend_network_name}.network
Entrypoint=/usr/local/bin/valkey-entrypoint.sh
# skips find/chown in docker entrypoint which tries to chown lost+found and receive a perm denied
User=valkey
Group=valkey
[Service]
Restart=on-failure
[Install]
WantedBy=default.target

View file

@ -0,0 +1,8 @@
[Unit]
Description = "Valkey Cache Volume"
[Volume]
VolumeName = ${valkey_cache_volume_name}
Device=/dev/disk/by-label/${valkey_cache_volume_name}
Options=nodev,noexec,nosuid,rootcontext=system_u:object_r:container_file_t:s0
Type=ext4

452
modules/castopod/main.tf Normal file
View file

@ -0,0 +1,452 @@
locals {
caddy_frontend_network_name = "caddy-frontend"
caddy_container_name = "caddy"
caddy_version = "2.9.1-alpine"
caddy_config_dir = "/var/opt/caddy"
castopod_frontend_network_name = "castopod-frontend"
castopod_backend_network_name = "castopod-backend"
castopod_media_volume_name = "castopod-media"
castopod_container_name = "castopod"
castopod_db_name = "castopod"
castopod_db_user = "castopod"
castopod_base_url = var.base_url
valkey_container_name = "valkey"
valkey_cache_volume_name = "castopod-cache"
mariadb_container_name = "mariadb"
mariadb_data_volume_name = "castopod-db"
mariadb_version = "11.5"
secrets_part_name = "secrets"
secrets_path = "/var/opt/secrets"
secrets_path_escaped = "var-opt-secrets"
data_device_path = "/dev/vdb"
data_disk = {
device = local.data_device_path
wipeTable = true
partitions = [
{
label = local.secrets_part_name
number = 1
sizeMiB = 1024
wipePartitionEntry = true
shouldExist = true
resize = true
},
{
label = local.castopod_media_volume_name
number = 2
sizeMiB = 20 * 1024
wipePartitionEntry = true
shouldExist = true
resize = true
},
{
label = local.mariadb_data_volume_name
number = 3
sizeMiB = 5 * 1024
wipePartitionEntry = true
shouldExist = true
resize = true
},
{
label = local.valkey_cache_volume_name
number = 4
sizeMiB = 1024
wipePartitionEntry = true
shouldExist = true
resize = true
},
]
}
caddy_config_directory = {
path = local.caddy_config_dir
user = {id = 0}
group = {id = 0}
mode = 448 # 0700
}
caddy_config_file = {
path = "${local.caddy_config_dir}/Caddyfile"
user = {id = 0}
group = {id = 0}
mode = 420 # 0644
contents = {
source = format(
"data:text/plain;base64,%s",
base64encode(
templatefile(
"${path.module}/files/Caddyfile.tftpl",
{
castopod_domain = var.castopod_domain
castopod_container_name = local.castopod_container_name
}
)
)
)
}
}
caddy_frontend_network_file = {
path = "/etc/containers/systemd/caddy-frontend.network"
user = {id = 0}
group = {id = 0}
mode = 420 # 0644
contents = {
source = format(
"data:text/plain;base64,%s",
base64encode(
templatefile(
"${path.module}/files/caddy-frontend.network.tftpl",
{
caddy_frontend_network_name = local.caddy_frontend_network_name
}
)
)
)
}
}
caddy_container_file = {
path = "/etc/containers/systemd/caddy.container"
user = {id = 0}
group = {id = 0}
mode = 420 # 0644
contents = {
source = format(
"data:text/plain;base64,%s",
base64encode(
templatefile(
"${path.module}/files/caddy.container.tftpl",
{
caddy_container_name = local.caddy_container_name
caddy_version = local.caddy_version
caddy_config_dir = local.caddy_config_dir
caddy_frontend_network_name = local.caddy_frontend_network_name
castopod_frontend_network_name = local.castopod_frontend_network_name
castopod_upstream_port = var.castopod_upstream_port
castopod_media_volume_name = local.castopod_media_volume_name
}
)
)
)
}
}
castopod_secrets_filesystem = {
device = "${local.data_device_path}1"
format = "ext4"
label = local.secrets_part_name
}
castopod_secrets_directory = {
path = local.secrets_path
user = {id = 0}
group = {id = 0}
mode = 448 # 0700
}
castopod_secrets_mount_unit = {
name = "${local.secrets_path_escaped}.mount"
enabled = true
contents = templatefile(
"${path.module}/files/secrets.mount.tftpl",
{
secrets_part_name = local.secrets_part_name
secrets_path = local.secrets_path
}
)
}
castopod_generate_secrets_script_file = {
path = "/var/opt/generate_secrets.sh"
user = {id = 0}
group = {id = 0}
mode = 448 # 0700
contents = {
source = format(
"data:text/plain;base64,%s",
base64encode(
templatefile(
"${path.module}/files/generate_secrets.sh.tftpl",
{
secrets_path = local.secrets_path
}
)
)
)
}
}
castopod_generate_secrets_service_unit = {
name = "generate_secrets.service"
enabled = true
contents = templatefile(
"${path.module}/files/generate_secrets.service.tftpl",
{
secrets_path = local.secrets_path
secrets_path_escaped = local.secrets_path_escaped
}
)
}
castopod_frontend_network_file = {
path = "/etc/containers/systemd/${local.castopod_frontend_network_name}.network"
user = {id = 0}
group = {id = 0}
mode = 420 # 0644
contents = {
source = format(
"data:text/plain;base64,%s",
base64encode(
templatefile(
"${path.module}/files/castopod-frontend.network.tftpl",
{
castopod_frontend_network_name = local.castopod_frontend_network_name
}
)
)
)
}
}
castopod_backend_network_file = {
path = "/etc/containers/systemd/${local.castopod_backend_network_name}.network"
user = {id = 0}
group = {id = 0}
mode = 420 # 0644
contents = {
source = format(
"data:text/plain;base64,%s",
base64encode(
templatefile(
"${path.module}/files/castopod-backend.network.tftpl",
{
castopod_backend_network_name = local.castopod_backend_network_name
}
)
)
)
}
}
castopod_media_volume_filesystem = {
device = "${local.data_device_path}2"
format = "ext4"
label = local.castopod_media_volume_name
options = [
"-E", "root_owner=33:33",
]
}
castopod_media_volume_file = {
path = "/etc/containers/systemd/${local.castopod_media_volume_name}.volume"
user = {id = 0}
group = {id = 0}
mode = 420 # 0644
contents = {
source = format(
"data:text/plain;base64,%s",
base64encode(
templatefile(
"${path.module}/files/castopod-media.volume.tftpl",
{
castopod_media_volume_name = local.castopod_media_volume_name
}
)
)
)
}
}
mariadb_data_volume_filesystem = {
device = "${local.data_device_path}3"
format = "ext4"
label = local.mariadb_data_volume_name
options = [
"-E", "root_owner=999:999",
]
}
mariadb_data_volume_file = {
path = "/etc/containers/systemd/${local.mariadb_data_volume_name}.volume"
user = {id = 0}
group = {id = 0}
mode = 420 # 0644
contents = {
source = format(
"data:text/plain;base64,%s",
base64encode(
templatefile(
"${path.module}/files/mariadb-data.volume.tftpl",
{
mariadb_data_volume_name = local.mariadb_data_volume_name
}
)
)
)
}
}
mariadb_container_file = {
path = "/etc/containers/systemd/${local.mariadb_container_name}.container"
user = {id = 0}
group = {id = 0}
mode = 420 # 0644
contents = {
source = format(
"data:text/plain;base64,%s",
base64encode(
templatefile(
"${path.module}/files/mariadb.container.tftpl",
{
mariadb_container_name = local.mariadb_container_name
mariadb_version = local.mariadb_version
mariadb_data_volume_name = local.mariadb_data_volume_name
castopod_backend_network_name = local.castopod_backend_network_name
castopod_db_name = local.castopod_db_name
castopod_db_user = local.castopod_db_user
secrets_path = local.secrets_path
}
)
)
)
}
}
valkey_cache_volume_filesystem = {
device = "${local.data_device_path}4"
format = "ext4"
label = local.valkey_cache_volume_name
options = [
"-E", "root_owner=999:999",
]
}
valkey_cache_volume_file = {
path = "/etc/containers/systemd/${local.valkey_cache_volume_name}.volume"
user = {id = 0}
group = {id = 0}
mode = 420 # 0644
contents = {
source = format(
"data:text/plain;base64,%s",
base64encode(
templatefile(
"${path.module}/files/valkey.volume.tftpl",
{
valkey_cache_volume_name = local.valkey_cache_volume_name
}
)
)
)
}
}
valkey_container_file = {
path = "/etc/containers/systemd/${local.valkey_container_name}.container"
user = {id = 0}
group = {id = 0}
mode = 420 # 0644
contents = {
source = format(
"data:text/plain;base64,%s",
base64encode(
templatefile(
"${path.module}/files/valkey.container.tftpl",
{
valkey_container_name = local.valkey_container_name
valkey_version = "8.0-alpine"
valkey_cache_volume_name = local.valkey_cache_volume_name
castopod_backend_network_name = local.castopod_backend_network_name
secrets_path = local.secrets_path
}
)
)
)
}
}
castopod_container_file = {
path = "/etc/containers/systemd/${local.castopod_container_name}.container"
user = {id = 0}
group = {id = 0}
mode = 420 # 0644
contents = {
source = format(
"data:text/plain;base64,%s",
base64encode(
templatefile("${path.module}/files/castopod.container.tftpl", {
castopod_version = "1.13.2",
castopod_container_name = local.castopod_container_name
castopod_frontend_network_name = local.castopod_frontend_network_name
castopod_backend_network_name = local.castopod_backend_network_name
castopod_media_volume_name = local.castopod_media_volume_name
castopod_db_name = local.castopod_db_name
castopod_db_user = local.castopod_db_user
castopod_base_url = var.base_url
mariadb_container_name = local.mariadb_container_name
valkey_container_name = local.valkey_container_name
secrets_path = local.secrets_path
})
)
)
}
}
ignition_config = jsonencode({
ignition = {
version = "3.4.0"
}
storage = {
disks = [
local.data_disk,
]
filesystems = [
local.castopod_secrets_filesystem,
local.castopod_media_volume_filesystem,
local.mariadb_data_volume_filesystem,
local.valkey_cache_volume_filesystem,
]
files = [
local.caddy_config_file,
local.caddy_frontend_network_file,
local.caddy_container_file,
local.castopod_generate_secrets_script_file,
local.castopod_frontend_network_file,
local.castopod_backend_network_file,
local.castopod_media_volume_file,
local.mariadb_data_volume_file,
local.mariadb_container_file,
local.valkey_cache_volume_file,
local.valkey_container_file,
local.castopod_container_file,
]
directories = [
local.caddy_config_directory,
local.castopod_secrets_directory,
]
}
systemd = {
units = [
local.castopod_secrets_mount_unit,
local.castopod_generate_secrets_service_unit,
]
}
passwd = {
users = [
{
name = "core"
sshAuthorizedKeys = var.ssh_authorized_keys
}
]
}
})
}

View file

@ -0,0 +1,3 @@
output "config" {
value = local.ignition_config
}

View file

@ -0,0 +1,19 @@
variable "ssh_authorized_keys" {
type = list(string)
nullable = false
}
variable "base_url" {
type = string
nullable = false
}
variable "castopod_domain" {
type = string
nullable = false
}
variable "castopod_upstream_port" {
type = number
nullable = false
}

2
modules/dns_resolver/.gitignore vendored Normal file
View file

@ -0,0 +1,2 @@
dns_resolver_dhcp_config_*.conf
dns_resolver_sftp_script_for_*

View file

@ -0,0 +1,3 @@
dhcp-host=${mac_address},set:exampletag,${host_ip},example
dhcp-option=tag:sampletag,encap:128,2,"${vm_id}.ign"
dhcp-option=tag:sampletag,encap:128,3,"/dev/disk/by-path/pci-0000:00:0a.0"

View file

@ -0,0 +1,168 @@
terraform {
required_providers {
proxmox = {
source = "bpg/proxmox"
version = "~>0.56.1"
}
}
required_version = ">=1.6.2"
}
locals {
core_user = {
name = "core"
password_hash = "$6$vDMAZf/yOO6mEbcs$6VE7WD8T9/PeotszMFxatOQxB/rFmLDWsNajg4sI0O47OikSuVpqPjkxRbzcueiXn6rBUY1ubCHlp0nnoZ1VI1"
}
hostname_file = {
path = "/etc/hostname"
user = {id = 0}
group = {id = 0}
mode = 420 # 0644
contents = {
source = format(
"data:text/plain;base64,%s",
base64encode(var.instance_name)
)
}
}
ignition_configuration = jsonencode({
ignition = {
version = "3.4.0"
}
storage = {
files = [
{
path = "/etc/hostname"
user = {id = 0}
group = {id = 0}
mode = 420 # 0644
contents = {
source = format(
"data:text/plain;base64,%s",
base64encode(var.instance_name)
)
}
},
]
}
passwd = {
users = [
local.core_user,
]
}
})
}
resource "random_pet" "config_name" {
length = 4
}
locals {
generated_ignition_config_file = "${path.module}/dns_resolver_ignition_config_${random_pet.config_name.id}.ign"
}
resource "local_file" "sftp_script_for_ignition_file" {
content = <<EOT
cd writable
-rm ${var.pve_vm_id}.ign
put ${local.generated_ignition_config_file} ${var.pve_vm_id}.ign
EOT
filename = "${path.module}/dns_resolver_sftp_script_for_ignition_config_${random_pet.config_name.id}"
file_permission = "0644"
}
resource "local_file" "dns_resolver_ignition_config" {
content = local.ignition_configuration
filename = local.generated_ignition_config_file
file_permission = "0644"
provisioner "local-exec" {
command = <<EOT
sftp -P ${var.netboot_server_sftp_port} \
-o ProxyJump=${var.pve_ssh_user}@${var.pve_ssh_host} \
-b "${path.module}/dns_resolver_sftp_script_for_ignition_config_${random_pet.config_name.id}" \
terraform_ignition@${var.netboot_server_ip_address}
EOT
}
lifecycle {
replace_triggered_by = [local_file.sftp_script_for_ignition_file]
}
}
resource "local_file" "sftp_script_for_dhcp_config" {
content = <<EOT
cd writable
-rm ${var.pve_vm_id}.conf
put ${path.module}/dns_resolver_dhcp_config_${random_pet.config_name.id}.conf ${var.pve_vm_id}.conf
EOT
filename = "${path.module}/dns_resolver_sftp_script_for_dhcp_config_${random_pet.config_name.id}"
file_permission = "0644"
}
resource "local_file" "dhcp_config" {
depends_on = [ local_file.sftp_script_for_dhcp_config ]
content = templatefile(
"${path.module}/files/dhcp_config.conf.tftpl",
{
vm_id = var.pve_vm_id
host_ip = cidrhost(var.prod_network.prefix, var.pve_vm_id)
mac_address = var.prod_network.mac_address
}
)
filename = "${path.module}/dns_resolver_dhcp_config_${random_pet.config_name.id}.conf"
file_permission = "0644"
provisioner "local-exec" {
command = <<EOT
sftp -P ${var.netboot_server_sftp_port} \
-o ProxyJump=${var.pve_ssh_user}@${var.pve_ssh_host} \
-b "${path.module}/dns_resolver_sftp_script_for_dhcp_config_${random_pet.config_name.id}" \
terraform_dhcp@${var.netboot_server_ip_address}
EOT
}
lifecycle {
replace_triggered_by = [local_file.sftp_script_for_dhcp_config ]
}
}
resource "proxmox_virtual_environment_vm" "netboot_server" {
name = var.instance_name
node_name = var.pve_node_name
vm_id = var.pve_vm_id
cpu {
architecture = "x86_64"
type = "host"
sockets = 1
cores = 4
}
memory {
dedicated = 4096
}
disk {
datastore_id = var.pve_storage_id
interface = "virtio0"
size = 10
}
network_device {
bridge = "prod"
model = "virtio"
mac_address = var.prod_network.mac_address
}
boot_order = ["net0"]
operating_system {
type = "l26"
}
vga {}
serial_device{}
}

View file

View file

@ -0,0 +1,70 @@
variable "pve_node_name" {
type = string
nullable = false
}
variable "pve_storage_id" {
type = string
nullable = false
}
variable "pve_vm_id" {
type = number
nullable = false
}
variable "pve_ssh_user" {
type = string
nullable = false
default = "root"
}
variable "pve_ssh_host" {
type = string
nullable = false
default = "proxmox.broken-by-design.fr"
}
variable "netboot_server_ip_address" {
type = string
nullable = false
default = "10.109.0.2"
}
variable "netboot_server_sftp_port" {
type = number
nullable = false
default = 2222
}
variable "instance_name" {
type = string
default = "knot-resolver"
}
variable "admin_network" {
type = object({
name = string
prefix = string
mac_address = string
})
nullable = false
}
variable "prod_network" {
type = object({
name = string
prefix = string
mac_address = string
})
nullable = false
}
variable "monitoring_network" {
type = object({
name = string
prefix = string
mac_address = string
})
nullable = false
}

View file

@ -0,0 +1,7 @@
output "castopod_config" {
value = module.castopod_config.config
}
output "caddy_config" {
value = module.caddy_config.config
}