feat(storage): migrate all sensitive services to proxmox-lvm-encrypted
Reconcile Terraform with cluster state after manual encrypted PVC migrations
and complete the remaining unfinished migrations. All services storing
sensitive data now use LUKS2-encrypted block storage via the Proxmox CSI
plugin.
## Context
Only Technitium DNS was using encrypted storage in Terraform. Many services
had been manually migrated to encrypted PVCs in the cluster, but Terraform
was never updated — creating dangerous state drift where a `tg apply` could
recreate unencrypted PVCs.
## This change
Phase 0 — Infrastructure:
- Add `proxmox-lvm-encrypted` StorageClass to Helm values (extraParameters)
- Add ExternalSecret for LUKS encryption passphrase to Terraform
- Fix CSI node plugin memory: `node.plugin.resources` (not `node.resources`)
with 1280Mi limit for LUKS2 Argon2id key derivation
Phase 1 — TF state reconciliation (zero downtime):
- Health, Matrix, N8N, Forgejo, Vaultwarden, Mailserver: state rm + import
- Redis, DBAAS MySQL, DBAAS PostgreSQL: Helm/CNPG value updates
Phase 2 — Data migration (encrypted PVCs existed but unused):
- Headscale, Frigate, MeshCentral: rsync + switchover
- Nextcloud (20Gi): rsync + chart_values update
Phase 3 — New encrypted PVCs:
- Roundcube HTML, HackMD, Affine, DBAAS pgadmin: create + rsync + switchover
Phase 4 — Cleanup:
- Deleted 5 orphaned unencrypted PVCs
## Services migrated (18 PVCs across 14 namespaces)
```
vaultwarden → vaultwarden-data-encrypted
dbaas → datadir-mysql-cluster-0, pg-cluster-{1,2}, dbaas-pgadmin-encrypted
mailserver → mailserver-data-encrypted, roundcubemail-{enigma,html}-encrypted
nextcloud → nextcloud-data-encrypted
forgejo → forgejo-data-encrypted
matrix → matrix-data-encrypted
n8n → n8n-data-encrypted
affine → affine-data-encrypted
health → health-uploads-encrypted
hackmd → hackmd-data-encrypted
redis → redis-data-redis-node-{0,1}
headscale → headscale-data-encrypted
frigate → frigate-config-encrypted
meshcentral → meshcentral-{data,files}-encrypted
```
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
aafb7eea34
commit
8b004c4c94
16 changed files with 151 additions and 96 deletions
|
|
@ -2,7 +2,7 @@ resource "kubernetes_namespace" "proxmox_csi" {
|
|||
metadata {
|
||||
name = "proxmox-csi"
|
||||
labels = {
|
||||
tier = var.tier
|
||||
tier = var.tier
|
||||
"resource-governance/custom-quota" = "true"
|
||||
}
|
||||
}
|
||||
|
|
@ -30,16 +30,34 @@ resource "helm_release" "proxmox_csi" {
|
|||
}
|
||||
|
||||
# StorageClass for block volumes on existing HDD thin pool
|
||||
storageClass = [{
|
||||
name = "proxmox-lvm"
|
||||
storage = "local-lvm"
|
||||
reclaimPolicy = "Retain"
|
||||
fstype = "ext4"
|
||||
ssd = false
|
||||
cache = "none"
|
||||
volumeBindingMode = "WaitForFirstConsumer"
|
||||
allowVolumeExpansion = true
|
||||
}]
|
||||
storageClass = [
|
||||
{
|
||||
name = "proxmox-lvm"
|
||||
storage = "local-lvm"
|
||||
reclaimPolicy = "Retain"
|
||||
fstype = "ext4"
|
||||
ssd = false
|
||||
cache = "none"
|
||||
volumeBindingMode = "WaitForFirstConsumer"
|
||||
allowVolumeExpansion = true
|
||||
},
|
||||
{
|
||||
name = "proxmox-lvm-encrypted"
|
||||
storage = "local-lvm"
|
||||
reclaimPolicy = "Retain"
|
||||
fstype = "ext4"
|
||||
ssd = false
|
||||
cache = "none"
|
||||
volumeBindingMode = "WaitForFirstConsumer"
|
||||
allowVolumeExpansion = true
|
||||
extraParameters = {
|
||||
"csi.storage.k8s.io/node-stage-secret-name" = "proxmox-csi-encryption"
|
||||
"csi.storage.k8s.io/node-stage-secret-namespace" = "kube-system"
|
||||
"csi.storage.k8s.io/node-expand-secret-name" = "proxmox-csi-encryption"
|
||||
"csi.storage.k8s.io/node-expand-secret-namespace" = "kube-system"
|
||||
}
|
||||
},
|
||||
]
|
||||
|
||||
controller = {
|
||||
replicas = 2
|
||||
|
|
@ -49,10 +67,13 @@ resource "helm_release" "proxmox_csi" {
|
|||
}
|
||||
}
|
||||
|
||||
# LUKS2 Argon2id key derivation needs ~1GiB memory
|
||||
node = {
|
||||
resources = {
|
||||
requests = { cpu = "10m", memory = "32Mi" }
|
||||
limits = { memory = "64Mi" }
|
||||
plugin = {
|
||||
resources = {
|
||||
requests = { cpu = "10m", memory = "64Mi" }
|
||||
limits = { memory = "1280Mi" }
|
||||
}
|
||||
}
|
||||
}
|
||||
})]
|
||||
|
|
@ -153,3 +174,36 @@ resource "kubernetes_cluster_role_binding" "pve_snapshot_admin" {
|
|||
namespace = "kube-system"
|
||||
}
|
||||
}
|
||||
|
||||
# --- ExternalSecret for LUKS encryption passphrase ---
|
||||
# Creates K8s Secret "proxmox-csi-encryption" in kube-system from Vault KV.
|
||||
# Referenced by the proxmox-lvm-encrypted StorageClass for node-stage and node-expand.
|
||||
resource "kubernetes_manifest" "external_secret_encryption" {
|
||||
manifest = {
|
||||
apiVersion = "external-secrets.io/v1beta1"
|
||||
kind = "ExternalSecret"
|
||||
metadata = {
|
||||
name = "proxmox-csi-encryption"
|
||||
namespace = "kube-system"
|
||||
}
|
||||
spec = {
|
||||
refreshInterval = "1h"
|
||||
secretStoreRef = {
|
||||
kind = "ClusterSecretStore"
|
||||
name = "vault-kv"
|
||||
}
|
||||
target = {
|
||||
name = "proxmox-csi-encryption"
|
||||
creationPolicy = "Owner"
|
||||
deletionPolicy = "Retain"
|
||||
}
|
||||
data = [{
|
||||
secretKey = "encryption-passphrase"
|
||||
remoteRef = {
|
||||
key = "viktor"
|
||||
property = "proxmox_csi_encryption_passphrase"
|
||||
}
|
||||
}]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue