From 91ed0b300cf88be8d7cb95b3f507d2fbc9198943 Mon Sep 17 00:00:00 2001 From: Petar Cubela Date: Tue, 27 Oct 2026 13:58:21 +0100 Subject: [PATCH] Add infra/ with student-vms OpenTofu root module - infra/pve: host facts, IP plan, one-time setup - infra/tofu/student-vms: 3 student VMs via tf-mod-pve v1.0.0 - infra/sandbox: template clones for throwaway test VMs - README/TODO: Windows DC/AD instead of FreeIPA, new network - .gitignore: only ignore secrets.auto.tfvars instead of all tfvars Co-Authored-By: Claude Opus 5.5 --- .gitignore | 10 ++--- README.md | 43 ++++++------------- TODO.md | 11 ++--- infra/pve/README.md | 41 ++++++++++++++++++ infra/sandbox/README.md | 13 ++++++ infra/tofu/student-vms/.terraform.lock.hcl | 36 ++++++++++++++++ infra/tofu/student-vms/README.md | 15 +++++++ infra/tofu/student-vms/providers.tf | 11 +++++ .../student-vms/secrets.auto.tfvars.example | 3 ++ infra/tofu/student-vms/variables.tf | 16 +++++++ infra/tofu/student-vms/versions.tf | 10 +++++ infra/tofu/student-vms/vms.tf | 32 ++++++++++++++ 12 files changed, 197 insertions(+), 44 deletions(-) create mode 100644 infra/pve/README.md create mode 100644 infra/sandbox/README.md create mode 100644 infra/tofu/student-vms/.terraform.lock.hcl create mode 100644 infra/tofu/student-vms/README.md create mode 100644 infra/tofu/student-vms/providers.tf create mode 100644 infra/tofu/student-vms/secrets.auto.tfvars.example create mode 100644 infra/tofu/student-vms/variables.tf create mode 100644 infra/tofu/student-vms/versions.tf create mode 100644 infra/tofu/student-vms/vms.tf diff --git a/.gitignore b/.gitignore index 519f09e..9522234 100644 --- a/.gitignore +++ b/.gitignore @@ -13,12 +13,8 @@ crash.log crash.*.log -# Exclude all .tfvars files, which are likely to contain sensitive data, such as -# password, private keys, and other secrets. These should not be part of version -# control as they are data points which are potentially sensitive and subject -# to change depending on the environment. -*.tfvars -*.tfvars.json +# Secrets (API token, SSH user); see secrets.auto.tfvars.example +secrets.auto.tfvars # Ignore override files as they are usually used to override resources locally and so # are not checked in @@ -52,7 +48,7 @@ terraform.rc .LSOverride # Icon must end with two \r -Icon +Icon # Thumbnails ._* diff --git a/README.md b/README.md index 8473244..e7a3645 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ -# Learning Plattform for Students +# Learning Platform for Students ## init @@ -21,47 +21,32 @@ Build a reliable platform on a pve host to teach students Linux/Windows server a ## User Management -Apart from the pve host, the main core of the environment will be a _FreeIPA_ Server which combines +A Windows Server DC/AD is the identity and DNS server of the lab. Each student gets an AD account and is in the group `linux_students` (further groups: `linux_admins`, `linux_users`). Student VMs join the domain. -- identity management server (ldap, sso, acl, ...) via a 389 directory server -- DNS server (bind) -- NTP server -- Kerberos -- Dogtag for certificate management -- NFS server or 'advertise' one for home folders of all users and other userful shares when needed in an exercise +Additional DCs and firewalls/routers are built by the students as exercises. +## Repo layout -We will setup user accounts for each student on the ipa server and a home folder for each one which are shared via nfs -and automatically mounted on user login on any device in the domain/realm. -The student will always have their files available no matter the device they login with their own account. - -Define designated groups, `linux_admins`, `linux_students`, `linux_users`, with each its own ACLs mediated via the ipa server. - +- `exercises/`: exercise sheets +- `infra/pve/`: PVE host facts, IP plan, one-time setup +- `infra/tofu//`: one OpenTofu root module (and state) per role, e.g. `student-vms` +- `infra/sandbox/`: throwaway test VMs (template clones, no OpenTofu) ## Facts -**Domain:** - -- domain: `lab.softbox.net` -- realm: `LAB.SOFTBOX.NET` -- ipa: - - hostname: `ipa.lab.softox.net` - - ip address: `10.11.12.65/24` - +**Domain:** `lab.softbox.net` **Students:** - username: `firstname.surname` -- mail: `firstname.surname@softbox.de` -- user_ssh_public_key: Created in exercise sheet-00 -- uid: `1000-1020` -- gid: `1000-1020` -- groups: `linux_students` +- mail: `firstname.surname@softbox.de` +- user_ssh_public_key: created in exercise sheet-00 +- groups: `linux_students` **VMs:** -- hostname: `vm_00` -- IP addresses: `10.11.12.200-220` +- hostname: `vm-00` … `vm-20` +- IP addresses: `10.100.200.80-100` (see [infra/pve/README.md](infra/pve/README.md)) ## Schedule diff --git a/TODO.md b/TODO.md index 2350dd6..1c8c7e7 100644 --- a/TODO.md +++ b/TODO.md @@ -1,11 +1,6 @@ ## TODOs -- [ ] change hostnames and correspondingly the dns entries of the student hosts to `vm_00` -> manage dns via opentofu -- [ ] include all users to freeipa and integrate freeipa as main identity and dns server. Deprecate current bind servers -> automate client installation -- [ ] migrate dns zone to freeipa and manage its bind server with opentofu -- [ ] create nfs shares for the home folder of each user -- [ ] write loop for student_vm deployment -> they are similar - -- [ ] include automation which install freeipa-client on each vm via opentofu or ansible executed via opentofu +- [ ] set up the Windows DC/AD `dc01` (`10.100.200.70`, 2 vCPU, 4-8 GB) as identity and DNS server +- [ ] switch the student VMs' DNS to the DC and join them to the domain +- [ ] create AD accounts and home folders for all students - [ ] write nice markdown template for sheets. With better code highlighting and better text formatting - diff --git a/infra/pve/README.md b/infra/pve/README.md new file mode 100644 index 0000000..630b018 --- /dev/null +++ b/infra/pve/README.md @@ -0,0 +1,41 @@ +# PVE host + +- Host: `pve` (`10.100.200.50`), Proxmox VE 9.2, 6 cores, 31 GiB RAM +- Storage: `local` (dir: ISOs, snippets), `local-lvm` (VM disks) +- Network: `vmbr0` on `10.100.200.0/24`, gateway and current DNS `10.100.200.4` +- Templates: `903` `temp-debian-13` + +## IP plan (10.100.200.0/24) + +| Address | Use | Size | +|---|---|---| +| `.4` | Gateway, current DNS | | +| `.50` | PVE host | | +| `.70` | DC/AD `dc01.lab.softbox.net` (planned) | 2 vCPU, 4-8 GB | +| `.80-.100` | Student VMs (`vm-00`, …), VMID `1080-1100`, 5-10 planned | 1 vCPU, 1 GB | +| `.101-.211` | In use by other devices (probably DHCP) | | + +## One-time setup (as root, done) + +```bash +# 1. Allow snippets on local storage +pvesm set local --content iso,vztmpl,backup,import,snippets + +# 2. API user and token for OpenTofu; put the printed value into +# infra/tofu//secrets.auto.tfvars as pve_api_token = "tofu@pve!tofu=" +pveum user add tofu@pve --comment "OpenTofu" +pveum aclmod / -user tofu@pve -role PVEAdmin +pveum user token add tofu@pve tofu --privsep 0 + +# 3. sudo for sbxadmin; passwordless rules for the snippet upload of the bpg/proxmox provider +usermod -aG sudo sbxadmin +cat > /etc/sudoers.d/sbxadmin-tofu <<'EOF' +sbxadmin ALL=(root) NOPASSWD: /usr/sbin/pvesm apiinfo +sbxadmin ALL=(root) NOPASSWD: /usr/bin/tee ^/var/lib/vz/snippets/[a-zA-Z0-9_][a-zA-Z0-9_.-]*$ +EOF +chmod 440 /etc/sudoers.d/sbxadmin-tofu && visudo -c + +# 4. Templates: tf-mod-pve/scripts/create_template.sh, copied to /root/templates/ with +# storage=local-lvm, ssh_keyfile=/root/.ssh/authorized_keys, only temp-debian-13 enabled +cd /root/templates && bash create_template.sh +``` diff --git a/infra/sandbox/README.md b/infra/sandbox/README.md new file mode 100644 index 0000000..4600622 --- /dev/null +++ b/infra/sandbox/README.md @@ -0,0 +1,13 @@ +# sandbox + +Throwaway test VMs, not managed by OpenTofu. Clone a template on the PVE host (as root), use VMIDs `9990-9999`: + +```bash +qm clone 903 9991 --name test-01 --full +qm set 9991 --ipconfig0 ip=dhcp +qm start 9991 +# done testing +qm stop 9991 && qm destroy 9991 +``` + +Login: `ssh admin@` (IP via `qm guest cmd 9991 network-get-interfaces`). diff --git a/infra/tofu/student-vms/.terraform.lock.hcl b/infra/tofu/student-vms/.terraform.lock.hcl new file mode 100644 index 0000000..dd10a2b --- /dev/null +++ b/infra/tofu/student-vms/.terraform.lock.hcl @@ -0,0 +1,36 @@ +# This file is maintained automatically by "tofu init". +# Manual edits may be lost in future updates. + +provider "registry.opentofu.org/bpg/proxmox" { + version = "0.118.1" + constraints = ">= 0.118.0, ~> 0.118" + hashes = [ + "h1:/tyENGiIdJgBVlcOQOyHaBmLyQEoogFnTTMN+GJ6ayE=", + "h1:057hFYAlLv2HcjHR7TVoNnDmm57YWNZDNqhDvv6PS7I=", + "h1:CxLdcddSmTqP00oYkvWdN6d+qQSDTOhDzbM2/FzCPbY=", + "h1:E1uc/cddGUBfnYyLOxnmqpzd6yLL4RKHqZJVxRMn/DA=", + "h1:G34GMrwG/FejGMTxwJYKx4tghadFA+xqtrF40FbyOG8=", + "h1:M1nzf28WMPzm4p9sUJGrAUOcqfDyQwRjRIpFMaHzo+Q=", + "h1:MeUGboRbtAzb07dgljVCzUmU5hJ8BOfHBQOOtFzNziU=", + "h1:OCF6iuMldDOqn52ULPf22IJb0Ni3GFx6HdoiTAPIb3M=", + "h1:f46bI0fO0leCFEkYdEVcRrsjAAtSFEN6yZKu2fZymTI=", + "h1:fhAY0NwBwBBu/qUeTb1lAd3a7bffgouILhhjUNNNXCk=", + "h1:gaCA8Ez8zV/OXIVWIE1B11iQmKH7gILdn7Vm9KSRrbU=", + "h1:r52a2cEQ7ftGS1G6vHFMqWI6Bg6o0F/T5BsShH8O2DQ=", + "h1:wAD5hFQpgEptQYR5UK8Hbh8WoTcMGnRCxehLqN+3f9w=", + "zh:07405857f3dda91d1a065b0349547920f4f9b58ec214b19f7e54a07d776c1f88", + "zh:0bd362db77c5971fe0e92b0faf57ed9b50062d4321b5c7a09f492c57dffecb19", + "zh:2cb31f9d4ad2ae90f72f87e3cb77c2f745acbe4b17822821c385612647ea178d", + "zh:2e63ff741101e173feafb133f96273634831322984939115396763b6f860706b", + "zh:787bdda1b4e0924c334acebeb7491966b8e611aaa8e542ec003cb8cbfd9081f4", + "zh:7f2ece41d81a24988f5a357684b2b323cc5165022434e7f91e5a29f10a4182c7", + "zh:9a1e263e4111105c14ed1dc44a5c4e45f00f0f88cb726d16388771c1cdd9b26d", + "zh:a2c1ae4950667bdc0f023f3d12d175fec3cf1a809c4d990cf72f0a200606f6f8", + "zh:abd342c9153e712a8542b0fd03f6a0b25f954573184dfe8f5b05b08b1e26e254", + "zh:b5cc543d31183a63c782af7fab9a4a203b4eebf782db13af94ae65c9b7d51373", + "zh:befeb50e7346802d3c222541e91a06093933990acdfc89cf93364615fdac91c4", + "zh:d164da80b3f3339549942a3131b0717f14798ed514e8362c76874b68feba89f8", + "zh:dc265c6e4c3e656e014c524b50e0ff02761070cb9db7cb24f7b0ab57c12b9e97", + "zh:f26e0763dbe6a6b2195c94b44696f2110f7f55433dc142839be16b9697fa5597", + ] +} diff --git a/infra/tofu/student-vms/README.md b/infra/tofu/student-vms/README.md new file mode 100644 index 0000000..76cb7e1 --- /dev/null +++ b/infra/tofu/student-vms/README.md @@ -0,0 +1,15 @@ +# student-vms + +One Debian 13 VM per student (`vm-00`, `vm-01`, …, IPs `10.100.200.80-100`), created with [tf-mod-pve](https://github.com/CubelaPetar/tf-mod-pve). The VM list is in `vms.tf`. + +Prerequisites: host setup from [`../../pve/README.md`](../../pve/README.md) and your SSH key authorized for `sbxadmin` on the host. + +```bash +ssh-add ~/.ssh/id_ed25519 # provider uploads snippets via ssh-agent +cp secrets.auto.tfvars.example secrets.auto.tfvars # fill in the API token +tofu init +tofu plan +tofu apply +``` + +State is local (`terraform.tfstate`, gitignored). Back it up. diff --git a/infra/tofu/student-vms/providers.tf b/infra/tofu/student-vms/providers.tf new file mode 100644 index 0000000..b5b135c --- /dev/null +++ b/infra/tofu/student-vms/providers.tf @@ -0,0 +1,11 @@ +provider "proxmox" { + endpoint = "https://10.100.200.50:8006/" + api_token = var.pve_api_token + insecure = true # self-signed certificate + + # Used to upload the cloud-init snippets, key comes from the ssh-agent + ssh { + agent = true + username = var.pve_ssh_user + } +} diff --git a/infra/tofu/student-vms/secrets.auto.tfvars.example b/infra/tofu/student-vms/secrets.auto.tfvars.example new file mode 100644 index 0000000..d40ac5d --- /dev/null +++ b/infra/tofu/student-vms/secrets.auto.tfvars.example @@ -0,0 +1,3 @@ +# Copy to secrets.auto.tfvars (gitignored) and fill in +pve_api_token = "tofu@pve!tofu=" +pve_ssh_user = "sbxadmin" diff --git a/infra/tofu/student-vms/variables.tf b/infra/tofu/student-vms/variables.tf new file mode 100644 index 0000000..2221b48 --- /dev/null +++ b/infra/tofu/student-vms/variables.tf @@ -0,0 +1,16 @@ +variable "pve_api_token" { + type = string + description = "Proxmox API token, e.g. tofu@pve!tofu=" + sensitive = true +} + +variable "pve_ssh_user" { + type = string + description = "SSH user on the PVE host for the snippet upload" +} + +variable "ssh_public_key_file" { + type = string + description = "Public key added to the VM user admin" + default = "~/.ssh/id_ed25519.pub" +} diff --git a/infra/tofu/student-vms/versions.tf b/infra/tofu/student-vms/versions.tf new file mode 100644 index 0000000..0f26929 --- /dev/null +++ b/infra/tofu/student-vms/versions.tf @@ -0,0 +1,10 @@ +terraform { + required_version = ">= 1.10" + + required_providers { + proxmox = { + source = "bpg/proxmox" + version = "~> 0.118" + } + } +} diff --git a/infra/tofu/student-vms/vms.tf b/infra/tofu/student-vms/vms.tf new file mode 100644 index 0000000..b321db7 --- /dev/null +++ b/infra/tofu/student-vms/vms.tf @@ -0,0 +1,32 @@ +# One VM per student: hostname => VMID and IP (10.100.200.80-100) +locals { + vms = { + "vm-00" = { vm_id = 1080, ip = "10.100.200.80" } + "vm-01" = { vm_id = 1081, ip = "10.100.200.81" } + "vm-02" = { vm_id = 1082, ip = "10.100.200.82" } + } +} + +module "vm" { + source = "git::https://github.com/CubelaPetar/tf-mod-pve.git?ref=v1.0.0" + for_each = local.vms + + node_name = "pve" + vm_id = each.value.vm_id + template_vm_id = 903 # temp-debian-13 + hostname = each.key + domain = "lab.softbox.net" + tags = ["student"] + ssh_public_key = file(pathexpand(var.ssh_public_key_file)) + + cpu_cores = 1 + memory = 1024 + storage = "local-lvm" + disk_size = 16 + + net_bridge = "vmbr0" + ip_address = "${each.value.ip}/24" + gateway = "10.100.200.4" + # Current resolver until the DC/AD takes over DNS + nameservers = ["10.100.200.4"] +}