From 2f243f23c3b0ad0233480a87b852cb249f92c919 Mon Sep 17 00:00:00 2001 From: Sluijsens Date: Tue, 28 Jul 2026 19:41:34 +0200 Subject: [PATCH] Fixes the account model deploy setup got wrong webadmin turned out to be FTP-only and belongs to whoever uploads customer websites, not to this pipeline. Switched to a separate deploy account with its own home-directory paths, well away from where the other sites live, and made the persistent website symlink cross into webadmin's territory instead of the other way around. --- .../gitea-deployment-workflow/aidlc-state.md | 8 ++ .../gitea-deployment-workflow/audit.md | 22 +++++ .../deployment/deployment-instructions.md | 88 ++++++++++++++----- .../operations/deployment/deployment-plan.md | 8 ++ .../deployment-clarification-questions.md | 51 +++++++++++ 5 files changed, 157 insertions(+), 20 deletions(-) create mode 100644 aidlc-docs/features/gitea-deployment-workflow/operations/plans/deployment-clarification-questions.md diff --git a/aidlc-docs/features/gitea-deployment-workflow/aidlc-state.md b/aidlc-docs/features/gitea-deployment-workflow/aidlc-state.md index 9155e3b..657ee8e 100644 --- a/aidlc-docs/features/gitea-deployment-workflow/aidlc-state.md +++ b/aidlc-docs/features/gitea-deployment-workflow/aidlc-state.md @@ -46,6 +46,14 @@ procedure drafted (Q5 = B), with an explicit caveat that shared hosting is very likely IIS-based, so systemd-restart and atomic-symlink-switch do not carry over unchanged — treated as a starting brief for a future Infrastructure Design pass, not a ready-to-execute procedure +- **Revised after user feedback (real host facts)**: `webadmin` (the Pi's FileZilla/SFTP account, + root `/mnt/storage1/www/`) cannot SSH in and is not the deploy account — it's the + `WEBSITE_WORKSPACE.md` website-author role. A **separate, dedicated** SSH-capable account now runs + the deploy pipeline (`PI_MAIN_USERNAME`), with deploy paths under that account's own home + directory rather than under `/mnt/storage1/www/html/`, so the CMS's release structure never + interferes with the other hosted websites. `shared/wwwroot-web` is now a cross-account symlink to + wherever `webadmin` uploads this customer's site, requiring a one-time shared-group permission + setup — documented, not automated - **Artifacts**: `operations/deployment/deployment-plan.md`, `deployment-instructions.md`, `rollback-plan.md` diff --git a/aidlc-docs/features/gitea-deployment-workflow/audit.md b/aidlc-docs/features/gitea-deployment-workflow/audit.md index 15fb8f0..7323591 100644 --- a/aidlc-docs/features/gitea-deployment-workflow/audit.md +++ b/aidlc-docs/features/gitea-deployment-workflow/audit.md @@ -1257,3 +1257,25 @@ by Infrastructure Design. U5 carries REF-U3-01's Umami origin gate plus FR-21 an **Context**: OPERATIONS — Deployment Setup complete, presenting completion message --- +## Deployment Setup — Real Host Facts Surface a Gap (Clarification Needed) + +**Timestamp**: 2026-07-28T03:40:00Z + +**User Input**: "Ik weet niet wat er als locatie werd gekozen voor alle bestanden en applicaties, maar het pad op de pi waar alle websites in komen is /mnt/storage1/www/html/. De gebruiker webadmin kan niet via ssh inloggen. Dus als we die willen gebruiken moet er wat veranderd worden. Bij filezilla logt webadmin in op locatie /mnt/storage1/www/ wat daar de root / is." + +**AI Response**: "This surfaces a real gap: deploy-scp.yaml needs SSH shell access (mkdir, ln -sfn, systemctl --user restart, the backup script), not just SFTP file transfer, which is what webadmin apparently has via FileZilla. Rather than guess which account to use or assume webadmin's role, created deployment-clarification-questions.md asking: (1) which account the deploy pipeline should use — grant webadmin shell access, reuse the reference project's existing SSH-capable account, or create a new one; (2) the real directory placement under /mnt/storage1/www/html/; (3) whether webadmin is actually meant for the WEBSITE_WORKSPACE.md website-author role rather than the CMS's own deploy pipeline." + +**Context**: OPERATIONS — Deployment Setup, real host facts revealed an access-model gap; awaiting clarification before correcting deployment-instructions.md + +--- +## Deployment Setup — Revised for Real Host Facts + +**Timestamp**: 2026-07-28T03:50:00Z + +**User Input**: "Ik heb de vragen ingevuld, ga verder" (Q1=C preferred/A fallback, Q2=X — deploy path should live in Gitea variables and can be a separate path next to html/, Q3=X — webadmin for website uploads, separate account for the pipeline) + +**AI Response**: "Revised deployment-instructions.md and deployment-plan.md: webadmin stays FileZilla/SFTP-only for website-workspace authors; a new dedicated SSH-capable account runs the deploy pipeline instead (with a documented fallback to granting webadmin shell access only if a new account isn't feasible). Deploy paths moved to that account's own home directory, away from /mnt/storage1/www/html/, confirming the existing DEPLOY_PATH_* Gitea variables already make this configurable without a workflow change. shared/wwwroot-web is now documented as a cross-account symlink to wherever webadmin uploads the customer's site, with a one-time shared-group permission setup noted as a host-setup requirement. Renumbered affected sections (1.1-1.9) and fixed all internal cross-references." + +**Context**: OPERATIONS — Deployment Setup artifacts corrected before final approval + +--- diff --git a/aidlc-docs/features/gitea-deployment-workflow/operations/deployment/deployment-instructions.md b/aidlc-docs/features/gitea-deployment-workflow/operations/deployment/deployment-instructions.md index df81933..e4fbe90 100644 --- a/aidlc-docs/features/gitea-deployment-workflow/operations/deployment/deployment-instructions.md +++ b/aidlc-docs/features/gitea-deployment-workflow/operations/deployment/deployment-instructions.md @@ -14,12 +14,34 @@ assumes already exists — `deploy-scp.yaml` (U6) never creates any of it. ### 1.1 Prerequisites - .NET 10 runtime installed on the Pi (ASM-03) — the publish is framework-dependent (`infrastructure-design.md` § 1), so the Pi needs the runtime, not the full SDK -- SSH access for the deploy user, password auth for now (matches the reference project; - SSH-key migration remains a documented future step, same as there) - `sqlcmd` installed, for the backup script (§ 4) — e.g. `mssql-tools18` / `unixodbc` on Debian-based Raspberry Pi OS -### 1.2 Enable Lingering (INFRA-U6-01 — do this first, easy to forget) +### 1.2 Account Model (revised — `webadmin` is not the deploy account) + +Clarified during Deployment Setup: `webadmin` is the FileZilla/SFTP account website-workspace +authors use to upload customer sites under `/mnt/storage1/www/html/` (`WEBSITE_WORKSPACE.md`'s +role) — it cannot SSH in, and should **stay** SFTP-only. `deploy-scp.yaml` needs a real SSH shell +(for `mkdir`, `ln -sfn`, `systemctl --user restart`, the backup script), which is a different kind +of access than FileZilla uses. + +**Preferred**: create a separate, dedicated account for the deploy pipeline — e.g. +`slpmodularcms-deploy` (rename freely; nothing in the workflow hardcodes this name, it only reads +`PI_MAIN_USERNAME`). This keeps the two roles cleanly apart, matching your stated preference. +```bash +sudo useradd -m -s /bin/bash slpmodularcms-deploy +sudo passwd slpmodularcms-deploy +``` +**Fallback, only if a new account genuinely isn't feasible** (e.g. a hosting provider that doesn't +allow arbitrary new system accounts): grant `webadmin` SSH shell access instead +(`sudo usermod -s /bin/bash webadmin`, plus enabling SSH password/key auth for it if currently +blocked at the `sshd_config` level). This merges the two roles onto one account — acceptable as a +fallback, but worth revisiting later, since it means a website-workspace author's FTP credential +would also be able to run shell commands on the Pi. + +The rest of this document uses `` — substitute whichever of the two you actually use. + +### 1.3 Enable Lingering (INFRA-U6-01 — do this first, easy to forget) ```bash sudo loginctl enable-linger ``` @@ -29,15 +51,38 @@ common "Failed to connect to bus" error `systemctl --user` can throw when invoke non-interactive SSH command — lingering keeps the user's systemd instance (and `XDG_RUNTIME_DIR`) running independent of any login session. -### 1.3 Directory Skeleton -Run once per environment (`test`, `production`): +### 1.4 Directory Skeleton + +Deliberately placed under ``'s **own home directory**, not under +`/mnt/storage1/www/html/` — since it's now a separate account from `webadmin`, there is no reason +for the CMS's own release/current/shared structure to live anywhere near the other websites at all, +which directly avoids interfering with them (as you asked in Q2): + ```bash mkdir -p ~/apps/slpmodularcms-/releases -mkdir -p ~/apps/slpmodularcms-/shared/wwwroot-web ``` + `current` is created by the first deploy itself (`ln -sfn`) — don't pre-create it. -### 1.4 Runtime Configuration File +**`shared/wwwroot-web` is the one exception** — it must resolve to wherever `webadmin` actually +uploads *this* customer's website via FileZilla, e.g. `/mnt/storage1/www/html/slpmodularcms-/` +(adjust the exact folder name to whatever convention the other sites under `html/` already use, if +one exists). Rather than a plain directory, make it a symlink across accounts: + +```bash +mkdir -p ~/apps/slpmodularcms- +ln -s /mnt/storage1/www/html/slpmodularcms- ~/apps/slpmodularcms-/shared/wwwroot-web +``` + +`deploy-scp.yaml`'s existing logic (`releases/{ts}/wwwroot/web -> ../../../shared/wwwroot-web`) +needs no changes for this — it only ever resolves the symlink chain, it doesn't care how many hops +that chain has. What **does** need attention: `` needs read + traverse permission on +`/mnt/storage1/www/html/slpmodularcms-/` and its parent directories, which `webadmin` owns. +Simplest fix: put both accounts in a shared group (e.g. `webshared`), `chgrp -R webshared` that +folder, and make sure webadmin's FTP server creates new uploads group-readable (`g+rx`, not just +owner-readable) — a one-time permission setup, not something either pipeline touches per deploy. + +### 1.5 Runtime Configuration File One file per environment, **outside** the release directory so it survives every switch: ```bash touch ~/apps/slpmodularcms-/shared/env @@ -63,11 +108,11 @@ occupies them. `ASPNETCORE_ENVIRONMENT=Production` is used for **both** environm should be true for anything reachable at a real domain, including test. **`SecurityHeaders__AllowedScriptOrigins__0` / `_AllowedConnectOrigins__0` must exactly match** the -Gitea variables `SECURITY_ALLOWED_SCRIPT_ORIGINS_TEST` / `_PRODUCTION` (see § 3) — REF-U5-01's CI +Gitea variables `SECURITY_ALLOWED_SCRIPT_ORIGINS_TEST` / `_PRODUCTION` (see § 1.9) — REF-U5-01's CI gate only catches drift between the frontend build and that Gitea variable; it cannot see this file, so keeping the two in sync is a manual discipline, not something enforced automatically. -### 1.5 systemd User Units +### 1.6 systemd User Units Create `~/.config/systemd/user/slpmodularcms-test.service`: ```ini [Unit] @@ -96,7 +141,7 @@ systemctl --user enable slpmodularcms-test.service systemctl --user enable slpmodularcms-production.service ``` -### 1.6 nginx Routing +### 1.7 nginx Routing The existing reverse proxy (`infrastructure-design.md` § 1, § 4) needs a server block per domain, routing to the matching local port: ```nginx @@ -123,7 +168,7 @@ server { TLS certificate provisioning (e.g. certbot) is existing host operations, out of scope for this feature — assumed already handled the same way the reference project's domains are. -### 1.7 Database Backup Credentials (§ 4 depends on this) +### 1.8 Database Backup Credentials (§ 4 depends on this) ```bash touch ~/.config/slpmodularcms-db-backup.env chmod 600 ~/.config/slpmodularcms-db-backup.env @@ -134,21 +179,24 @@ DB_NAME=SlpModularCmsProduction DB_USER= DB_PASSWORD= ``` -Kept **separate** from `shared/env` (§ 1.4) deliberately — the backup script needs its own +Kept **separate** from `shared/env` (§ 1.5) deliberately — the backup script needs its own credential, ideally scoped to just `BACKUP DATABASE` permission rather than the application's own data-access login. -### 1.8 Gitea Actions Variables and Secrets -Set once, in this repository's Gitea Actions settings: +### 1.9 Gitea Actions Variables and Secrets +Set once, in this repository's Gitea Actions settings. This is exactly where the real path lives — +`deploy-scp.yaml` never hardcodes it, it only reads `${{ vars.DEPLOY_PATH_TEST }}` / +`${{ vars.DEPLOY_PATH_PRODUCTION }}`, so changing the path later is a variable edit, not a workflow +change: | Name | Kind | Value | |---|---|---| | `PI_MAIN_ADDRESS` | secret | the Pi's address | | `PI_MAIN_PORT` | secret | SSH port | -| `PI_MAIN_USERNAME` | secret | deploy user | -| `PI_MAIN_PASSWORD` | secret | deploy user's password | -| `DEPLOY_PATH_TEST` | variable | `/home//apps/slpmodularcms-test` | -| `DEPLOY_PATH_PRODUCTION` | variable | `/home//apps/slpmodularcms-production` | +| `PI_MAIN_USERNAME` | secret | `` — the new dedicated account (§ 1.2), **not** `webadmin` | +| `PI_MAIN_PASSWORD` | secret | ``'s password | +| `DEPLOY_PATH_TEST` | variable | `/home//apps/slpmodularcms-test` | +| `DEPLOY_PATH_PRODUCTION` | variable | `/home//apps/slpmodularcms-production` | | `SERVICE_NAME_TEST` | variable | `slpmodularcms-test.service` | | `SERVICE_NAME_PRODUCTION` | variable | `slpmodularcms-production.service` | | `HEALTH_CHECK_URL_TEST` | variable | `https://test.slpsoftware.nl/health` | @@ -156,7 +204,7 @@ Set once, in this repository's Gitea Actions settings: | `VITE_SENTRY_DSN` | variable | Sentry DSN (shared, not sensitive — safe in the client bundle) | | `VITE_UMAMI_SCRIPT_URL` | variable | Umami script host (shared) | | `VITE_UMAMI_WEBSITE_ID_TEST` / `_PRODUCTION` | variable | per-environment Umami website ID | -| `SECURITY_ALLOWED_SCRIPT_ORIGINS_TEST` / `_PRODUCTION` | variable | **must match** § 1.4's `SecurityHeaders__AllowedScriptOrigins__0` for that environment | +| `SECURITY_ALLOWED_SCRIPT_ORIGINS_TEST` / `_PRODUCTION` | variable | **must match** § 1.5's `SecurityHeaders__AllowedScriptOrigins__0` for that environment | --- @@ -194,7 +242,7 @@ ENVIRONMENT="${1:?Usage: backup-slpmodularcms-db.sh }" CREDENTIALS_FILE="$HOME/.config/slpmodularcms-db-backup.env" if [[ ! -f "$CREDENTIALS_FILE" ]]; then - echo "Missing $CREDENTIALS_FILE — see deployment-instructions.md § 1.7" >&2 + echo "Missing $CREDENTIALS_FILE — see deployment-instructions.md § 1.8" >&2 exit 1 fi # shellcheck source=/dev/null diff --git a/aidlc-docs/features/gitea-deployment-workflow/operations/deployment/deployment-plan.md b/aidlc-docs/features/gitea-deployment-workflow/operations/deployment/deployment-plan.md index c819ade..a25513a 100644 --- a/aidlc-docs/features/gitea-deployment-workflow/operations/deployment/deployment-plan.md +++ b/aidlc-docs/features/gitea-deployment-workflow/operations/deployment/deployment-plan.md @@ -25,6 +25,14 @@ procedure. Same physical host for both (per `infrastructure-design.md` § 1) — separated by directory, `systemd --user` unit, and local port, never by anything the workflow manages directly. +**Account model (revised at Deployment Setup)**: the Pi's existing `webadmin` account is FileZilla/ +SFTP-only, used by website-workspace authors uploading customer sites under +`/mnt/storage1/www/html/` (`WEBSITE_WORKSPACE.md`'s role) — it cannot SSH in and stays that way. The +deploy pipeline uses a **separate, dedicated** SSH-capable account instead +(`deployment-instructions.md` § 1.2), so the two roles never overlap on one credential. Deploy paths +live under that account's own home directory, not under `html/` — this was a deliberate choice to +avoid the CMS's release/current/shared structure interfering with the other websites hosted there. + ## Rationale for What's Documented Here vs. Already Decided | Already decided (Construction) | Documented here (Operations) | diff --git a/aidlc-docs/features/gitea-deployment-workflow/operations/plans/deployment-clarification-questions.md b/aidlc-docs/features/gitea-deployment-workflow/operations/plans/deployment-clarification-questions.md new file mode 100644 index 0000000..77d3479 --- /dev/null +++ b/aidlc-docs/features/gitea-deployment-workflow/operations/plans/deployment-clarification-questions.md @@ -0,0 +1,51 @@ +# Deployment Setup — Clarification Questions + +You mentioned `/mnt/storage1/www/html/` as the real path convention, and that `webadmin` (the +FileZilla account) cannot log in via SSH. `deploy-scp.yaml` (built in U6) needs more than file +transfer — it runs `mkdir`, `ln -sfn`, `systemctl --user restart`, and the backup script over an SSH +**shell** command, not just SFTP. That needs a genuinely different kind of access than FileZilla +uses, so I want to confirm rather than guess. + +## Question 1: Which Account Should the Deploy Pipeline Use? + +A) `webadmin` is the only account that exists for this Pi's web content — grant it SSH **shell** + access (not just SFTP) so the deploy workflow can use it +B) There's a different, existing account with full SSH shell access already (e.g. whatever the + reference `SlpSoftware` project's working pipeline uses) — reuse that one for + `PI_MAIN_USERNAME`, keep `webadmin` purely for FileZilla/manual use +C) Create a brand-new dedicated account for the deploy pipeline, separate from both `webadmin` and + whatever the reference project uses +D) Other (please describe after [Answer]: tag below) + +[Answer]: C, dit is het mooiste, maar is dta haalbaar. Anders terugvallen naar optie A + +## Question 2: Directory Placement Under `/mnt/storage1/www/html/` + +`deployment-instructions.md` currently assumes `~/apps/slpmodularcms-/` as the deploy path — +that needs to change to match this Pi's real convention. What should the two environments' paths be? + +A) `/mnt/storage1/www/html/slpmodularcms-test/` and `/mnt/storage1/www/html/slpmodularcms-production/` + — siblings, same naming pattern I already used +B) A different naming convention is already used for other sites under `html/` — describe the + pattern to follow after [Answer]: below +C) Not sure yet — pick reasonable names, I'll rename if it clashes with something already there + +X) Other (please describe after [Answer]: tag below) + +[Answer]: X, zou de deploy path niet in de variabelen moeten staan van gitea? En dan vanuit daar bepalen waar de boel komt? Het is eventueel mogelijk een los pad aan te geven naast de html-map zodat het niet in de weg gaat zitten met de (andere) websites + +## Question 3: Does `webadmin`'s FileZilla Access Relate to the Website Workspace Contract? + +`WEBSITE_WORKSPACE.md` (U7) describes how a separate website-workspace author deploys the public +site into `wwwroot/web/`. Is `webadmin`/FileZilla actually **that** role — the account a website +builder uses to upload the customer's site — rather than the account meant to run the CMS's own +deploy pipeline? + +A) Yes — `webadmin` is for website-workspace authors (FTPS into `wwwroot/web/`), completely separate + from whatever account runs the CI/CD deploy pipeline +B) No — `webadmin` was meant to be used for everything, including the CMS deploy pipeline itself +C) Not decided yet / don't know + +X) Other (please describe after [Answer]: tag below) + +[Answer]: X, zoals in vraag 1 aangegeven zou het het beste zijn als webadmin is voor het uploaden van websites via filezilla en dat er een apart pipeline gebruiker is.