diff --git a/.config/nvim/lazy-lock.json b/.config/nvim/lazy-lock.json index 79f6043..1e2b7ed 100644 --- a/.config/nvim/lazy-lock.json +++ b/.config/nvim/lazy-lock.json @@ -1,33 +1,33 @@ { "LuaSnip": { "branch": "master", "commit": "73813308abc2eaeff2bc0d3f2f79270c491be9d7" }, "NvChad": { "branch": "v2.5", "commit": "f107fabe11ac8013dc3435ecd5382bee872b1584" }, - "base46": { "branch": "v3.0", "commit": "db58475d3fd2a16f9b1467d6895e3c4c195ed7dd" }, + "base46": { "branch": "v3.0", "commit": "884b990dcdbe07520a0892da6ba3e8d202b46337" }, "brightburn.vim": { "branch": "master", "commit": "fc0d2fafc51e86d6065acd54b5e82e686019ff2f" }, "cloak.nvim": { "branch": "main", "commit": "648aca6d33ec011dc3166e7af3b38820d01a71e4" }, "cmd.nvim": { "branch": "main", "commit": "540174697858d244ae1794a37521c0c94e3098a0" }, - "cmp-async-path": { "branch": "main", "commit": "0ed1492f59e730c366d261a5ad822fa37e44c325" }, + "cmp-async-path": { "branch": "main", "commit": "f8af3f726e07f2e9d37672eaa9102581aefce149" }, "cmp-buffer": { "branch": "main", "commit": "b74fab3656eea9de20a9b8116afa3cfc4ec09657" }, - "cmp-nvim-lsp": { "branch": "main", "commit": "bd5a7d6db125d4654b50eeae9f5217f24bb22fd3" }, - "cmp-nvim-lua": { "branch": "main", "commit": "f12408bdb54c39c23e67cab726264c10db33ada8" }, + "cmp-nvim-lsp": { "branch": "main", "commit": "cbc7b02bb99fae35cb42f514762b89b5126651ef" }, + "cmp-nvim-lua": { "branch": "main", "commit": "e3a22cb071eb9d6508a156306b102c45cd2d573d" }, "cmp_luasnip": { "branch": "master", "commit": "98d9cb5c2c38532bd9bdb481067b20fea8f32e90" }, "conform.nvim": { "branch": "master", "commit": "9d6f881a4047a51c7709223dcf24e967633c6523" }, - "friendly-snippets": { "branch": "main", "commit": "572f5660cf05f8cd8834e096d7b4c921ba18e175" }, + "friendly-snippets": { "branch": "main", "commit": "6cd7280adead7f586db6fccbd15d2cac7e2188b9" }, "gitsigns.nvim": { "branch": "main", "commit": "1ee5c1fd068c81f9dd06483e639c2aa4587dc197" }, "golf": { "branch": "main", "commit": "abf1bc0c1c4a5482b4a4b36b950b49aaa0f39e69" }, "gopher.nvim": { "branch": "main", "commit": "295e21e637f9194a4d2bc34622d324a88b028141" }, "gruvbox": { "branch": "main", "commit": "5e0a460d8e0f7f669c158dedd5f9ae2bcac31437" }, "guihua.lua": { "branch": "master", "commit": "c49a0fb7346586a1b1431d7e407f943c4164d8cb" }, "harpoon": { "branch": "master", "commit": "1bc17e3e42ea3c46b33c0bbad6a880792692a1b3" }, - "indent-blankline.nvim": { "branch": "master", "commit": "005b56001b2cb30bfa61b7986bc50657816ba4ba" }, - "lazy.nvim": { "branch": "main", "commit": "a1380a8461ab115d69ac6a570a92611969e16c3a" }, + "indent-blankline.nvim": { "branch": "master", "commit": "d28a3f70721c79e3c5f6693057ae929f3d9c0a03" }, + "lazy.nvim": { "branch": "main", "commit": "85c7ff3711b730b4030d03144f6db6375044ae82" }, "lsp_signature.nvim": { "branch": "master", "commit": "62cadce83aaceed677ffe7a2d6a57141af7131ea" }, "mason-nvim-dap.nvim": { "branch": "main", "commit": "86389a3dd687cfaa647b6f44731e492970034baa" }, "mason.nvim": { "branch": "main", "commit": "ad7146aa61dcaeb54fa900144d768f040090bff0" }, "menu": { "branch": "main", "commit": "7a0a4a2896b715c066cfbe320bdc048091874cc6" }, "minty": { "branch": "main", "commit": "aafc9e8e0afe6bf57580858a2849578d8d8db9e0" }, "navigator.lua": { "branch": "master", "commit": "0ba1be08ba383f0f73ca467694afe0270f95fad5" }, - "nvim-autopairs": { "branch": "master", "commit": "23320e75953ac82e559c610bec5a90d9c6dfa743" }, - "nvim-cmp": { "branch": "main", "commit": "b5311ab3ed9c846b585c0c15b7559be131ec4be9" }, + "nvim-autopairs": { "branch": "master", "commit": "59bce2eef357189c3305e25bc6dd2d138c1683f5" }, + "nvim-cmp": { "branch": "main", "commit": "da88697d7f45d16852c6b2769dc52387d1ddc45f" }, "nvim-dap": { "branch": "master", "commit": "7367cec8e8f7a0b1e4566af9a7ef5959d11206a7" }, "nvim-dap-go": { "branch": "main", "commit": "b4421153ead5d726603b02743ea40cf26a51ed5f" }, "nvim-dap-ui": { "branch": "master", "commit": "cf91d5e2d07c72903d052f5207511bf7ecdb7122" }, @@ -35,10 +35,10 @@ "nvim-lspconfig": { "branch": "master", "commit": "db8fef885009fdec0daeff3e5dda92e1f539611e" }, "nvim-nio": { "branch": "master", "commit": "21f5324bfac14e22ba26553caf69ec76ae8a7662" }, "nvim-tree.lua": { "branch": "master", "commit": "87d096a39cb2d5d43e6771563575ff042a79f48b" }, - "nvim-treesitter": { "branch": "master", "commit": "42fc28ba918343ebfd5565147a42a26580579482" }, + "nvim-treesitter": { "branch": "main", "commit": "42fc28ba918343ebfd5565147a42a26580579482" }, "nvim-treesitter-context": { "branch": "master", "commit": "41847d3dafb5004464708a3db06b14f12bde548a" }, "nvim-ts-autotag": { "branch": "main", "commit": "c4ca798ab95b316a768d51eaaaee48f64a4a46bc" }, - "nvim-web-devicons": { "branch": "master", "commit": "b8221e42cf7287c4dcde81f232f58d7b947c210d" }, + "nvim-web-devicons": { "branch": "master", "commit": "737cf6c657898d0c697311d79d361288a1343d50" }, "plenary.nvim": { "branch": "master", "commit": "b9fd5226c2f76c951fc8ed5923d85e4de065e509" }, "py.nvim": { "branch": "main", "commit": "cc68e1adab6ff02f6d678abfbe95949391880b1a" }, "render-markdown.nvim": { "branch": "main", "commit": "48934b49a2363b49ae1d698ed4cb30fb79d7efe8" }, @@ -54,7 +54,7 @@ "telescope.nvim": { "branch": "master", "commit": "a0bbec21143c7bc5f8bb02e0005fa0b982edc026" }, "tokyonight.nvim": { "branch": "main", "commit": "4d159616aee17796c2c94d2f5f87d2ee1a3f67c7" }, "trouble.nvim": { "branch": "main", "commit": "f176232e7759c4f8abd923c21e3e5a5c76cd6837" }, - "ui": { "branch": "v3.0", "commit": "03b9718140375e7f3f5e4f3e04bc2b6c907440ec" }, + "ui": { "branch": "v3.0", "commit": "cb75908a86720172594b30de147272c1b3a7f452" }, "undotree": { "branch": "master", "commit": "0f1c9816975b5d7f87d5003a19c53c6fd2ff6f7f" }, "vim-astro": { "branch": "main", "commit": "9b4674ecfe1dd84b5fb9b4de1653975de6e8e2e1" }, "vim-be-good": { "branch": "master", "commit": "0ae3de14eb8efc6effe7704b5e46495e91931cc5" }, @@ -62,6 +62,6 @@ "vim-tmux-navigator": { "branch": "master", "commit": "c45243dc1f32ac6bcf6068e5300f3b2b237e576a" }, "vim-wakatime": { "branch": "master", "commit": "d7973b157a632d1edeff01818f18d67e584eeaff" }, "volt": { "branch": "main", "commit": "620de1321f275ec9d80028c68d1b88b409c0c8b1" }, - "which-key.nvim": { "branch": "main", "commit": "904308e6885bbb7b60714c80ab3daf0c071c1492" }, + "which-key.nvim": { "branch": "main", "commit": "3aab2147e74890957785941f0c1ad87d0a44c15a" }, "yanky.nvim": { "branch": "main", "commit": "04775cc6e10ef038c397c407bc17f00a2f52b378" } } diff --git a/.config/tmux/tmux.conf b/.config/tmux/tmux.conf index c1111e3..d6d9609 100644 --- a/.config/tmux/tmux.conf +++ b/.config/tmux/tmux.conf @@ -31,7 +31,7 @@ bind -n S-Right next-window # Easily reorder windows with CTRL+SHIFT+Arrow bind-key -n C-S-Left swap-window -t -1 bind-key -n C-S-Right swap-window -t +1 -bind -n C-f run-shell "tmux neww sessionizer" +bind-key k run-shell "tmux neww sessionizer" bind-key -r n new-session diff --git a/reports/pii-scan-2026-04-26.md b/reports/pii-scan-2026-04-26.md new file mode 100644 index 0000000..40ed5a5 --- /dev/null +++ b/reports/pii-scan-2026-04-26.md @@ -0,0 +1,239 @@ +# PII Exposure Scan Report + +**Date:** 2026-04-26 +**Scope:** User-owned repositories and home-directory configs under `/var/home/bupd` +**Repos scanned:** `dotfiles`, `code/ks`, `code/arch-bootc-hetzner`, `k8s-backups`, `night-family` +**Exclusions:** Vendored/third-party code (`code/OSS/*`, `go/pkg/mod/*`, `.local/share/containers/*`) + +--- + +## Findings + +### F-01 — Cloudflare API Token Tracked in Git + +- **file:** `code/ks/certmanager-issuer-secret.yaml` +- **line:** 8 +- **category:** env-secret +- **severity:** critical +- **detail:** A Kubernetes Secret manifest containing a plaintext Cloudflare API token (`api-token: `) is tracked in git despite being listed in `.gitignore`. The file was committed before the gitignore rule was added, so git continues to track it. The token is exposed in the public repository's commit history. +- **recommendation:** (1) Revoke and rotate the Cloudflare API token immediately. (2) Remove the file from tracking with `git rm --cached certmanager-issuer-secret.yaml`. (3) Use `git filter-repo` or BFG to scrub the token from commit history. (4) Store the token in a sealed secret, SOPS-encrypted file, or external secret manager. + +### F-02 — GitHub OAuth Token on Disk (Untracked) + +- **file:** `dotfiles/.config/gh/hosts.yml` +- **line:** 4, 7 +- **category:** env-secret +- **severity:** high +- **detail:** The GitHub CLI config file contains a live GitHub OAuth token (`oauth_token: gho_`). The file is NOT tracked in git, but it is also NOT covered by `.gitignore`, making it vulnerable to accidental commit via `git add .` or `git add -A`. +- **recommendation:** (1) Add `.config/gh/hosts.yml` to `dotfiles/.gitignore`. (2) Consider rotating the OAuth token periodically. (3) Verify the token has minimum required scopes. + +### F-03 — Keycloak .env Tracked with Plaintext Passwords + +- **file:** `code/ks/keycloak/.env` +- **line:** 1-12 +- **category:** env-secret +- **severity:** critical +- **detail:** The keycloak `.env` file is tracked in git despite a `.gitignore` entry for `keycloak/.env`. It contains plaintext credentials: `KEYCLOAK_ADMIN_PASSWORD`, `KC_DB_PASSWORD`, and `POSTGRES_PASSWORD` (all set to ``). These are exposed in the public repository. +- **recommendation:** (1) Rotate all passwords in the live Keycloak/Postgres deployment. (2) Remove from tracking: `git rm --cached keycloak/.env`. (3) Use `git filter-repo` or BFG to scrub from history. (4) Use a `.env.example` with placeholder values and source real secrets from a vault or sealed secret. + +### F-04 — Harbor Admin Password in Ground Control Helm Values + +- **file:** `code/ks/gc-chart/values.yaml` +- **line:** 25, 29-30, 55 +- **category:** env-secret +- **severity:** high +- **detail:** Tracked Helm values file contains plaintext credentials: `DB_PASSWORD` (line 25), `HARBOR_USERNAME`/`HARBOR_PASSWORD` (lines 29-30), and `postgresql.auth.password` (line 55). These include well-known default passwords and generic values, all committed to a public repository and potentially matching live deployments. +- **recommendation:** (1) Move secrets to a separate `values-secrets.yaml` that is gitignored, or use SOPS encryption. (2) If these credentials match any live deployment, rotate them. (3) Add a comment in the tracked values file pointing to the secret source. + +### F-05 — Harbor Admin Password in arch-bootc-hetzner Values + +- **file:** `code/arch-bootc-hetzner/files/harbor/values.yaml` +- **line:** 13 +- **category:** env-secret +- **severity:** high +- **detail:** Tracked Harbor Helm values file contains `harborAdminPassword: ""` — the well-known Harbor default password. This file is baked into the container image build and is tracked in a public repository. +- **recommendation:** (1) Replace the hardcoded password with an environment variable or mounted secret. (2) If this password is used on any live Harbor instance, rotate it. (3) Consider gitignoring this values file and using a `.example` pattern. + +### F-06 — Plaintext Passwords in Gitea Session Report + +- **file:** `code/ks/reports/gitea-session-report.md` +- **line:** 96, 99 +- **category:** hardcoded-pii +- **severity:** high +- **detail:** The tracked session report documents admin credential changes with plaintext password values (e.g., `password `). This is committed to a public repository. +- **recommendation:** (1) Redact password values from the report, replacing with `` or `***`. (2) Add a policy to session report templates that forbids including credential values. + +### F-07 — Plaintext Passwords in Nexus Session Report + +- **file:** `code/ks/reports/nexus-session-report.md` +- **line:** 216, 676 +- **category:** hardcoded-pii +- **severity:** high +- **detail:** The tracked session report records the admin password in plaintext (line 216: `password `) and discusses it as weak (line 676). Committed to a public repository. +- **recommendation:** (1) Redact password values from the report. (2) If the password matches any live deployment, rotate it. + +### F-08 — Pre-hashed Root Password with Fixed Salt in Containerfile + +- **file:** `code/arch-bootc-hetzner/Containerfile` +- **line:** 131 +- **category:** unencrypted-storage +- **severity:** medium +- **detail:** The root password is set via `chpasswd -e` using a SHA-512 hash with a fixed, predictable salt. While the password is hashed (not plaintext), the fixed salt makes it vulnerable to precomputed rainbow table attacks. The hash and salt are committed to a public repository. +- **recommendation:** (1) Use a randomly generated salt instead of a fixed one. (2) Consider using a stronger KDF or disabling root login entirely (SSH is already key-only). (3) The comment explains the fixed salt is for ostree merge stability — evaluate if a random salt truly causes merge conflicts or if there's a better approach. + +### F-09 — SSH Public Key Embedded in Containerfile + +- **file:** `code/arch-bootc-hetzner/Containerfile` +- **line:** 152 +- **category:** hardcoded-pii +- **severity:** low +- **detail:** An SSH RSA public key is hardcoded in the Containerfile for the `bupd` user. Public keys are not secret, but embedding them in a public repo ties a specific key to a specific user and server, aiding targeted attacks. +- **recommendation:** (1) Consider loading the public key from a build argument or mounted file instead of hardcoding. (2) This is low risk since public keys are meant to be public, but decoupling is good practice. + +### F-10 — Real Email in Cert-Manager Issuer Configs + +- **file:** `code/ks/certmanager-clusterissuer-prod.yaml` +- **line:** 11 +- **category:** hardcoded-pii +- **severity:** medium +- **detail:** Real email address `bupdprasanth@gmail.com` is used as the ACME account email in cert-manager cluster issuer configs. Also found in: `certmanager-clusterissuer-staging.yaml:11`, `issuer-lets-encrypt-production.yaml:10`, `issuer-lets-encrypt-staging.yaml:10`, `cert-issue.yaml:59`, `cluster-issuer.yaml:8,14`. This is expected for Let's Encrypt but exposes a personal email in a public repo. +- **recommendation:** (1) Consider using a project-specific or alias email for Let's Encrypt registrations. (2) This is a known and accepted practice for cert-manager, so severity is medium. + +### F-11 — Real Email in Containerfile Git Config + +- **file:** `code/arch-bootc-hetzner/Containerfile` +- **line:** 179 +- **category:** hardcoded-pii +- **severity:** low +- **detail:** The Containerfile embeds a git config with `email = bupdprasanth@gmail.com` and `signingkey = EFD822952819E418`. The email is the same as the commit author email and is already public. The GPG key ID is public information. +- **recommendation:** No action required — this is consistent with public git commits. Included for completeness. + +### F-12 — Gitignore Gap: certmanager-issuer-secret.yaml Tracked Despite Rule + +- **file:** `code/ks/.gitignore` / `code/ks/certmanager-issuer-secret.yaml` +- **line:** 8 (gitignore), all (secret file) +- **category:** gitignore-gap +- **severity:** critical +- **detail:** `.gitignore` contains `certmanager-issuer-secret.yaml` but the file was committed before the rule was added. Git does not retroactively untrack files added to `.gitignore`. The file remains tracked and its contents (Cloudflare API token) are in commit history. +- **recommendation:** (1) `git rm --cached certmanager-issuer-secret.yaml` to stop tracking. (2) Scrub from history with `git filter-repo`. (3) See F-01 for token rotation. + +### F-13 — Gitignore Gap: keycloak/.env Tracked Despite Rule + +- **file:** `code/ks/.gitignore` / `code/ks/keycloak/.env` +- **line:** 19 (gitignore), all (env file) +- **category:** gitignore-gap +- **severity:** critical +- **detail:** `.gitignore` lists `keycloak/.env` but the file was committed before the rule was added and remains tracked. Contains plaintext admin and database passwords. +- **recommendation:** (1) `git rm --cached keycloak/.env` to stop tracking. (2) Scrub from history. (3) See F-03 for password rotation. + +### F-14 — Gitignore Gap: dotfiles Missing Coverage for Auth Tokens + +- **file:** `dotfiles/.gitignore` +- **line:** (entire file) +- **category:** gitignore-gap +- **severity:** high +- **detail:** The dotfiles `.gitignore` only covers `state.yml`, `.zsh_history`, `.wakatime.cfg`, and `.zshrc`. It does NOT cover: `.config/gh/hosts.yml` (GitHub OAuth tokens), `*.pem`, `*.key`, `credentials.*`, `secrets.*`, or `.env*`. A `git add .` could accidentally commit sensitive files. +- **recommendation:** Add the following patterns to `dotfiles/.gitignore`: + ``` + .config/gh/hosts.yml + *.pem + *.key + credentials.* + secrets.* + .env + .env.* + ``` + +### F-15 — Gitignore Gap: ks Repo Missing Generic Secret Patterns + +- **file:** `code/ks/.gitignore` +- **line:** (entire file) +- **category:** gitignore-gap +- **severity:** medium +- **detail:** The ks `.gitignore` covers specific secret files by name but lacks generic patterns for `*.pem`, `*.key`, `credentials.*`, `secrets.*`, and `.env*` (only `*-cred.env` is covered). New secret files with common names could be accidentally committed. +- **recommendation:** Add generic patterns: `*.pem`, `*.key`, `credentials.*`, `secrets.*`, `.env`, `.env.*`. + +### F-16 — DB and Admin Passwords in gc-chart PostgreSQL Config + +- **file:** `code/ks/gc-chart/values.yaml` +- **line:** 54-57 +- **category:** unencrypted-storage +- **severity:** high +- **detail:** The PostgreSQL subchart auth block stores `password`, `username`, and `database` in plaintext in a tracked values file. These are used directly by the PostgreSQL Helm chart to configure the database credentials. +- **recommendation:** (1) Use `existingSecret` to reference a Kubernetes Secret instead of inline plaintext. (2) Create a gitignored `values-secrets.yaml` overlay for sensitive values. + +### F-17 — Default/Weak Password Pattern Across Multiple Files + +- **file:** Multiple (see below) +- **line:** Various +- **category:** unencrypted-storage +- **severity:** medium +- **detail:** Harbor's well-known default password is reused across multiple tracked files as an admin password for Harbor, Gitea, and Nexus. Files: `gc-chart/values.yaml:30`, `files/harbor/values.yaml:13`, `reports/gitea-session-report.md:96,99`, `reports/nexus-session-report.md:216`, `code/arch-bootc-hetzner/Containerfile.harbor:27` (in comment). Using a default password across services increases blast radius if any one is compromised. +- **recommendation:** (1) Use unique, strong passwords per service. (2) Store all passwords in a secret manager, not in tracked files. (3) Redact passwords from session reports. + +### F-18 — Prometheus Admin Credentials Tracked in Plaintext + +- **file:** `code/ks/prom-stack/admin-user`, `code/ks/prom-stack/admin-password` +- **line:** 1 (each file) +- **category:** env-secret +- **severity:** high +- **detail:** Two tracked files contain plaintext Prometheus/Grafana admin credentials (`admin-user` contains ``, `admin-password` contains ``). These files are NOT covered by `.gitignore` and are fully exposed in the public repository. +- **recommendation:** (1) Add `prom-stack/admin-user` and `prom-stack/admin-password` to `.gitignore`. (2) `git rm --cached` both files. (3) Use a Kubernetes Secret or sealed secret for Prometheus admin credentials instead. + +### F-19 — Shell Script Echoes Credential Variables (Low Risk) + +- **file:** `code/ks/reg-pull-secret-gen.sh` +- **line:** 11 +- **category:** pii-in-logs +- **severity:** low +- **detail:** The script constructs a base64 auth string from `$USERNAME:$PASSWORD` and outputs a Kubernetes Secret YAML containing the encoded credentials to stdout. The credentials come from interactive `read` prompts, not hardcoded values, and the output is intended to be piped or copied. This is by design but could leak credentials in shell history or terminal scrollback. +- **recommendation:** (1) Consider writing output to a file instead of stdout. (2) Use `read -s` for the password prompt to prevent echo. (3) Add the generated YAML file to `.gitignore`. + +### F-20 — Harbor Init Script Logs Default Admin Password + +- **file:** `code/arch-bootc-hetzner/files/harbor/harbor-init.sh` +- **line:** 25 +- **category:** pii-in-logs +- **severity:** medium +- **detail:** The Harbor initialization script logs the admin username and default password to stdout via `log " Login: admin / "`. This prints the credential to terminal output and container logs at runtime, where it could be captured by log aggregation systems. +- **recommendation:** (1) Remove the password from log output — print only the username and refer to a secret source. (2) If the password is only for initial setup, log a message directing the user to change it without revealing the value. + +### F-21 — Real Email in Helm Chart Maintainer Fields + +- **file:** `code/ks/gc-chart/Chart.yaml` +- **line:** 11, 13 +- **category:** hardcoded-pii +- **severity:** low +- **detail:** Helm chart maintainer fields contain real email addresses (`bupdprasanth@gmail.com`, `vadim@8gears.com`). This is standard practice for Helm charts and the emails are already associated with public profiles. +- **recommendation:** No action required. Standard Helm chart metadata. Included for completeness. + +--- + +## Summary + +### By Category + +| Category | Count | IDs | +|---|---|---| +| env-secret | 6 | F-01, F-02, F-03, F-04, F-05, F-18 | +| hardcoded-pii | 6 | F-06, F-07, F-09, F-10, F-11, F-21 | +| gitignore-gap | 4 | F-12, F-13, F-14, F-15 | +| unencrypted-storage | 3 | F-08, F-16, F-17 | +| pii-in-logs | 2 | F-19, F-20 | + +### By Severity + +| Severity | Count | IDs | +|---|---|---| +| critical | 4 | F-01, F-03, F-12, F-13 | +| high | 8 | F-02, F-04, F-05, F-06, F-07, F-14, F-16, F-18 | +| medium | 5 | F-08, F-10, F-15, F-17, F-20 | +| low | 4 | F-09, F-11, F-19, F-21 | +| **Total** | **21** | | + +### Priority Actions + +1. **Immediate:** Rotate the Cloudflare API token (F-01) and all Keycloak/Postgres passwords (F-03). These are live credentials exposed in a public repo. +2. **Urgent:** `git rm --cached` the two tracked-but-gitignored files (F-12, F-13) and scrub git history. +3. **Soon:** `git rm --cached` Prometheus credential files and add to `.gitignore` (F-18). Add missing `.gitignore` patterns to dotfiles and ks repos (F-14, F-15). Redact passwords from session reports (F-06, F-07). Move Helm chart secrets to gitignored overlays or sealed secrets (F-04, F-05, F-16). +4. **Low priority:** Consider decoupling SSH key and email from Containerfile (F-09, F-11). Add `read -s` to secret generation script (F-19). Remove password from harbor-init.sh log output (F-20).