From 6b7ef92c5b59dd4519c38f9276d05ae615a79fd7 Mon Sep 17 00:00:00 2001 From: Graham McIntire Date: Thu, 4 Jun 2026 15:24:43 -0500 Subject: [PATCH] cleanup: remove audit findings files --- findings.md | 296 ---------------------------------------------------- 1 file changed, 296 deletions(-) delete mode 100644 findings.md diff --git a/findings.md b/findings.md deleted file mode 100644 index cd46dd4..0000000 --- a/findings.md +++ /dev/null @@ -1,296 +0,0 @@ -# Infra Code Audit Findings - -> Generated: 2026-06-04 -> Scope: `tofu/` (OpenTofu) and `talos/` (Talos Kubernetes cluster) - -## Severity Legend -- **πŸ”΄ CRITICAL** β€” Security vulnerability or data exposure -- **🟠 HIGH** β€” Configuration that will break or is actively broken -- **🟑 MEDIUM** β€” Suboptimal config, drift, or potential future issue -- **πŸ”΅ LOW** β€” Cleanup / best practice / suggestion - ---- - -## πŸ”΄ Security Issues - -### S-1: `talos/kubeconfig` committed to git -- **File**: `talos/kubeconfig` -- **Severity**: πŸ”΄ CRITICAL -- **Issue**: Full admin kubeconfig for the home-cluster is tracked in git. Grants cluster admin access. -- **Fix**: Add to `.gitignore`, remove from git history (`git rm --cached talos/kubeconfig`), rotate certs if exposed publicly. - -### S-2: `talos/secrets.yaml.backup` tracked in git -- **File**: `talos/secrets.yaml.backup` -- **Severity**: πŸ”΄ CRITICAL -- **Issue**: Backup of cluster secrets (contains CA keys, bootstrap tokens, service account keys) is tracked in git. The main `secrets.yaml` is correctly gitignored, but the backup isn't β€” `.gitignore` only has `*.bak` not `*.backup`. -- **Fix**: Add `*.backup` to `.gitignore`, `git rm --cached talos/secrets.yaml.backup`. - -### S-3: `talos/talosconfig.backup` tracked in git -- **File**: `talos/talosconfig.backup` -- **Severity**: πŸ”΄ CRITICAL -- **Issue**: Backup of talosctl admin config is tracked in git (same `.backup` gitignore gap). -- **Fix**: Add `*.backup` to `.gitignore`, `git rm --cached talos/talosconfig.backup`. - -### S-4: Tailscale pre-auth keys in plaintext in CP configs -- **Files**: `talos/configs/talos-cp1.yaml`, `talos-cp2.yaml`, `talos-cp3.yaml` (TS_AUTHKEY line) -- **Severity**: πŸ”΄ CRITICAL -- **Issue**: Tailscale pre-authentication keys are stored in plaintext in three config files. Also exposed in `talos/configs/talos-worker2.yaml`. These keys are reusable until revoked and grant Tailscale network access. -- **Fix**: Use `sops` or environment variable injection at apply time. Rotate the exposed keys in Tailscale admin console. - -### S-5: `tofu/terraform.tfstate` committed to git -- **File**: `tofu/terraform.tfstate` -- **Severity**: πŸ”΄ CRITICAL -- **Issue**: Terraform state file (5,081 lines) committed to repo. Contains Cloudflare account IDs, zone IDs, internal K8s service DNS names (`*.svc.cluster.local`), tunnel ingress rules, and DKIM keys. OpenTofu has native state encryption (v1.7+) which is not being used. -- **Fix**: Add `*.tfstate*` to `.gitignore`, migrate to remote encrypted state backend, or use OpenTofu state encryption. - -### S-6: Private CA keys and cluster secrets exposed in per-node configs -- **Files**: All `talos/configs/talos-*.yaml` β€” every per-node config contains raw PKI private keys (machine CA, cluster CA, aggregator CA, service account, etcd CA) -- **Severity**: πŸ”΄ CRITICAL -- **Issue**: Per-node configs contain the full cluster PKI private keys in base64. These are NOT in `.gitignore`. Anyone with repo access can decrypt and use them to impersonate cluster components. -- **Fix**: Either gitignore the per-node configs, or strip keys and inject at apply time via `talosctl apply-config --patch`. - ---- - -## 🟠 Configuration Issues & Likely Bugs - -### C-1: Installer version drift β€” CPs on v1.12.4, some workers on v1.12.6 -- **Files**: - - `talos/controlplane.yaml:80` β†’ `v1.12.4` - - `talos/configs/talos-cp{1,2,3}.yaml:80` β†’ `v1.12.4` - - `talos/configs/talos-worker{1,2,3}.yaml:78` β†’ `v1.12.4` - - `talos/configs/talos-worker{4,5}.yaml:78` β†’ `v1.12.6` - - `talos/CLAUDE.md:9` β†’ "Talos Version: v1.12.6" -- **Severity**: 🟠 HIGH -- **Issue**: Configs reference a mix of `v1.12.4` and `v1.12.6` installer images. CLAUDE.md documents v1.12.6 as the cluster version. Per-node CP configs and workers 1-3 are on v1.12.4. Mixed versions across control plane and workers can cause compatibility issues. -- **Fix**: Update all references to v1.12.6 (whatever the current running version is). - -### C-2: `grubUseUKICmdline` + `extraKernelArgs` conflict on CPs -- **Files**: - - `talos/configs/talos-cp1.yaml:84-85` (and cp2, cp3) - - `talos/CLAUDE.md` documents this explicitly: "install.extraKernelArgs and install.grubUseUKICmdline cannot be used together" -- **Severity**: 🟠 HIGH -- **Issue**: All three control plane per-node configs set BOTH `grubUseUKICmdline: true` AND `extraKernelArgs: ["console=tty0"]`. Talos v1.12 validates these as mutually exclusive and will reject the config. Since these are Proxmox VMs booting with UEFI (OVMF), they likely use `systemd-boot` which ignores `extraKernelArgs` anyway. -- **Fix**: Remove `extraKernelArgs` and bake kernel args into the Image Factory schematic if needed. - -### C-3: Tailscale extension won't work on control plane nodes -- **Files**: `talos/configs/talos-cp{1,2,3}.yaml` - - `install.image`: `ghcr.io/siderolabs/installer:v1.12.4` (stock installer) - - `install.extensions`: `[ghcr.io/siderolabs/tailscale:1.82.1]` - - `ExtensionServiceConfig`: configured for tailscale with `TS_AUTHKEY` -- **Severity**: 🟠 HIGH -- **Issue**: CP configs specify `install.extensions` but use the **stock** installer image (`ghcr.io/siderolabs/installer`). Extensions require a **custom/factory** installer image (like the worker configs use: `factory.talos.dev/installer/`). The stock installer does NOT support extensions. Result: tailscale extension binary is never installed, `ExtensionServiceConfig` for tailscale silently fails. -- **Fix**: Either switch CP nodes to use the factory installer image (bundling tailscale into the schematic), or remove the tailscale extension config from CP nodes. - -### ~~C-4: Longhorn disk configs missing on workers 2 and 3~~ βœ… RESOLVED -- **Files**: `talos/configs/talos-worker{1,2,3,4,5}.yaml` -- **Severity**: 🟠 HIGH β†’ βœ… -- **Issue**: Longhorn has been removed from the cluster. All Longhorn disk configs (`extraMounts`, `disks`, installer image comments) removed from worker configs and documentation. - -### ~~C-5: All per-node worker configs lack `/dev/sdb` disk config~~ βœ… RESOLVED -- **Same as C-4** β€” all Longhorn disk references removed. - -### C-6: Suspect firewall expression `(not http.user_agent ne "")` -- **File**: `tofu/cloudflare.tf:437` -- **Severity**: 🟠 HIGH -- **Issue**: The expression `(not http.user_agent ne "")` uses confusing double negation. Cloudflare's WAF expression language may not handle this as intended β€” Cloudflare normalizes empty User-Agents by injecting defaults, making this condition effectively a no-op. -- **Fix**: Use `(not http.user_agent exists)` to block requests with missing User-Agent, or remove the condition entirely. - ---- - -## 🟑 Configuration Drift & Suboptimal Settings - -### D-1: Base configs (`controlplane.yaml`, `worker.yaml`) are stale templates -- **Files**: `talos/controlplane.yaml`, `talos/worker.yaml` -- **Severity**: 🟑 MEDIUM -- **Issue**: These are the original `talosctl gen config` output with auto-generated hostnames. They are NOT applied to any node (per-node configs in `configs/` are used instead). Yet they contain the same cluster secrets and must be kept in sync with any changes. They serve no deployment purpose and add maintenance burden. -- **Fix**: Either delete them (rely solely on per-node configs) or clearly mark them as templates with a big warning comment. - -### D-2: `controlplane.yaml` has no DNS nameservers configured -- **File**: `talos/controlplane.yaml` (network: `{}`) -- **Severity**: 🟑 MEDIUM -- **Issue**: Base controlplane config has empty network config. `worker.yaml` has three explicit nameservers (`9.9.9.9`, `149.112.112.112`, `1.1.1.1`). CP nodes rely entirely on DHCP DNS with no fallback. -- **Fix**: Add the same nameservers to the CP template for consistency. - -### D-3: No nameservers in per-node configs -- **Files**: All `talos/configs/talos-*.yaml` -- **Severity**: 🟑 MEDIUM -- **Issue**: While `worker.yaml` template has nameservers, none of the per-node configs carry them. All nodes rely on DHCP DNS only. -- **Fix**: Add nameservers to per-node configs for DNS resilience. - -### D-4: Hostname auto-generation in base configs (potential footgun) -- **Files**: `talos/controlplane.yaml` (HostnameConfig: `auto: stable`), `talos/worker.yaml` (same) -- **Severity**: 🟑 MEDIUM -- **Issue**: The base configs use `auto: stable` which generates hostnames from machine IDs. If someone applies the base config to a new node thinking it's a template, they'll get an auto-generated hostname instead of a predictable one. -- **Fix**: Comment out the `auto` line and add a big WARNING comment. - -### D-5: Per-node worker configs lack time servers -- **Files**: `talos/configs/talos-worker{1,2,3}.yaml` -- **Severity**: 🟑 LOW -- **Issue**: Per-node CP configs have `time.servers: [0/1/2.us.pool.ntp.org]` but worker per-node configs do not. Workers fall back to default NTP. -- **Fix**: Add NTP servers to worker configs for consistency. - -### D-6: Kubernetes discovery registry disabled -- **Files**: All configs β€” `cluster.discovery.registries.kubernetes.disabled: true` -- **Severity**: 🟑 MEDIUM -- **Issue**: Kubernetes discovery is disabled, and the external service registry is empty `{}`. If the Talos discovery service is unavailable, new nodes can't discover existing cluster members and won't join. -- **Fix**: Enable Kubernetes discovery (`disabled: false`) so nodes can discover each other via the API server. - -### D-7: `talos/CLAUDE.md` and `talos/README.md` doc drift -- **Files**: `talos/README.md` vs `talos/CLAUDE.md` -- **Severity**: 🟑 MEDIUM -- **Issue**: README.md says 3 workers (10.0.15.4-6), CLAUDE.md says 4 workers (10.0.15.4-7 including bare-metal talos4). README.md says "Proxmox Ceph storage" for VMs but Ceph is NOT used β€” it's `local-lvm`. README.md upgrade example references v1.9.5 (far behind). -- **Fix**: Align README.md with actual cluster state. Remove Ceph references. - -### D-8: `tofu/` provider versions behind -- **Files**: `tofu/.terraform.lock.hcl` -- **Severity**: 🟑 LOW -- **Issue**: Cloudflare provider pinned at v4.52.5 (v5.x available β€” breaking changes to review). Porkbun at v1.3.1 (v1.3.3 latest). -- **Fix**: Review changelogs and upgrade if safe. - -### D-9: Towerops tunnel uses `.svc.cluster.local` DNS -- **File**: `tofu/cloudflare.tf:574-580` -- **Severity**: 🟑 MEDIUM -- **Issue**: Tunnel ingress rules point to `http://towerops.towerops.svc.cluster.local:4000` etc. `cloudflared` running on a VM at 204.110.191.200 must be able to resolve these cluster-internal DNS names, which requires special DNS forwarding setup. -- **Fix**: Verify DNS resolution works, or document the setup. - -### D-10: PodSecurity admission uses deprecated `v1alpha1` API version -- **Files**: All CP configs β€” `admissionControl[0].configuration.apiVersion: pod-security.admission.config.k8s.io/v1alpha1` -- **Severity**: 🟑 LOW -- **Issue**: The `v1alpha1` API version for PodSecurityConfiguration was deprecated in Kubernetes 1.33+. The cluster runs v1.35.0. Should use `v1` or `v1beta1`. -- **Fix**: Update to `pod-security.admission.config.k8s.io/v1`. - -### D-11: `machine.time`, `machine.disks`, `machine.network.nameservers` use legacy fields -- **Files**: Various in `talos/` -- **Severity**: 🟑 LOW -- **Issue**: Talos v1.12 deprecates these fields in favor of multi-doc configs (`TimeSyncConfig`, `UserVolumeConfig`, `ResolverConfig`). Currently functional but will be removed in a future Talos version. -- **Fix**: Migrate to multi-doc configs when convenient. - ---- - -## πŸ”΅ Cleanup & Suggestions - -### E-1: Stale backup files committed to git -- **Files**: - - `tofu/terraform.tfstate.*.backup` (3 files) - - `tofu/terraform.tfstate.backup` - - `talos/configs/talos-worker1.yaml.bak` - - `talos/secrets.yaml.backup` - - `talos/talosconfig.backup` -- **Severity**: πŸ”΅ LOW -- **Issue**: Multiple stale backup files clutter the repo. `.gitignore` in talos/ has `*.bak` but not `*.backup`. -- **Fix**: Add `*.backup`, `*.bak`, `*.tfstate*` to gitignore both places. Clean up tracked files. - -### E-2: `tofu/tfplan` committed -- **File**: `tofu/tfplan` -- **Severity**: πŸ”΅ LOW -- **Issue**: Binary plan file committed. Should be ephemeral/gitignored. -- **Fix**: Add `tfplan` to gitignore. - -### E-3: No `.gitignore` in `tofu/` -- **File**: (missing) `tofu/.gitignore` -- **Severity**: πŸ”΅ LOW -- **Issue**: The tofu directory has no `.gitignore`. State files, `.terraform/` provider cache, plan files, and IDE settings are tracked. -- **Fix**: Create `tofu/.gitignore` with `.terraform/`, `*.tfstate*`, `tfplan`, `.envrc`, `.claude/`. - -### E-4: `tofu/.claude/` IDE settings tracked -- **File**: `tofu/.claude/settings.local.json` -- **Severity**: πŸ”΅ LOW -- **Issue**: IDE/editor configuration committed to repo. -- **Fix**: Add `.claude/` to gitignore. - -### E-5: Orphaned/unused worker configs (workers 4 and 5) -- **Files**: `talos/configs/talos-worker4.yaml`, `talos/configs/talos-worker5.yaml` -- **Severity**: πŸ”΅ LOW -- **Issue**: Worker4 config has `hostname: talos4` (filename doesn't match hostname). Worker5 hostname `talos5` with no documented existence. Worker4 config uses bare-metal install disk pattern (`/dev/sdb`). Worker5 uses `/dev/nvme0n1`. No VMs or nodes documented for either. -- **Fix**: Either document the purpose of these nodes or remove the orphaned configs. - -### E-6: `towerops-dev` tunnel documented as "down" -- **File**: `tofu/cloudflare.tf` (comment around line 556) -- **Severity**: πŸ”΅ LOW -- **Issue**: dev tunnel marked as "down" in comments but still has import + config resource. If the tunnel was deleted from Cloudflare, `tofu apply` will fail. -- **Fix**: Either remove the tunnel config or fix the tunnel and update the comment. - -### E-7: `talos/.gitignore` doesn't cover secrets effectively -- **File**: `talos/.gitignore` -- **Severity**: πŸ”΅ LOW -- **Issue**: Currently only ignores `secrets.yaml`, `talosconfig`, `*.tmp`, `*.bak`. Missing: `*.backup`, `kubeconfig`, `configs/*.yaml` (which contain full cluster PKI). -- **Fix**: Add `*.backup`, `kubeconfig` to `.gitignore`. Consider gitignoring per-node configs and maintaining them outside the repo. - -### E-8: `disableManifestsDirectory: true` comment is misleading -- **Files**: All configs -- **Severity**: πŸ”΅ LOW -- **Issue**: Comment says "configures the kubelet to get static pod manifests from the /etc/kubernetes/manifests directory" β€” but setting `true` DISABLES that directory. The comment describes the inverse behavior. -- **Fix**: Fix the comment to say "Disable static pod manifest directory". - -### E-9: `talos/README.md` has stale upgrade example -- **File**: `talos/README.md` line 47, 112 -- **Severity**: πŸ”΅ LOW -- **Issue**: Upgrade example shows `v1.9.5` (cluster is on v1.12.x). Also mentions `tofu` deployment workflow referencing `terraform/talos_vms.tf` which was removed. -- **Fix**: Update examples to match current versions and directory structure. - -### E-10: `tofu/` uses OpenTofu-specific features (worth documenting) -- **File**: `tofu/dns.tf`, `tofu/cloudflare.tf` -- **Severity**: πŸ”΅ LOW (INFO) -- **Issue**: Uses `import` with `for_each` which is an OpenTofu v1.8+ feature with no Terraform equivalent. This config can't go back to Terraform. -- **Fix**: Document this constraint to prevent future migration attempts. - ---- - -## Summary - -| Severity | Count | -|----------|-------| -| πŸ”΄ CRITICAL | 6 | -| 🟠 HIGH | 4 | -| 🟑 MEDIUM | 11 | -| πŸ”΅ LOW | 10 | -| **Total** | **31** | - -**Top 5 priorities:** -1. Remove sensitive files from git (kubeconfig, secrets backup, talosconfig backup, per-node configs with PKI) β€” S-1, S-2, S-3, S-6 -2. Rotate exposed Tailscale pre-auth keys β€” S-4 -3. Fix CP configs: either remove tailscale extension or switch to factory installer β€” C-3 -4. Fix `grubUseUKICmdline` + `extraKernelArgs` conflict on CPs β€” C-2 -5. Resolve installer version drift across Talos configs (v1.12.4 β†’ v1.12.6) β€” C-1 - ---- - -## βœ… Fixes Applied (2026-06-04) - -### Security -- **S-1, S-2, S-3**: Removed `talos/kubeconfig`, `talos/secrets.yaml.backup`, `talos/talosconfig.backup` from git tracking via `git rm --cached` -- **S-5**: Created `tofu/.gitignore`, removed `tofu/terraform.tfstate` from tracking -- Updated `talos/.gitignore` with `*.backup`, `kubeconfig`, `secrets.yaml.backup`, `talosconfig.backup` - -### Talos Config Fixes (background task) -- **C-1**: Installer version drift resolved (all configs β†’ v1.12.6) -- **C-2**: `grubUseUKICmdline` + `extraKernelArgs` conflict resolved on CPs -- **C-3**: Tailscale extension removed from stock installer CP configs -- **D-2/D-3**: Nameservers added to controlplane.yaml and per-node configs -- **D-4**: HostnameConfig `auto: stable` removed from base templates -- **D-5**: Time servers added to all per-node worker configs -- **D-7**: README.md updated (worker count 4, local-lvm, versions corrected) -- **E-5**: Orphaned worker4/5 configs documented -- Root CLAUDE.md: Longhorn removed, worker count corrected, Cephβ†’local-lvm fixed - -### Longhorn Removal -- `talos/worker.yaml`: Removed active `extraMounts` and `disks` blocks -- `talos/configs/talos-worker*.yaml`: Removed commented-out Longhorn blocks -- All documentation cleaned up (CLAUDE.md, README.md, root CLAUDE.md) - -### Provider Upgrades -- **Porkbun**: v1.3.1 β†’ v1.3.3 (via `tofu init -upgrade`) -- **Cloudflare**: v4.52.5 β†’ v5.19.1 (full v5 migration): - - `main.tf`: Bumped constraint to `~> 5.0` - - `cloudflare_zone`: Renamed `zone` β†’ `name`, `account_id` β†’ `account = { id = ... }` - - `cloudflare_record` β†’ `cloudflare_dns_record` (50+ resources + imports) - - Replaced 4x `cloudflare_zone_settings_override` with 24x `cloudflare_zone_setting` - - Converted ALL `cloudflare_ruleset` blocks β†’ attribute syntax (`rules = [...]`) - - Converted DNS `data {}` blocks β†’ attribute syntax (`data = {}`) - - Added `ttl = 1` to proxied records (required in v5) - - Tunnel config blocks β†’ attribute syntax (`config = { ingress = [...] }`) - -### Other Fixes -- **C-6**: Fixed suspect firewall expression `(not http.user_agent ne "")` β†’ `(not http.request.user_agent exists)` -- **E-1/2/3**: Created `tofu/.gitignore`, cleaned up backup files and tfplan -- Root CLAUDE.md: Updated Talos notes (storage, worker count, Longhorn references)