initial
This commit is contained in:
commit
0dd3b5bdfe
50 changed files with 2019 additions and 0 deletions
8
.gitignore
vendored
Normal file
8
.gitignore
vendored
Normal file
|
@ -0,0 +1,8 @@
|
|||
.terraform/
|
||||
.terraform.lock.hcl
|
||||
terraform.tfstate
|
||||
**/*.iso
|
||||
**/*.sig
|
||||
**/*.ign
|
||||
pve_api_token
|
||||
settings.tfvars
|
12
LICENSE.md
Normal file
12
LICENSE.md
Normal file
|
@ -0,0 +1,12 @@
|
|||
# SPDX short identifier: BSD-3-Clause
|
||||
Copyright 2024 Florian Maury
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
|
||||
|
||||
3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS “AS IS” AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
7
README.md
Normal file
7
README.md
Normal file
|
@ -0,0 +1,7 @@
|
|||
# Preuve de concept pour une infra virtualisée sur Proxmox avec Fedora CoreOS
|
||||
|
||||
## Contributions
|
||||
|
||||
Les contributions, commentaires, et pull-requests sont à envoyer à l'adresse florian.maury@metempsychose.fr.
|
||||
|
||||
Vous pouvez consulter [ce site](https://git-send-email.io/) pour en apprendre plus sur l'envoi de contributions par email.
|
59
main.tf
Normal file
59
main.tf
Normal file
|
@ -0,0 +1,59 @@
|
|||
terraform {
|
||||
required_providers {
|
||||
proxmox = {
|
||||
source = "bpg/proxmox"
|
||||
version = "~>0.56.1"
|
||||
}
|
||||
}
|
||||
required_version = ">=1.6.2"
|
||||
}
|
||||
|
||||
provider "proxmox" {
|
||||
endpoint = var.pve_api_base_url
|
||||
api_token = var.pve_api_token
|
||||
}
|
||||
|
||||
module "netboot_server" {
|
||||
source = "./modules/netboot_server"
|
||||
hostname = "netboot_server"
|
||||
prod_network_name = var.admin_network_name
|
||||
dhcp_iface = "ens18"
|
||||
dhcp_server_ip_addr = cidrhost(var.admin_network_prefix, 2)
|
||||
dhcp_gateway = cidrhost(var.admin_network_prefix, 1)
|
||||
dhcp_range = var.admin_network_prefix
|
||||
ssh_public_key_opentofu_netboot_server = var.ssh_public_key_opentofu_netboot_server
|
||||
|
||||
pve_api_base_url = var.pve_api_base_url
|
||||
pve_api_token = var.pve_api_token
|
||||
pve_node_name = var.pve_node_name
|
||||
pve_storage_id = var.pve_storage_id
|
||||
pve_vm_id = 108
|
||||
}
|
||||
|
||||
module "poc" {
|
||||
depends_on = [ module.netboot_server ]
|
||||
source = "./modules/poc"
|
||||
pve_vm_id = 110
|
||||
pve_storage_id = "local"
|
||||
pve_node_name = "ns3152888"
|
||||
pve_ssh_user = var.pve_ssh_user
|
||||
pve_ssh_host = var.pve_ssh_host
|
||||
netboot_server_ip_address = cidrhost(var.admin_network_prefix, 2)
|
||||
|
||||
admin_network = {
|
||||
name = var.admin_network_name
|
||||
prefix = var.admin_network_prefix
|
||||
mac_address = "1c:69:7a:ff:ff:01"
|
||||
}
|
||||
prod_network = {
|
||||
name = var.prod_network_name
|
||||
prefix = var.prod_network_prefix
|
||||
mac_address = "1c:69:7a:ef:ff:01"
|
||||
}
|
||||
monitoring_network = {
|
||||
name = var.monit_network_name
|
||||
prefix = var.monit_network_prefix
|
||||
mac_address = "1c:69:7a:df:ff:01"
|
||||
}
|
||||
admin_ssh_public_key = var.ssh_public_key_admin_netboot_server
|
||||
}
|
149
modules/netboot_server/caddy.tf
Normal file
149
modules/netboot_server/caddy.tf
Normal file
|
@ -0,0 +1,149 @@
|
|||
locals {
|
||||
caddy_data_filesystem = {
|
||||
device = "${local.data_device_path}-part1"
|
||||
format = "ext4"
|
||||
label = "caddy_data"
|
||||
}
|
||||
|
||||
caddy_data_volume_file = {
|
||||
path = "/etc/containers/systemd/caddy_data.volume"
|
||||
user = {id = 0}
|
||||
group = {id = 0}
|
||||
mode = 420 # 0644
|
||||
contents = {
|
||||
source = format(
|
||||
"data:text/plain;base64,%s",
|
||||
base64encode(file("${path.module}/files/caddy/caddy_data.volume"))
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fcos_images_filesystem = {
|
||||
device = "${local.data_device_path}-part4"
|
||||
format = "ext4"
|
||||
label = "fcos_images"
|
||||
}
|
||||
|
||||
fcos_images_volume_file = {
|
||||
path = "/etc/containers/systemd/fcos_images.volume"
|
||||
user = {id = 0}
|
||||
group = {id = 0}
|
||||
mode = 420 # 0644
|
||||
contents = {
|
||||
source = format(
|
||||
"data:text/plain;base64,%s",
|
||||
base64encode(file("${path.module}/files/caddy/fcos_images.volume"))
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
image_downloader_image_file = {
|
||||
path = "/etc/containers/systemd/image_downloader.image"
|
||||
user = {id = 0}
|
||||
group = {id = 0}
|
||||
mode = 420 # 0644
|
||||
contents = {
|
||||
source = format(
|
||||
"data:text/plain;base64,%s",
|
||||
base64encode(file("${path.module}/files/caddy/image_downloader.image"))
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
image_downloader_container_file = {
|
||||
path = "/etc/containers/systemd/image_downloader.container"
|
||||
user = {id = 0}
|
||||
group = {id = 0}
|
||||
mode = 420 # 0644
|
||||
contents = {
|
||||
source = format(
|
||||
"data:text/plain;base64,%s",
|
||||
base64encode(file("${path.module}/files/caddy/image_downloader.container"))
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
caddy_builddir_dir = {
|
||||
path = "/root/caddy"
|
||||
user = {id = 0}
|
||||
group = {id = 0}
|
||||
mode = 448 # 0700
|
||||
}
|
||||
|
||||
caddyfile_file = {
|
||||
path = "/root/caddy/Caddyfile"
|
||||
user = {id = 0}
|
||||
group = {id = 0}
|
||||
mode = 384 # 0600
|
||||
contents = {
|
||||
source = format(
|
||||
"data:text/plain;base64,%s",
|
||||
base64encode(file("${path.module}/files/caddy/Caddyfile"))
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
ipxe_script_file = {
|
||||
path = "/root/caddy/ipxe.script"
|
||||
user = {id = 0}
|
||||
group = {id = 0}
|
||||
mode = 384 # 0600
|
||||
contents = {
|
||||
source = format(
|
||||
"data:text/plain;base64,%s",
|
||||
base64encode(file("${path.module}/files/caddy/ipxe.script"))
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
caddy_containerfile_file = {
|
||||
path = "/root/caddy/Containerfile"
|
||||
user = {id = 0}
|
||||
group = {id = 0}
|
||||
mode = 384 # 0600
|
||||
contents = {
|
||||
source = format(
|
||||
"data:text/plain;base64,%s",
|
||||
base64encode(file("${path.module}/files/caddy/caddy.Containerfile"))
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
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(file("${path.module}/files/caddy/caddy.container"))
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
caddy_filesystems = [
|
||||
local.caddy_data_filesystem,
|
||||
local.fcos_images_filesystem,
|
||||
]
|
||||
|
||||
caddy_directories = [
|
||||
local.caddy_builddir_dir,
|
||||
]
|
||||
|
||||
caddy_files = [
|
||||
local.caddy_data_volume_file,
|
||||
local.fcos_images_volume_file,
|
||||
local.image_downloader_image_file,
|
||||
local.image_downloader_container_file,
|
||||
local.caddyfile_file,
|
||||
local.ipxe_script_file,
|
||||
local.caddy_containerfile_file,
|
||||
local.caddy_container_file,
|
||||
]
|
||||
|
||||
caddy_systemd_units = [
|
||||
]
|
||||
}
|
||||
|
||||
|
126
modules/netboot_server/dhcp.tf
Normal file
126
modules/netboot_server/dhcp.tf
Normal file
|
@ -0,0 +1,126 @@
|
|||
locals {
|
||||
dhcp_config_path_systemd_unit = {
|
||||
name = "dhcp_config.path"
|
||||
enabled = true
|
||||
contents = templatefile(
|
||||
"${path.module}/files/dhcp/dhcp_config.path.tftpl",
|
||||
{
|
||||
path = "/var/lib/containers/storage/volumes/dhcp_config/_data/writable/"
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
dhcp_config_service_systemd_unit = {
|
||||
name = "dhcp_config.service"
|
||||
enabled = false
|
||||
contents = file("${path.module}/files/dhcp/dhcp_config.service")
|
||||
}
|
||||
|
||||
dhcp_data_filesystem = {
|
||||
device = "${local.data_device_path}-part3"
|
||||
format = "ext4"
|
||||
label = "dhcp_data"
|
||||
}
|
||||
|
||||
dhcp_data_volume_file = {
|
||||
path = "/etc/containers/systemd/dhcp_data.volume"
|
||||
user = {id = 0}
|
||||
group = {id = 0}
|
||||
mode = 420 # 0644
|
||||
contents = {
|
||||
source = format(
|
||||
"data:text/plain;base64,%s",
|
||||
base64encode(file("${path.module}/files/dhcp/dhcp_data.volume"))
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
dhcp_builddir_dir = {
|
||||
path = "/root/dhcp"
|
||||
user = {id = 0}
|
||||
group = {id = 0}
|
||||
mode = 448 # 0700
|
||||
}
|
||||
|
||||
dnsmasq_base_config_file = {
|
||||
path = "/root/dhcp/dnsmasq.conf"
|
||||
user = {id = 0}
|
||||
group = {id = 0}
|
||||
mode = 420 # 0644
|
||||
contents = {
|
||||
source = format(
|
||||
"data:text/plain;base64,%s",
|
||||
base64encode(templatefile(
|
||||
"${path.module}/files/dhcp/dnsmasq.conf.tftpl",
|
||||
{
|
||||
dhcp_server_ip_addr = var.dhcp_server_ip_addr
|
||||
dhcp_range = split("/", var.dhcp_range)[0]
|
||||
dhcp_range_netmask = cidrnetmask(var.dhcp_range)
|
||||
dhcp_router = var.dhcp_gateway
|
||||
config_extension_dir = "/etc/dnsmasq.d/writable/"
|
||||
}
|
||||
))
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
generate_dhcp_options_script_file = {
|
||||
path = "/var/roothome/generate_dhcp_options.sh"
|
||||
user = {id = 0}
|
||||
group = {id = 0}
|
||||
mode = 448 # 0700
|
||||
contents = {
|
||||
source = format(
|
||||
"data:text/plain;base64,%s",
|
||||
base64encode(file("${path.module}/files/dhcp/generate_dhcp_options.sh"))
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
dhcp_containerfile_file = {
|
||||
path = "/root/dhcp/Containerfile"
|
||||
user = {id = 0}
|
||||
group = {id = 0}
|
||||
mode = 420 # 0644
|
||||
contents = {
|
||||
source = format(
|
||||
"data:text/plain;base64,%s",
|
||||
base64encode(file("${path.module}/files/dhcp/dnsmasq.Containerfile"))
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
dhcp_container_file = {
|
||||
path = "/etc/containers/systemd/dnsmasq_container.container"
|
||||
user = {id = 0}
|
||||
group = {id = 0}
|
||||
mode = 420 # 0644
|
||||
contents = {
|
||||
source = format(
|
||||
"data:text/plain;base64,%s",
|
||||
base64encode(file("${path.module}/files/dhcp/dnsmasq_container.container"))
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
dhcp_filesystems = [
|
||||
local.dhcp_data_filesystem,
|
||||
]
|
||||
|
||||
dhcp_directories = [
|
||||
local.dhcp_builddir_dir,
|
||||
]
|
||||
|
||||
dhcp_files = [
|
||||
local.dhcp_data_volume_file,
|
||||
local.dnsmasq_base_config_file,
|
||||
local.generate_dhcp_options_script_file,
|
||||
local.dhcp_containerfile_file,
|
||||
local.dhcp_container_file,
|
||||
]
|
||||
|
||||
dhcp_systemd_units = [
|
||||
local.dhcp_config_path_systemd_unit,
|
||||
local.dhcp_config_service_systemd_unit,
|
||||
]
|
||||
}
|
16
modules/netboot_server/files/caddy/Caddyfile
Normal file
16
modules/netboot_server/files/caddy/Caddyfile
Normal file
|
@ -0,0 +1,16 @@
|
|||
:80 {
|
||||
handle_path /isos/* {
|
||||
root * /srv/isos
|
||||
file_server
|
||||
}
|
||||
handle_path /config/* {
|
||||
root * /srv/config/writable
|
||||
file_server
|
||||
}
|
||||
handle_path /ipxe/* {
|
||||
root * /srv/ipxe
|
||||
file_server
|
||||
}
|
||||
error * 404
|
||||
log
|
||||
}
|
5
modules/netboot_server/files/caddy/caddy.Containerfile
Normal file
5
modules/netboot_server/files/caddy/caddy.Containerfile
Normal file
|
@ -0,0 +1,5 @@
|
|||
FROM docker.io/caddy:2.8
|
||||
COPY Caddyfile /etc/caddy/
|
||||
RUN mkdir -p /srv/ipxe
|
||||
COPY ipxe.script /srv/ipxe/
|
||||
EXPOSE 80
|
22
modules/netboot_server/files/caddy/caddy.container
Normal file
22
modules/netboot_server/files/caddy/caddy.container
Normal file
|
@ -0,0 +1,22 @@
|
|||
[Unit]
|
||||
Description = HTTP Server (Caddy)
|
||||
Wants = network-online.target
|
||||
After = network-online.target
|
||||
Wants=ign_files_init.service
|
||||
After=ign_files_init.service
|
||||
|
||||
[Container]
|
||||
ContainerName = caddy
|
||||
Image=localhost/caddy:2.8
|
||||
Volume=caddy_data.volume:/data:z
|
||||
Volume=fcos_images.volume:/srv/isos:ro,z
|
||||
Volume=ign_files.volume:/srv/config:ro,z
|
||||
PublishPort=80:80/tcp
|
||||
|
||||
[Service]
|
||||
WorkingDirectory=/var/roothome/caddy/
|
||||
ExecStartPre=/usr/bin/podman build -t caddy:2.8 .
|
||||
Restart=on-failure
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
8
modules/netboot_server/files/caddy/caddy_data.volume
Normal file
8
modules/netboot_server/files/caddy/caddy_data.volume
Normal file
|
@ -0,0 +1,8 @@
|
|||
[Unit]
|
||||
Description = Caddy Data Volume
|
||||
|
||||
[Volume]
|
||||
VolumeName = caddy_data
|
||||
Device=/dev/disk/by-label/caddy_data
|
||||
Options=nodev,noexec,nosuid,rootcontext=system_u:object_r:container_file_t:s0
|
||||
Type=ext4
|
8
modules/netboot_server/files/caddy/fcos_images.volume
Normal file
8
modules/netboot_server/files/caddy/fcos_images.volume
Normal file
|
@ -0,0 +1,8 @@
|
|||
[Unit]
|
||||
Description = FCOS Image Volume
|
||||
|
||||
[Volume]
|
||||
VolumeName = fcos_images
|
||||
Device=/dev/disk/by-label/fcos_images
|
||||
Options=nodev,noexec,nosuid,rootcontext=system_u:object_r:container_file_t:s0
|
||||
Type=ext4
|
|
@ -0,0 +1,12 @@
|
|||
[Unit]
|
||||
Description = Download Latest FCOS Image
|
||||
|
||||
[Container]
|
||||
ContainerName = fcos_downloader
|
||||
Image = image_downloader.image
|
||||
Exec = download -s stable -f pxe
|
||||
Volume = fcos_images.volume:/data:z
|
||||
WorkingDir = /data
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
|
@ -0,0 +1,9 @@
|
|||
[Unit]
|
||||
Description = FCOS Container Image
|
||||
|
||||
Wants=network-online.target
|
||||
After=network-online.target
|
||||
|
||||
[Image]
|
||||
Image = quay.io/coreos/coreos-installer:release
|
||||
TLSVerify = true
|
13
modules/netboot_server/files/caddy/ipxe.script
Normal file
13
modules/netboot_server/files/caddy/ipxe.script
Normal file
|
@ -0,0 +1,13 @@
|
|||
#!ipxe
|
||||
|
||||
set BASEURL ${128.1:string}
|
||||
set CONFIGURL ${BASEURL}config/${128.2:string}
|
||||
set INSTALLDEV ${128.3:string}
|
||||
set KERNEL ${BASEURL}isos/${129.1:string}
|
||||
set INITRAMFS ${BASEURL}isos/${129.2:string}
|
||||
set ROOTFS ${BASEURL}isos/${129.3:string}
|
||||
|
||||
kernel ${KERNEL} initrd=main coreos.live.rootfs_url=${ROOTFS} coreos.inst.install_dev=${INSTALLDEV} coreos.inst.ignition_url=${CONFIGURL}
|
||||
initrd --name main ${INITRAMFS}
|
||||
|
||||
boot
|
9
modules/netboot_server/files/dhcp/dhcp_config.path.tftpl
Normal file
9
modules/netboot_server/files/dhcp/dhcp_config.path.tftpl
Normal file
|
@ -0,0 +1,9 @@
|
|||
[Unit]
|
||||
Description = Path Monitor for DHCP Config
|
||||
|
||||
[Path]
|
||||
PathChanged=${path}
|
||||
TriggerLimitIntervalSec=0
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
6
modules/netboot_server/files/dhcp/dhcp_config.service
Normal file
6
modules/netboot_server/files/dhcp/dhcp_config.service
Normal file
|
@ -0,0 +1,6 @@
|
|||
[Unit]
|
||||
Description = Restart DNSMasq Service
|
||||
|
||||
[Service]
|
||||
Type=oneshot
|
||||
ExecStart=/usr/bin/systemctl restart dnsmasq_container.service
|
8
modules/netboot_server/files/dhcp/dhcp_data.volume
Normal file
8
modules/netboot_server/files/dhcp/dhcp_data.volume
Normal file
|
@ -0,0 +1,8 @@
|
|||
[Unit]
|
||||
Description = DHCP Data Volume
|
||||
|
||||
[Volume]
|
||||
VolumeName = dhcp_data
|
||||
Device=/dev/disk/by-label/dhcp_data
|
||||
Options=nodev,noexec,nosuid,rootcontext=system_u:object_r:container_file_t:s0
|
||||
Type=ext4
|
6
modules/netboot_server/files/dhcp/dnsmasq.Containerfile
Normal file
6
modules/netboot_server/files/dhcp/dnsmasq.Containerfile
Normal file
|
@ -0,0 +1,6 @@
|
|||
FROM docker.io/alpine:3.19.1
|
||||
RUN apk add dnsmasq
|
||||
COPY dnsmasq.conf /etc/dnsmasq.conf
|
||||
COPY dhcp-options /etc/dnsmasq.options
|
||||
EXPOSE 67/udp
|
||||
ENTRYPOINT ["/usr/sbin/dnsmasq", "--conf-file=/etc/dnsmasq.conf"]
|
22
modules/netboot_server/files/dhcp/dnsmasq.conf.tftpl
Normal file
22
modules/netboot_server/files/dhcp/dnsmasq.conf.tftpl
Normal file
|
@ -0,0 +1,22 @@
|
|||
conf-file=/etc/dnsmasq.options
|
||||
conf-dir=${config_extension_dir},*.conf
|
||||
|
||||
port=0 # disables DNS feature
|
||||
keep-in-foreground # keep in foreground to prevent exit of the container
|
||||
|
||||
dhcp-range=${dhcp_range},static,${dhcp_range_netmask}
|
||||
dhcp-option=option:router,${dhcp_router}
|
||||
dhcp-option=encap:128,1,"http://${dhcp_server_ip_addr}/"
|
||||
dhcp-boot=http://${dhcp_server_ip_addr}/ipxe/ipxe.script
|
||||
|
||||
dhcp-lease-max=150
|
||||
dhcp-leasefile=/data/dnsmasq.leases
|
||||
|
||||
log-dhcp
|
||||
|
||||
|
||||
#Host Example
|
||||
#dhcp-host=,id:sample,set:sampletag,10.109.0.10,sample
|
||||
#dhcp-option=tag:sampletag,encap:128,2,"sample.ign"
|
||||
#dhcp-option=tag:sampletag,encap:128,3,"/dev/disk/by-path/0000:00:00.0"
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
[Unit]
|
||||
Description = DHCP Container
|
||||
|
||||
Wants=image_downloader.service
|
||||
After=image_downloader.service
|
||||
Wants=network-online.target
|
||||
After=network-online.target
|
||||
Wants=dhcp_config_init.service
|
||||
After=dhcp_config_init.service
|
||||
|
||||
[Container]
|
||||
ContainerName = dnsmasq_container
|
||||
Image = localhost/dnsmasq:latest
|
||||
Volume = dhcp_config.volume:/etc/dnsmasq.d:z
|
||||
Volume = dhcp_data.volume:/data:z
|
||||
Volume = /dev/log:/dev/log
|
||||
Network = host
|
||||
AddCapability = CAP_NET_ADMIN,CAP_NET_RAW
|
||||
|
||||
[Service]
|
||||
WorkingDirectory=/var/roothome/dhcp
|
||||
ExecStartPre=/bin/bash /var/roothome/generate_dhcp_options.sh
|
||||
ExecStartPre=/usr/bin/podman build -t dnsmasq:latest .
|
||||
Restart=on-failure
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
26
modules/netboot_server/files/dhcp/generate_dhcp_options.sh
Normal file
26
modules/netboot_server/files/dhcp/generate_dhcp_options.sh
Normal file
|
@ -0,0 +1,26 @@
|
|||
#!/bin/bash
|
||||
|
||||
set -o errexit -o nounset -o pipefail -o xtrace
|
||||
|
||||
declare -r MOUNTDIR="$(mktemp -d)"
|
||||
|
||||
cleanstep1() {
|
||||
rm -r "${MOUNTDIR}"
|
||||
}
|
||||
trap cleanstep1 EXIT
|
||||
|
||||
mount -t ext4 -o noexec,nosuid,nodev,rootcontext=system_u:object_r:container_file_t:s0 LABEL=fcos_images "${MOUNTDIR}"
|
||||
|
||||
cleanstep2() {
|
||||
umount -f "${MOUNTDIR}"
|
||||
cleanstep1
|
||||
}
|
||||
trap cleanstep2 EXIT
|
||||
|
||||
declare -r KERNEL_FILE="$(basename "$(ls -1 "${MOUNTDIR}"/fedora-coreos-*-live-kernel-x86_64)")"
|
||||
declare -r INITRAMFS_FILE="$(basename "$(ls -1 "${MOUNTDIR}"/fedora-coreos-*-live-initramfs.x86_64.img)")"
|
||||
declare -r ROOTFS_FILE="$(basename "$(ls -1 "${MOUNTDIR}"/fedora-coreos-*-live-rootfs.x86_64.img)")"
|
||||
|
||||
echo "dhcp-option=encap:129,1,\"${KERNEL_FILE}\"" > /root/dhcp/dhcp-options
|
||||
echo "dhcp-option=encap:129,2,\"${INITRAMFS_FILE}\"" >> /root/dhcp/dhcp-options
|
||||
echo "dhcp-option=encap:129,3,\"${ROOTFS_FILE}\"" >> /root/dhcp/dhcp-options
|
10
modules/netboot_server/files/dhcp_nmconnection.tftpl
Normal file
10
modules/netboot_server/files/dhcp_nmconnection.tftpl
Normal file
|
@ -0,0 +1,10 @@
|
|||
[connection]
|
||||
id=${iface}
|
||||
type=ethernet
|
||||
interface-name=${iface}
|
||||
[ipv4]
|
||||
address1=${ip_address}/${netmask},${gateway}
|
||||
dns=${dns_server};
|
||||
dns-search=
|
||||
may-fail=false
|
||||
method=manual
|
23
modules/netboot_server/files/sftp/Containerfile.tftpl
Normal file
23
modules/netboot_server/files/sftp/Containerfile.tftpl
Normal file
|
@ -0,0 +1,23 @@
|
|||
FROM docker.io/alpine:3.19.1
|
||||
RUN apk add openssh-server bash
|
||||
COPY sshd_config /etc/ssh/sshd_config
|
||||
RUN /bin/bash -c "\
|
||||
%{for idx, chroot_user in chrooted_users ~}
|
||||
addgroup -g $((2000 + ${idx})) ${chroot_user.username} && \
|
||||
adduser -D -G ${chroot_user.username} -u $((2000 + ${idx})) ${chroot_user.username} && \
|
||||
echo '${chroot_user.username}:*' | chpasswd -e && \
|
||||
mkdir -p ${chroot_user.chroot} && \
|
||||
chown root:root ${chroot_user.chroot} && \
|
||||
chmod 0755 ${chroot_user.chroot} && \
|
||||
mkdir /home/${chroot_user.username}/.ssh && \
|
||||
chown ${chroot_user.username}:${chroot_user.username} /home/${chroot_user.username}/.ssh && \
|
||||
chmod 0700 /home/${chroot_user.username}/.ssh && \
|
||||
touch /home/${chroot_user.username}/.ssh/authorized_keys && \
|
||||
chown ${chroot_user.username}:${chroot_user.username} /home/${chroot_user.username}/.ssh/authorized_keys && \
|
||||
chmod 0600 /home/${chroot_user.username}/.ssh/authorized_keys && \
|
||||
echo '${chroot_user.ssh_public_key}' > /home/${chroot_user.username}/.ssh/authorized_keys && \
|
||||
%{endfor ~}
|
||||
:"
|
||||
|
||||
EXPOSE 22/tcp
|
||||
ENTRYPOINT ["/usr/sbin/sshd", "-D", "-f", "/etc/ssh/sshd_config"]
|
8
modules/netboot_server/files/sftp/dhcp_config.volume
Normal file
8
modules/netboot_server/files/sftp/dhcp_config.volume
Normal file
|
@ -0,0 +1,8 @@
|
|||
[Unit]
|
||||
Description = DHCP Config Volume
|
||||
|
||||
[Volume]
|
||||
VolumeName = dhcp_config
|
||||
Device=/dev/disk/by-label/dhcp_config
|
||||
Options=nodev,noexec,nosuid,rootcontext=system_u:object_r:container_file_t:s0
|
||||
Type=ext4
|
20
modules/netboot_server/files/sftp/dhcp_config_init.container
Normal file
20
modules/netboot_server/files/sftp/dhcp_config_init.container
Normal file
|
@ -0,0 +1,20 @@
|
|||
[Unit]
|
||||
Description = Initialize dhcp_config dirs
|
||||
|
||||
Wants=network-online.target
|
||||
After=network-online.target
|
||||
|
||||
[Container]
|
||||
ContainerName = dhcp_config_init
|
||||
Image = localhost/sftp:latest
|
||||
Volume = dhcp_config.volume:/blip:z
|
||||
Entrypoint = /bin/sh
|
||||
Exec = -c "mkdir -p /blip/writable ; chown root:terraform_dhcp /blip/writable ; chmod 0775 /blip/writable"
|
||||
|
||||
[Service]
|
||||
Restart=on-failure
|
||||
WorkingDirectory=/var/roothome/sftp
|
||||
ExecStartPre=podman build -t sftp:latest .
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
8
modules/netboot_server/files/sftp/ign_files.volume
Normal file
8
modules/netboot_server/files/sftp/ign_files.volume
Normal file
|
@ -0,0 +1,8 @@
|
|||
[Unit]
|
||||
Description = Ignition File Volume
|
||||
|
||||
[Volume]
|
||||
VolumeName = ign_files
|
||||
Device=/dev/disk/by-label/ign_files
|
||||
Options=nodev,noexec,nosuid,rootcontext=system_u:object_r:container_file_t:s0
|
||||
Type=ext4
|
20
modules/netboot_server/files/sftp/ign_files_init.container
Normal file
20
modules/netboot_server/files/sftp/ign_files_init.container
Normal file
|
@ -0,0 +1,20 @@
|
|||
[Unit]
|
||||
Description = Initialize ign_files dirs
|
||||
|
||||
Wants=network-online.target
|
||||
After=network-online.target
|
||||
|
||||
[Container]
|
||||
ContainerName = ign_files_init
|
||||
Image = localhost/sftp:latest
|
||||
Volume = ign_files.volume:/blip:z
|
||||
Entrypoint = /bin/sh
|
||||
Exec = -c "mkdir -p /blip/writable ; chown root:terraform_ignition /blip/writable ; chmod 0775 /blip/writable"
|
||||
|
||||
[Service]
|
||||
Restart=on-failure
|
||||
WorkingDirectory=/var/roothome/sftp
|
||||
ExecStartPre=podman build -t sftp:latest .
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
29
modules/netboot_server/files/sftp/sftp.container.tftpl
Normal file
29
modules/netboot_server/files/sftp/sftp.container.tftpl
Normal file
|
@ -0,0 +1,29 @@
|
|||
[Unit]
|
||||
Description = SFTP Server
|
||||
|
||||
Wants=sftp_init_keys.service
|
||||
After=sftp_init_keys.service
|
||||
Wants=network-online.target
|
||||
After=network-online.target
|
||||
|
||||
Wants=dhcp_config_init.service
|
||||
After=dhcp_config_init.service
|
||||
Wants=ign_files_init.service
|
||||
After=ign_files_init.service
|
||||
|
||||
[Container]
|
||||
ContainerName = sftp
|
||||
Image = localhost/sftp:latest
|
||||
PublishPort=${external_port}:${internal_port}
|
||||
Volume = dhcp_config.volume:/data/dhcp_config:z
|
||||
Volume = ign_files.volume:/data/ign_files:z
|
||||
Volume = ssh_keys.volume:/data/ssh_keys:z
|
||||
Volume = /dev/log:/dev/log
|
||||
|
||||
[Service]
|
||||
WorkingDirectory=/var/roothome/sftp
|
||||
ExecStartPre=podman build -t sftp:latest .
|
||||
Restart=on-failure
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
20
modules/netboot_server/files/sftp/sftp_init_keys.container
Normal file
20
modules/netboot_server/files/sftp/sftp_init_keys.container
Normal file
|
@ -0,0 +1,20 @@
|
|||
[Unit]
|
||||
Description = SFTP Key Initialisation
|
||||
|
||||
Wants=network-online.target
|
||||
After=network-online.target
|
||||
|
||||
[Container]
|
||||
ContainerName = sftp_init_keys
|
||||
Image = localhost/sftp:latest
|
||||
Volume = ssh_keys.volume:/data/ssh_keys:z
|
||||
Entrypoint = /bin/sh
|
||||
Exec = -c "[ -f /data/ssh_keys/ssh_host_ed25519_key ] || ssh-keygen -N '' -f /data/ssh_keys/ssh_host_ed25519_key -t ed25519"
|
||||
|
||||
[Service]
|
||||
WorkingDirectory=/var/roothome/sftp
|
||||
ExecStartPre=podman build -t sftp:latest .
|
||||
Restart=on-failure
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
8
modules/netboot_server/files/sftp/ssh_keys.volume
Normal file
8
modules/netboot_server/files/sftp/ssh_keys.volume
Normal file
|
@ -0,0 +1,8 @@
|
|||
[Unit]
|
||||
Description = SSH Keys
|
||||
|
||||
[Volume]
|
||||
VolumeName = ssh_keys
|
||||
Device=/dev/disk/by-label/ssh_keys
|
||||
Options=nodev,noexec,nosuid,rootcontext=system_u:object_r:container_file_t:s0
|
||||
Type=ext4
|
263
modules/netboot_server/main.tf
Normal file
263
modules/netboot_server/main.tf
Normal file
|
@ -0,0 +1,263 @@
|
|||
terraform {
|
||||
required_providers {
|
||||
proxmox = {
|
||||
source = "bpg/proxmox",
|
||||
version = "~>0.56.1"
|
||||
}
|
||||
random = {
|
||||
source = "hashicorp/random"
|
||||
}
|
||||
local = {
|
||||
source = "hashicorp/local"
|
||||
}
|
||||
}
|
||||
required_version = ">=1.6.2"
|
||||
}
|
||||
|
||||
module "sshd" {
|
||||
source = "../sshd"
|
||||
address_family = "inet"
|
||||
}
|
||||
|
||||
locals {
|
||||
data_device_path = "/dev/disk/by-path/pci-0000:00:0a.0"
|
||||
|
||||
data_disk = {
|
||||
device = local.data_device_path
|
||||
partitions = [
|
||||
{
|
||||
label = "caddy_data"
|
||||
number = 1
|
||||
startMiB = 0
|
||||
sizeMiB = 100
|
||||
typeGuid = "0FC63DAF-8483-4772-8E79-3D69D8477DE4"
|
||||
resize = true
|
||||
},
|
||||
{
|
||||
label = "dhcp_config"
|
||||
number = 2
|
||||
startMiB = 0
|
||||
sizeMiB = 10
|
||||
typeGuid= "0FC63DAF-8483-4772-8E79-3D69D8477DE4"
|
||||
resize = true
|
||||
},
|
||||
{
|
||||
label = "dhcp_data"
|
||||
number = 3
|
||||
startMiB = 0
|
||||
sizeMiB = 10
|
||||
typeGuid= "0FC63DAF-8483-4772-8E79-3D69D8477DE4"
|
||||
resize = true
|
||||
},
|
||||
{
|
||||
label = "fcos_images"
|
||||
number = 4
|
||||
startMiB = 0
|
||||
sizeMiB = 8192
|
||||
typeGuid= "0FC63DAF-8483-4772-8E79-3D69D8477DE4"
|
||||
resize = true
|
||||
},
|
||||
{
|
||||
label = "ign_files"
|
||||
number = 5
|
||||
startMiB = 0
|
||||
sizeMiB = 512
|
||||
typeGuid= "0FC63DAF-8483-4772-8E79-3D69D8477DE4"
|
||||
resize = true
|
||||
},
|
||||
{
|
||||
label = "ssh_keys"
|
||||
number = 6
|
||||
startMiB = 0
|
||||
sizeMiB = 10
|
||||
typeGuid= "0FC63DAF-8483-4772-8E79-3D69D8477DE4"
|
||||
resize = true
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
hostname_file = {
|
||||
path = "/etc/hostname"
|
||||
user = {id = 0}
|
||||
group = {id = 0}
|
||||
mode = 420 #0644
|
||||
contents = {
|
||||
source = format(
|
||||
"data:text/plain;base64,%s",
|
||||
base64encode(var.hostname),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
network_config_file = {
|
||||
path = "/etc/NetworkManager/system-connections/${var.dhcp_iface}.nmconnection"
|
||||
user = {id = 0}
|
||||
group = {id = 0}
|
||||
mode = 384 #0600
|
||||
contents = {
|
||||
source = format(
|
||||
"data:text/plain;base64,%s",
|
||||
base64encode(templatefile(
|
||||
"${path.module}/files/dhcp_nmconnection.tftpl",
|
||||
{
|
||||
iface = var.dhcp_iface
|
||||
ip_address = var.dhcp_server_ip_addr
|
||||
netmask = split("/", var.dhcp_range)[1]
|
||||
gateway = var.dhcp_gateway
|
||||
dns_server = var.dhcp_gateway
|
||||
}
|
||||
))
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
core_user = {
|
||||
name = "core"
|
||||
passwordHash = "$6$vDMAZf/yOO6mEbcs$6VE7WD8T9/PeotszMFxatOQxB/rFmLDWsNajg4sI0O47OikSuVpqPjkxRbzcueiXn6rBUY1ubCHlp0nnoZ1VI1" # password is "tititoto"; only there for debug; please remove in prod
|
||||
sshAuthorizedKeys = [
|
||||
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIFQnLSYLGzUVmDMMGgEKCNgfAOkIuqhOMGGuvgskACum fmaury@2a01cb00142b3d00ee15f742996f2775.ipv6.abo.wanadoo.fr"
|
||||
]
|
||||
}
|
||||
|
||||
ignition_config = jsonencode({
|
||||
ignition = {
|
||||
version = "3.4.0"
|
||||
}
|
||||
storage = {
|
||||
disks = [
|
||||
local.data_disk,
|
||||
]
|
||||
filesystems = concat(
|
||||
local.dhcp_filesystems,
|
||||
local.caddy_filesystems,
|
||||
local.sftp_filesystems,
|
||||
)
|
||||
directories = concat(
|
||||
local.dhcp_directories,
|
||||
local.caddy_directories,
|
||||
local.sftp_directories,
|
||||
)
|
||||
files = concat(
|
||||
[
|
||||
local.hostname_file,
|
||||
local.network_config_file,
|
||||
],
|
||||
module.sshd.files,
|
||||
local.dhcp_files,
|
||||
local.caddy_files,
|
||||
local.sftp_files,
|
||||
)
|
||||
}
|
||||
systemd = {
|
||||
units = concat(
|
||||
local.dhcp_systemd_units,
|
||||
local.caddy_systemd_units,
|
||||
module.sshd.systemd_units,
|
||||
)
|
||||
}
|
||||
passwd = {
|
||||
users = concat(
|
||||
[
|
||||
local.core_user
|
||||
],
|
||||
module.sshd.users,
|
||||
)
|
||||
groups = module.sshd.groups
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
resource "random_pet" "config_name" {
|
||||
length = 4
|
||||
}
|
||||
|
||||
locals {
|
||||
generated_ignition_config_file = "netboot_server_ignition_config_${random_pet.config_name.id}.ign"
|
||||
}
|
||||
|
||||
resource "local_file" "api_token" {
|
||||
content = "Authorization: PVEAPIToken=${var.pve_api_token}"
|
||||
filename = "pve_api_token"
|
||||
file_permission = "0600"
|
||||
}
|
||||
|
||||
resource "local_file" "netboot_server_ignition_config" {
|
||||
depends_on = [ local_file.api_token ]
|
||||
content = local.ignition_config
|
||||
filename = format("${path.module}/%s", local.generated_ignition_config_file)
|
||||
file_permission = "0644"
|
||||
|
||||
# Download ISO to customize
|
||||
provisioner "local-exec" {
|
||||
command = <<EOT
|
||||
podman run --security-opt label=disable --pull=always --rm -v ${path.cwd}/${path.module}:/data -w /data \
|
||||
quay.io/coreos/coreos-installer:release download -f iso
|
||||
EOT
|
||||
}
|
||||
|
||||
# Customize ISO
|
||||
provisioner "local-exec" {
|
||||
environment = {
|
||||
KERNEL_ARG = "--live-karg-append=coreos.liveiso.fromram"
|
||||
IGNITION_ARG = "--live-ignition=./${local.generated_ignition_config_file}"
|
||||
}
|
||||
command = <<EOT
|
||||
rm -f ${path.module}/customized-${random_pet.config_name.id}.iso && \
|
||||
podman run --security-opt label=disable --pull=always --rm -v ${path.cwd}/${path.module}:/data -w /data \
|
||||
quay.io/coreos/coreos-installer:release \
|
||||
iso customize $KERNEL_ARG $IGNITION_ARG \
|
||||
-o customized-${random_pet.config_name.id}.iso $(basename $(ls -1 ${path.module}/fedora-coreos-*-live.x86_64.iso))
|
||||
EOT
|
||||
}
|
||||
|
||||
provisioner "local-exec" {
|
||||
command = <<EOT
|
||||
curl \
|
||||
-F "content=iso" \
|
||||
-F "filename=@${path.module}/customized-${random_pet.config_name.id}.iso;type=application/vnd.efi.iso;filename=fcos-netboot-server-${random_pet.config_name.id}.iso" \
|
||||
-H "@${local_file.api_token.filename}" \
|
||||
"${var.pve_api_base_url}api2/json/nodes/${var.pve_node_name}/storage/${var.pve_storage_id}/upload"
|
||||
EOT
|
||||
}
|
||||
}
|
||||
|
||||
resource "proxmox_virtual_environment_vm" "netboot_server" {
|
||||
name = "netboot-server"
|
||||
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
|
||||
}
|
||||
|
||||
cdrom {
|
||||
enabled = true
|
||||
file_id = "${var.pve_storage_id}:iso/fcos-netboot-server-${random_pet.config_name.id}.iso"
|
||||
}
|
||||
|
||||
disk {
|
||||
datastore_id = var.pve_storage_id
|
||||
interface = "virtio0"
|
||||
size = 10
|
||||
}
|
||||
|
||||
network_device {
|
||||
bridge = var.prod_network_name
|
||||
model = "virtio"
|
||||
}
|
||||
|
||||
operating_system {
|
||||
type = "l26"
|
||||
}
|
||||
|
||||
keyboard_layout = "fr"
|
||||
vga {}
|
||||
serial_device{}
|
||||
}
|
0
modules/netboot_server/outputs.tf
Normal file
0
modules/netboot_server/outputs.tf
Normal file
191
modules/netboot_server/sftp.tf
Normal file
191
modules/netboot_server/sftp.tf
Normal file
|
@ -0,0 +1,191 @@
|
|||
module "sftp" {
|
||||
source = "../sshd"
|
||||
base_config_dir = "/var/roothome/sftp"
|
||||
use_socket_activation = true
|
||||
address_family = "inet"
|
||||
listen_port = 22
|
||||
sftp_only = true
|
||||
chrooted_users = local.chrooted_users
|
||||
host_keys = ["/data/ssh_keys/ssh_host_ed25519_key"]
|
||||
}
|
||||
|
||||
locals {
|
||||
sftp_keys_filesystem = {
|
||||
device = "${local.data_device_path}-part6"
|
||||
format = "ext4"
|
||||
label = "ssh_keys"
|
||||
}
|
||||
|
||||
chrooted_users = [
|
||||
{
|
||||
username = "terraform_dhcp"
|
||||
chroot = "/data/dhcp_config"
|
||||
ssh_public_key = var.ssh_public_key_opentofu_netboot_server
|
||||
},
|
||||
{
|
||||
username = "terraform_ignition"
|
||||
chroot = "/data/ign_files"
|
||||
ssh_public_key = var.ssh_public_key_opentofu_netboot_server
|
||||
}
|
||||
]
|
||||
|
||||
sftp_build_dir = {
|
||||
path = "/var/roothome/sftp"
|
||||
user = {id = 0}
|
||||
group = {id = 0}
|
||||
mode = 448 # 0700
|
||||
}
|
||||
|
||||
sftp_containerfile_file = {
|
||||
path = "/var/roothome/sftp/Containerfile"
|
||||
user = {id = 0}
|
||||
group = {id = 0}
|
||||
mode = 420 # 0644
|
||||
contents = {
|
||||
source = format(
|
||||
"data:text/plain;base64,%s",
|
||||
base64encode(templatefile(
|
||||
"${path.module}/files/sftp/Containerfile.tftpl",
|
||||
{
|
||||
chrooted_users = local.chrooted_users
|
||||
}
|
||||
))
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
sftp_keys_volume_file = {
|
||||
path = "/etc/containers/systemd/ssh_keys.volume"
|
||||
user = {id = 0}
|
||||
group = {id = 0}
|
||||
mode = 420 # 0644
|
||||
contents = {
|
||||
source = format(
|
||||
"data:text/plain;base64,%s",
|
||||
base64encode(file("${path.module}/files/sftp/ssh_keys.volume"))
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
sftp_init_keys_container_file = {
|
||||
path = "/etc/containers/systemd/sftp_init_keys.container"
|
||||
user = {id = 0}
|
||||
group = {id = 0}
|
||||
mode = 420 # 0644
|
||||
contents = {
|
||||
source = format(
|
||||
"data:text/plain;base64,%s",
|
||||
base64encode(file("${path.module}/files/sftp/sftp_init_keys.container"))
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
sftp_container_file = {
|
||||
path = "/etc/containers/systemd/sftp.container"
|
||||
user = {id = 0}
|
||||
group = {id = 0}
|
||||
mode = 420 # 0644
|
||||
contents = {
|
||||
source = format(
|
||||
"data:text/plain;base64,%s",
|
||||
base64encode(templatefile(
|
||||
"${path.module}/files/sftp/sftp.container.tftpl",
|
||||
{
|
||||
internal_port = 22
|
||||
external_port = 2222
|
||||
}
|
||||
))
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
dhcp_config_filesystem = {
|
||||
device = "${local.data_device_path}-part2"
|
||||
format = "ext4"
|
||||
label = "dhcp_config"
|
||||
}
|
||||
|
||||
sftp_dhcp_config_init_container = {
|
||||
path = "/etc/containers/systemd/dhcp_config_init.container"
|
||||
user = {id = 0}
|
||||
group = {id = 0}
|
||||
mode = 420 # 0644
|
||||
contents = {
|
||||
source = format(
|
||||
"data:text/plain;base64,%s",
|
||||
base64encode(file("${path.module}/files/sftp/dhcp_config_init.container"))
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
sftp_dhcp_config_volume_file = {
|
||||
path = "/etc/containers/systemd/dhcp_config.volume"
|
||||
user = {id = 0}
|
||||
group = {id = 0}
|
||||
mode = 420 # 0644
|
||||
contents = {
|
||||
source = format(
|
||||
"data:text/plain;base64,%s",
|
||||
base64encode(file("${path.module}/files/sftp/dhcp_config.volume"))
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
ignition_files_filesystem = {
|
||||
device = "${local.data_device_path}-part5"
|
||||
format = "ext4"
|
||||
label = "ign_files"
|
||||
}
|
||||
|
||||
sftp_ignition_files_init_container = {
|
||||
path = "/etc/containers/systemd/ign_files_init.container"
|
||||
user = {id = 0}
|
||||
group = {id = 0}
|
||||
mode = 420 # 0644
|
||||
contents = {
|
||||
source = format(
|
||||
"data:text/plain;base64,%s",
|
||||
base64encode(file("${path.module}/files/sftp/ign_files_init.container"))
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
sftp_ignition_files_volume_file = {
|
||||
path = "/etc/containers/systemd/ign_files.volume"
|
||||
user = {id = 0}
|
||||
group = {id = 0}
|
||||
mode = 420 # 0644
|
||||
contents = {
|
||||
source = format(
|
||||
"data:text/plain;base64,%s",
|
||||
base64encode(file("${path.module}/files/sftp/ign_files.volume"))
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
sftp_filesystems = [
|
||||
local.sftp_keys_filesystem,
|
||||
local.dhcp_config_filesystem,
|
||||
local.ignition_files_filesystem,
|
||||
]
|
||||
|
||||
sftp_directories = [
|
||||
local.sftp_build_dir,
|
||||
]
|
||||
|
||||
sftp_files = concat(
|
||||
[
|
||||
local.sftp_keys_volume_file,
|
||||
local.sftp_init_keys_container_file,
|
||||
local.sftp_container_file,
|
||||
local.sftp_containerfile_file,
|
||||
local.sftp_dhcp_config_init_container,
|
||||
local.sftp_dhcp_config_volume_file,
|
||||
local.sftp_ignition_files_init_container,
|
||||
local.sftp_ignition_files_volume_file,
|
||||
],
|
||||
module.sftp.files
|
||||
)
|
||||
|
||||
# we can safely ignore the systemd units and users since all of them go in the container and we already took care of it in the Containerfile
|
||||
}
|
78
modules/netboot_server/variables.tf
Normal file
78
modules/netboot_server/variables.tf
Normal file
|
@ -0,0 +1,78 @@
|
|||
variable "pve_api_base_url" {
|
||||
type = string
|
||||
nullable = false
|
||||
}
|
||||
|
||||
variable "pve_api_token" {
|
||||
type = string
|
||||
nullable = false
|
||||
sensitive = true
|
||||
}
|
||||
|
||||
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 "prod_network_name" {
|
||||
type = string
|
||||
nullable = false
|
||||
}
|
||||
|
||||
variable "dhcp_server_ip_addr" {
|
||||
type = string
|
||||
nullable = false
|
||||
validation {
|
||||
condition = can(cidrnetmask("${var.dhcp_server_ip_addr}/32"))
|
||||
error_message = "Invalid DHCP server address."
|
||||
}
|
||||
}
|
||||
|
||||
variable "dhcp_iface" {
|
||||
type = string
|
||||
nullable = false
|
||||
}
|
||||
|
||||
variable "dhcp_gateway" {
|
||||
type = string
|
||||
nullable = false
|
||||
validation {
|
||||
condition = can(cidrnetmask("${var.dhcp_gateway}/32"))
|
||||
error_message = "Invalid gateway"
|
||||
}
|
||||
}
|
||||
|
||||
variable "dhcp_range" {
|
||||
type = string
|
||||
nullable = false
|
||||
validation {
|
||||
condition = can(cidrnetmask(var.dhcp_range))
|
||||
error_message = "Invalid DHCP range."
|
||||
}
|
||||
}
|
||||
|
||||
variable "ssh_public_key_opentofu_netboot_server" {
|
||||
type = string
|
||||
nullable = false
|
||||
}
|
||||
|
||||
variable "fcos_image_version" {
|
||||
type = string
|
||||
nullable = false
|
||||
default = "40.20240504.3.0"
|
||||
}
|
||||
|
||||
variable "hostname" {
|
||||
type = string
|
||||
nullable = false
|
||||
}
|
3
modules/poc/files/dhcp_config.conf.tftpl
Normal file
3
modules/poc/files/dhcp_config.conf.tftpl
Normal file
|
@ -0,0 +1,3 @@
|
|||
dhcp-host=${mac_address},set:vm_id_${vm_id},${host_ip},${hostname}
|
||||
dhcp-option=tag:vm_id_${vm_id},encap:128,2,"${vm_id}.ign"
|
||||
dhcp-option=tag:vm_id_${vm_id},encap:128,3,"/dev/disk/by-path/pci-0000:00:0a.0"
|
171
modules/poc/main.tf
Normal file
171
modules/poc/main.tf
Normal file
|
@ -0,0 +1,171 @@
|
|||
terraform {
|
||||
required_providers {
|
||||
proxmox = {
|
||||
source = "bpg/proxmox"
|
||||
version = "~>0.56.1"
|
||||
}
|
||||
}
|
||||
required_version = ">=1.6.2"
|
||||
}
|
||||
|
||||
|
||||
locals {
|
||||
core_user = {
|
||||
name = "core"
|
||||
sshAuthorizedKeys = [
|
||||
var.admin_ssh_public_key
|
||||
]
|
||||
}
|
||||
|
||||
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}/poc_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}/poc_sftp_script_for_ignition_config_${random_pet.config_name.id}"
|
||||
file_permission = "0644"
|
||||
}
|
||||
|
||||
resource "local_file" "poc_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}/poc_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}/poc_dhcp_config_${random_pet.config_name.id}.conf ${var.pve_vm_id}.conf
|
||||
EOT
|
||||
filename = "${path.module}/poc_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
|
||||
hostname = var.instance_name
|
||||
host_ip = cidrhost(var.admin_network.prefix, var.pve_vm_id)
|
||||
mac_address = var.admin_network.mac_address
|
||||
}
|
||||
)
|
||||
filename = "${path.module}/poc_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}/poc_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" "poc" {
|
||||
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 = var.admin_network.name
|
||||
model = "virtio"
|
||||
mac_address = var.admin_network.mac_address
|
||||
}
|
||||
|
||||
boot_order = ["net0"]
|
||||
|
||||
operating_system {
|
||||
type = "l26"
|
||||
}
|
||||
|
||||
vga {}
|
||||
serial_device{}
|
||||
}
|
0
modules/poc/outputs.tf
Normal file
0
modules/poc/outputs.tf
Normal file
|
@ -0,0 +1,3 @@
|
|||
dhcp-host=1c:69:7a:ff:ff:01,set:vm_id_110,10.110.0.110,poc
|
||||
dhcp-option=tag:vm_id_110,encap:128,2,"110.ign"
|
||||
dhcp-option=tag:vm_id_110,encap:128,3,"/dev/disk/by-path/pci-0000:00:0a.0"
|
|
@ -0,0 +1,3 @@
|
|||
cd writable
|
||||
-rm 110.conf
|
||||
put modules/poc/poc_dhcp_config_apparently-morally-valid-quail.conf 110.conf
|
|
@ -0,0 +1,3 @@
|
|||
cd writable
|
||||
-rm 110.ign
|
||||
put modules/poc/poc_ignition_config_apparently-morally-valid-quail.ign 110.ign
|
73
modules/poc/variables.tf
Normal file
73
modules/poc/variables.tf
Normal file
|
@ -0,0 +1,73 @@
|
|||
variable "pve_node_name" {
|
||||
type = string
|
||||
nullable = false
|
||||
}
|
||||
|
||||
variable "pve_storage_id" {
|
||||
type = string
|
||||
nullable = false
|