mirror of
https://codeberg.org/PostERG/xamxam.git
synced 2026-05-06 11:09:18 +02:00
fix rsync permissions: setup-server.sh with setgid dirs, exclude .claude/.pi
This commit is contained in:
4
TODO.md
4
TODO.md
@@ -40,6 +40,10 @@
|
|||||||
|
|
||||||
## Admin / Server
|
## Admin / Server
|
||||||
|
|
||||||
|
- [x] Create `scripts/setup-server.sh` (one-time server setup: group, ownership, setgid 2775 on dirs)
|
||||||
|
- [x] Add `just setup-server` recipe (rsync + run setup-server.sh on remote)
|
||||||
|
- [x] Exclude `.claude` and `.pi` from rsync deploy
|
||||||
|
- [x] Update `docs/SERVER_SETUP.md` with correct permissions rationale and troubleshooting
|
||||||
- [ ] Add server status view in admin panel (nginx + php-fpm health, site HTTP check)
|
- [ ] Add server status view in admin panel (nginx + php-fpm health, site HTTP check)
|
||||||
- [ ] Add server log viewer in admin panel (tail nginx error/access logs via SSH or log endpoint)
|
- [ ] Add server log viewer in admin panel (tail nginx error/access logs via SSH or log endpoint)
|
||||||
- [ ] Add nginx config deploy flow to admin panel (upload `scripts/deploy-server.sh`, run remotely)
|
- [ ] Add nginx config deploy flow to admin panel (upload `scripts/deploy-server.sh`, run remotely)
|
||||||
|
|||||||
@@ -1,18 +1,42 @@
|
|||||||
# Server Setup
|
# Server Setup
|
||||||
|
|
||||||
## One-time setup on server
|
## One-time setup (before first deploy)
|
||||||
|
|
||||||
|
Run the setup script on the server. It creates `/var/www/posterg`, sets the
|
||||||
|
correct ownership/permissions, and adds the deploy user to the `posterg` group:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
ssh posterg
|
just setup-server
|
||||||
sudo mkdir -p /var/www/posterg
|
|
||||||
sudo chown www-data:posterg /var/www/posterg
|
|
||||||
sudo chmod 775 /var/www/posterg
|
|
||||||
exit
|
|
||||||
```
|
```
|
||||||
|
|
||||||
## Deploying the application
|
What the script does:
|
||||||
|
- Creates the `posterg` group if it doesn't exist
|
||||||
|
- Adds both the SSH user (read from `~/.ssh/config` via `ssh -G posterg`) and `www-data` to `posterg`
|
||||||
|
- Creates `/var/www/posterg` owned by `www-data:posterg`
|
||||||
|
- Sets all directories to **2775** (`rwxrws r-x`) — the setgid bit ensures
|
||||||
|
new files/dirs inherit the `posterg` group, which is required for
|
||||||
|
`rsync --chown=www-data:posterg` to succeed
|
||||||
|
- Sets files to **664**
|
||||||
|
- Sets `storage/` to **2775**, database files to **660**
|
||||||
|
|
||||||
Files are pushed via rsync — there is no repo on the server.
|
> **Important:** After running `setup-server`, log out and back in on the server
|
||||||
|
> (or run `newgrp posterg`) so the new group membership is active before deploying.
|
||||||
|
|
||||||
|
### Why setgid (2775) on directories?
|
||||||
|
|
||||||
|
rsync uses `--chown=www-data:posterg` to set ownership on transferred files.
|
||||||
|
For this to work, the receiving process (running as `padlock`) must have write
|
||||||
|
permission on every target directory. Without the setgid bit:
|
||||||
|
- Newly created subdirectories inherit `padlock`'s primary group
|
||||||
|
- `www-data` (nginx/php-fpm) can't write to them → 403 errors
|
||||||
|
- `padlock` can't write to dirs owned by `www-data` → rsync Permission denied
|
||||||
|
|
||||||
|
With `2775 + group=posterg`:
|
||||||
|
- Both `padlock` and `www-data` are in `posterg` → both can write
|
||||||
|
- New subdirs automatically get `posterg` as their group
|
||||||
|
- rsync can create files and directories without errors
|
||||||
|
|
||||||
|
## Deploying the application
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Push all app files
|
# Push all app files
|
||||||
@@ -24,7 +48,8 @@ just deploy-db
|
|||||||
|
|
||||||
## Applying the nginx config
|
## Applying the nginx config
|
||||||
|
|
||||||
The config is in `nginx/posterg.conf`. Upload it and run the deploy script on the server:
|
The config is in `nginx/posterg.conf`. Upload it and run the deploy script on
|
||||||
|
the server:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
rsync -v nginx/posterg.conf posterg:/tmp/posterg.conf
|
rsync -v nginx/posterg.conf posterg:/tmp/posterg.conf
|
||||||
@@ -32,8 +57,8 @@ ssh posterg "sudo bash /var/www/posterg/scripts/deploy-server.sh"
|
|||||||
ssh posterg "sudo systemctl reload nginx"
|
ssh posterg "sudo systemctl reload nginx"
|
||||||
```
|
```
|
||||||
|
|
||||||
`scripts/deploy-server.sh` fixes ownership/permissions and installs the nginx config
|
`scripts/deploy-server.sh` fixes ownership/permissions and installs the nginx
|
||||||
from `/tmp/posterg.conf`. It must be run as root.
|
config from `/tmp/posterg.conf`. It must be run as root.
|
||||||
|
|
||||||
## Managing admin users
|
## Managing admin users
|
||||||
|
|
||||||
@@ -41,24 +66,51 @@ from `/tmp/posterg.conf`. It must be run as root.
|
|||||||
ssh posterg "sudo bash /var/www/posterg/scripts/manage-admin-users.sh"
|
ssh posterg "sudo bash /var/www/posterg/scripts/manage-admin-users.sh"
|
||||||
```
|
```
|
||||||
|
|
||||||
This is an interactive menu for adding, changing, and deleting htpasswd entries
|
Interactive menu for adding, changing, and deleting htpasswd entries at
|
||||||
at `/etc/nginx/.htpasswd-posterg`.
|
`/etc/nginx/.htpasswd-posterg`.
|
||||||
|
|
||||||
## Troubleshooting
|
## Troubleshooting
|
||||||
|
|
||||||
### Nginx 403 Forbidden
|
### rsync: Permission denied on mkdir or mkstemp
|
||||||
|
|
||||||
|
The remote directory permissions are wrong. Run:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
just setup-server
|
||||||
|
```
|
||||||
|
|
||||||
|
Then log out/in on the server and retry `just deploy`.
|
||||||
|
|
||||||
|
If you need to fix it manually (replace `youruser` with your remote username):
|
||||||
|
|
||||||
|
```bash
|
||||||
|
ssh posterg
|
||||||
|
sudo DEPLOY_USER=youruser bash /tmp/setup-server.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
Or directly:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
ssh posterg
|
ssh posterg
|
||||||
sudo chown -R www-data:posterg /var/www/posterg
|
sudo chown -R www-data:posterg /var/www/posterg
|
||||||
sudo find /var/www/posterg -type d -exec chmod 755 {} \;
|
sudo find /var/www/posterg -type d -exec chmod 2775 {} \;
|
||||||
sudo find /var/www/posterg -type f -exec chmod 644 {} \;
|
sudo find /var/www/posterg -type f -exec chmod 664 {} \;
|
||||||
sudo chmod 775 /var/www/posterg/storage
|
sudo usermod -aG posterg youruser
|
||||||
|
```
|
||||||
|
|
||||||
|
### Nginx 403 Forbidden
|
||||||
|
|
||||||
|
```bash
|
||||||
|
ssh posterg
|
||||||
|
sudo find /var/www/posterg -type d -exec chmod 2775 {} \;
|
||||||
|
sudo find /var/www/posterg -type f -exec chmod 664 {} \;
|
||||||
sudo chmod 660 /var/www/posterg/storage/*.db
|
sudo chmod 660 /var/www/posterg/storage/*.db
|
||||||
```
|
```
|
||||||
|
|
||||||
### Database permission error
|
### Database permission error
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
ssh posterg
|
ssh posterg
|
||||||
sudo chown www-data:posterg /var/www/posterg/storage/test.db
|
sudo chown www-data:posterg /var/www/posterg/storage/posterg.db
|
||||||
sudo chmod 660 /var/www/posterg/storage/test.db
|
sudo chmod 660 /var/www/posterg/storage/posterg.db
|
||||||
```
|
```
|
||||||
|
|||||||
14
justfile
14
justfile
@@ -37,6 +37,8 @@ deploy:
|
|||||||
--exclude '*.md' \
|
--exclude '*.md' \
|
||||||
--exclude '.git*' \
|
--exclude '.git*' \
|
||||||
--exclude '.jj' \
|
--exclude '.jj' \
|
||||||
|
--exclude '.claude' \
|
||||||
|
--exclude '.pi' \
|
||||||
--exclude '.DS_Store' \
|
--exclude '.DS_Store' \
|
||||||
--exclude 'storage/backup_*' \
|
--exclude 'storage/backup_*' \
|
||||||
--exclude 'storage/fixtures' \
|
--exclude 'storage/fixtures' \
|
||||||
@@ -48,12 +50,12 @@ deploy:
|
|||||||
--exclude 'var/cache/*' \
|
--exclude 'var/cache/*' \
|
||||||
--exclude 'var/logs/*' \
|
--exclude 'var/logs/*' \
|
||||||
./ posterg:/var/www/posterg/
|
./ posterg:/var/www/posterg/
|
||||||
ssh posterg "cd /var/www/posterg && \
|
ssh posterg "mkdir -p /var/www/posterg/var/{cache,logs,tmp}"
|
||||||
mkdir -p var/{cache,logs,tmp} && \
|
|
||||||
chown -R www-data:posterg . && \
|
[group('deploy')]
|
||||||
chmod -R 755 . && \
|
setup-server:
|
||||||
chmod -R 775 var/ storage/ && \
|
rsync -v scripts/setup-server.sh posterg:/tmp/setup-server.sh
|
||||||
chmod 660 storage/*.db 2>/dev/null || true"
|
ssh posterg "sudo DEPLOY_USER=$(ssh -G posterg | awk '/^user / {print $2}') bash /tmp/setup-server.sh"
|
||||||
|
|
||||||
[group('deploy')]
|
[group('deploy')]
|
||||||
deploy-db:
|
deploy-db:
|
||||||
|
|||||||
100
scripts/setup-server.sh
Executable file
100
scripts/setup-server.sh
Executable file
@@ -0,0 +1,100 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# One-time server setup for Post-ERG
|
||||||
|
# Run this before the first deploy (or after a permission reset).
|
||||||
|
#
|
||||||
|
# Usage: ssh posterg "sudo bash /tmp/setup-server.sh"
|
||||||
|
# Or: just setup-server
|
||||||
|
#
|
||||||
|
# What it does:
|
||||||
|
# 1. Creates /var/www/posterg with correct ownership and permissions
|
||||||
|
# 2. Ensures the deploy user is in the posterg group
|
||||||
|
# 3. Sets sticky group bit (setgid) on all directories so new files
|
||||||
|
# inherit the posterg group — required for rsync --chown to work
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
# ── Config ────────────────────────────────────────────────────────────────────
|
||||||
|
# DEPLOY_USER is passed explicitly by the justfile (read from ~/.ssh/config via
|
||||||
|
# `ssh -G posterg`). Falls back to $SUDO_USER if run manually with sudo.
|
||||||
|
DEPLOY_USER="${DEPLOY_USER:-${SUDO_USER}}"
|
||||||
|
[ -n "$DEPLOY_USER" ] || die "DEPLOY_USER is not set. Pass it explicitly: sudo DEPLOY_USER=youruser bash $0"
|
||||||
|
APP_DIR="/var/www/posterg"
|
||||||
|
APP_GROUP="posterg"
|
||||||
|
WEB_USER="www-data"
|
||||||
|
# ─────────────────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
RED='\033[0;31m'
|
||||||
|
GREEN='\033[0;32m'
|
||||||
|
YELLOW='\033[1;33m'
|
||||||
|
NC='\033[0m'
|
||||||
|
|
||||||
|
ok() { echo -e "${GREEN}✓${NC} $*"; }
|
||||||
|
warn() { echo -e "${YELLOW}!${NC} $*"; }
|
||||||
|
die() { echo -e "${RED}✗${NC} $*" >&2; exit 1; }
|
||||||
|
|
||||||
|
[ "$EUID" -eq 0 ] || die "Run as root (sudo)"
|
||||||
|
|
||||||
|
echo "🔧 Post-ERG Server Setup"
|
||||||
|
echo "========================"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# ── 1. Create posterg group ───────────────────────────────────────────────────
|
||||||
|
if ! getent group "$APP_GROUP" >/dev/null; then
|
||||||
|
groupadd "$APP_GROUP"
|
||||||
|
ok "Created group: $APP_GROUP"
|
||||||
|
else
|
||||||
|
ok "Group already exists: $APP_GROUP"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# ── 2. Add deploy user and web user to group ──────────────────────────────────
|
||||||
|
for user in "$DEPLOY_USER" "$WEB_USER"; do
|
||||||
|
if id "$user" &>/dev/null; then
|
||||||
|
if ! id -nG "$user" | grep -qw "$APP_GROUP"; then
|
||||||
|
usermod -aG "$APP_GROUP" "$user"
|
||||||
|
ok "Added $user to $APP_GROUP"
|
||||||
|
else
|
||||||
|
ok "$user already in $APP_GROUP"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
warn "User $user not found — skipping"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
# ── 3. Create app directory ───────────────────────────────────────────────────
|
||||||
|
mkdir -p "$APP_DIR"
|
||||||
|
ok "Ensured $APP_DIR exists"
|
||||||
|
|
||||||
|
# ── 4. Set ownership ──────────────────────────────────────────────────────────
|
||||||
|
chown -R "$WEB_USER:$APP_GROUP" "$APP_DIR"
|
||||||
|
ok "Ownership: $WEB_USER:$APP_GROUP on $APP_DIR"
|
||||||
|
|
||||||
|
# ── 5. Set directory permissions with setgid ──────────────────────────────────
|
||||||
|
# 2775 = rwxrwsr-x
|
||||||
|
# - owner (www-data) and group (posterg) can read/write/execute
|
||||||
|
# - setgid bit ensures new files/dirs inherit the posterg group
|
||||||
|
# - this is what allows rsync --chown=www-data:posterg to succeed
|
||||||
|
find "$APP_DIR" -type d -exec chmod 2775 {} \;
|
||||||
|
ok "Directories: 2775 (setgid) on $APP_DIR/**"
|
||||||
|
|
||||||
|
# ── 6. Set file permissions ───────────────────────────────────────────────────
|
||||||
|
find "$APP_DIR" -type f -exec chmod 664 {} \;
|
||||||
|
ok "Files: 664 on $APP_DIR/**"
|
||||||
|
|
||||||
|
# ── 7. Tighten storage ───────────────────────────────────────────────────────
|
||||||
|
if [ -d "$APP_DIR/storage" ]; then
|
||||||
|
chmod 2775 "$APP_DIR/storage"
|
||||||
|
find "$APP_DIR/storage" -name "*.db" -exec chmod 660 {} \;
|
||||||
|
ok "Storage: 2775, databases: 660"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo -e "${GREEN}✓ Setup complete.${NC}"
|
||||||
|
echo ""
|
||||||
|
echo "Next steps:"
|
||||||
|
echo " 1. Log out and back in as '$DEPLOY_USER' so group membership takes effect"
|
||||||
|
echo " (or run: newgrp $APP_GROUP)"
|
||||||
|
echo " 2. Run: just deploy"
|
||||||
|
echo ""
|
||||||
|
warn "If this is a fresh server, also run after first deploy:"
|
||||||
|
echo " just deploy-db # push initial database"
|
||||||
|
echo " just deploy-nginx # apply nginx config"
|
||||||
Reference in New Issue
Block a user