nextwks.sh: replace full root escalation with per-command sudo, files owned by user
This commit is contained in:
parent
d41d1443a8
commit
3ca19403fa
1 changed files with 72 additions and 73 deletions
145
tools/nextwks.sh
145
tools/nextwks.sh
|
|
@ -21,16 +21,10 @@ usage() {
|
|||
MODE="${1#--}"
|
||||
case "$MODE" in install|update|destroy) ;; *) usage ;; esac
|
||||
|
||||
# --- Auto-escalate to root if not already ---
|
||||
if [ "$(id -u)" -ne 0 ]; then
|
||||
echo "[*] Escalating to root..."
|
||||
exec sudo bash "$(realpath "$0")" "$@"
|
||||
fi
|
||||
|
||||
# Determine the non-root user for file ownership
|
||||
RUN_USER="${SUDO_USER:-${USER:-master}}"
|
||||
RUN_UID=$(id -u "$RUN_USER" 2>/dev/null || echo 1000)
|
||||
RUN_GID=$(id -g "$RUN_USER" 2>/dev/null || echo 1000)
|
||||
# Helper: run with sudo if not already root
|
||||
maybe_sudo() {
|
||||
if [ "$(id -u)" -eq 0 ]; then "$@"; else sudo "$@"; fi
|
||||
}
|
||||
|
||||
# --- Load existing env (if any) ---
|
||||
if [ -f "$BACKUP_DIR/.env" ]; then
|
||||
|
|
@ -47,18 +41,19 @@ echo "=== NextWorkspace ${MODE} ==="
|
|||
# ============================================================
|
||||
if [ "$MODE" = "install" ]; then
|
||||
echo "[*] Installing system dependencies..."
|
||||
apt-get update -qq && apt-get install -y -qq git build-essential curl podman podman-compose
|
||||
maybe_sudo apt-get update -qq
|
||||
maybe_sudo apt-get install -y -qq git build-essential curl podman podman-compose
|
||||
|
||||
if ! command -v go &>/dev/null; then
|
||||
echo "[*] Installing Go..."
|
||||
GO_VERSION=$(curl -sL https://go.dev/VERSION?m=text)
|
||||
GO_URL="https://go.dev/dl/${GO_VERSION}.linux-amd64.tar.gz"
|
||||
curl -sL "$GO_URL" -o /tmp/go.tar.gz
|
||||
rm -rf /usr/local/go
|
||||
tar -C /usr/local -xzf /tmp/go.tar.gz
|
||||
maybe_sudo rm -rf /usr/local/go
|
||||
maybe_sudo tar -C /usr/local -xzf /tmp/go.tar.gz
|
||||
rm /tmp/go.tar.gz
|
||||
echo 'export PATH=$PATH:/usr/local/go/bin' > /etc/profile.d/go.sh
|
||||
chmod +x /etc/profile.d/go.sh
|
||||
maybe_sudo sh -c 'echo "export PATH=\$PATH:/usr/local/go/bin" > /etc/profile.d/go.sh'
|
||||
maybe_sudo chmod +x /etc/profile.d/go.sh
|
||||
export PATH=$PATH:/usr/local/go/bin
|
||||
fi
|
||||
|
||||
|
|
@ -70,7 +65,7 @@ if [ "$MODE" = "install" ]; then
|
|||
while echo "$TLS_EMAIL" | grep -qv '@'; do read -p "Invalid email: " TLS_EMAIL; done
|
||||
read -p "Admin username: " ADMIN_USERNAME
|
||||
while [ -z "$ADMIN_USERNAME" ]; do read -p "Admin username (required): " ADMIN_USERNAME; done
|
||||
ADMIN_PASSWORD=$(openssl rand -hex 6) # 12 chars, no pipefail issues
|
||||
ADMIN_PASSWORD=$(openssl rand -hex 6)
|
||||
|
||||
echo ""
|
||||
echo "========================================"
|
||||
|
|
@ -92,8 +87,8 @@ if [ "$MODE" = "install" ]; then
|
|||
read -p "IMAP port [993]: " IMAP_PORT; IMAP_PORT="${IMAP_PORT:-993}"
|
||||
|
||||
# Persist config to backup vault
|
||||
mkdir -p "$BACKUP_DIR"
|
||||
cat > "$BACKUP_DIR/.env" <<EOF
|
||||
maybe_sudo mkdir -p "$BACKUP_DIR"
|
||||
maybe_sudo bash -c "cat > '$BACKUP_DIR/.env' <<EOF
|
||||
# NextWorkspace Configuration — auto-generated by nextwks.sh --install
|
||||
DOMAIN=$DOMAIN
|
||||
TLS_EMAIL=$TLS_EMAIL
|
||||
|
|
@ -105,8 +100,8 @@ SMTP_USER=$SMTP_USER
|
|||
SMTP_PASS=$SMTP_PASS
|
||||
IMAP_HOST=$IMAP_HOST
|
||||
IMAP_PORT=$IMAP_PORT
|
||||
EOF
|
||||
chmod 600 "$BACKUP_DIR/.env"
|
||||
EOF"
|
||||
maybe_sudo chmod 600 "$BACKUP_DIR/.env"
|
||||
fi
|
||||
|
||||
# ============================================================
|
||||
|
|
@ -119,10 +114,9 @@ cd "$BUILD_DIR"
|
|||
|
||||
# Save script to user's home for easy future access (--install only)
|
||||
if [ "$MODE" = "install" ]; then
|
||||
USER_HOME=$(eval echo "~${SUDO_USER:-}" 2>/dev/null || echo "$HOME")
|
||||
cp "$BUILD_DIR/tools/nextwks.sh" "$USER_HOME/nextwks.sh"
|
||||
chmod +x "$USER_HOME/nextwks.sh"
|
||||
echo "[*] Saved to $USER_HOME/nextwks.sh — use it for future updates"
|
||||
cp "$BUILD_DIR/tools/nextwks.sh" "$HOME/nextwks.sh"
|
||||
chmod +x "$HOME/nextwks.sh"
|
||||
echo "[*] Saved to $HOME/nextwks.sh — use it for future updates"
|
||||
fi
|
||||
|
||||
# ============================================================
|
||||
|
|
@ -133,58 +127,61 @@ export PATH=$PATH:/usr/local/go/bin
|
|||
CGO_ENABLED=0 go build -o nextworkspace .
|
||||
|
||||
# ============================================================
|
||||
# 4. CREATE target directory structure
|
||||
# 4. CREATE target & backup directories (as root)
|
||||
# ============================================================
|
||||
mkdir -p "$TARGET_DIR/config/caddy" "$TARGET_DIR/config/authelia" \
|
||||
maybe_sudo mkdir -p "$TARGET_DIR/config/caddy" "$TARGET_DIR/config/authelia" \
|
||||
"$TARGET_DIR/data/caddy" "$TARGET_DIR/data/authelia" \
|
||||
"$TARGET_DIR/compose" "$TARGET_DIR/www" \
|
||||
"$TARGET_DIR/config/nextworkspace" "$TARGET_DIR/logs"
|
||||
"$TARGET_DIR/config/nextworkspace" "$TARGET_DIR/logs" \
|
||||
"$BACKUP_DIR"
|
||||
|
||||
# ============================================================
|
||||
# 5. TEARDOWN (destroy mode only — wipes target dir)
|
||||
# ============================================================
|
||||
if [ "$MODE" = "destroy" ]; then
|
||||
echo "[*] Full teardown..."
|
||||
rm -rf "$TARGET_DIR"
|
||||
mkdir -p "$TARGET_DIR/config/caddy" "$TARGET_DIR/config/authelia" \
|
||||
maybe_sudo rm -rf "$TARGET_DIR"
|
||||
maybe_sudo mkdir -p "$TARGET_DIR/config/caddy" "$TARGET_DIR/config/authelia" \
|
||||
"$TARGET_DIR/data/caddy" "$TARGET_DIR/data/authelia" \
|
||||
"$TARGET_DIR/compose" "$TARGET_DIR/www" \
|
||||
"$TARGET_DIR/config/nextworkspace" "$TARGET_DIR/logs"
|
||||
fi
|
||||
|
||||
# ============================================================
|
||||
# 6. COPY artifacts to target
|
||||
# 6. COPY artifacts to target (as root)
|
||||
# ============================================================
|
||||
echo "[*] Copying artifacts..."
|
||||
cp nextworkspace "$TARGET_DIR/nextworkspace"
|
||||
cp "$BUILD_DIR/VERSION" "$TARGET_DIR/VERSION"
|
||||
maybe_sudo cp nextworkspace "$TARGET_DIR/nextworkspace"
|
||||
maybe_sudo cp "$BUILD_DIR/VERSION" "$TARGET_DIR/VERSION"
|
||||
if [ -d "$BUILD_DIR/config/www" ]; then
|
||||
cp -r "$BUILD_DIR/config/www"/* "$TARGET_DIR/www/"
|
||||
maybe_sudo cp -r "$BUILD_DIR/config/www"/* "$TARGET_DIR/www/"
|
||||
fi
|
||||
if [ -d "$BUILD_DIR/lng" ]; then
|
||||
rm -rf "$TARGET_DIR/lng"
|
||||
cp -r "$BUILD_DIR/lng" "$TARGET_DIR/lng"
|
||||
maybe_sudo rm -rf "$TARGET_DIR/lng"
|
||||
maybe_sudo cp -r "$BUILD_DIR/lng" "$TARGET_DIR/lng"
|
||||
fi
|
||||
if [ -d "$BUILD_DIR/config/nextworkspace" ]; then
|
||||
cp -r "$BUILD_DIR/config/nextworkspace"/* "$TARGET_DIR/config/nextworkspace/"
|
||||
maybe_sudo cp -r "$BUILD_DIR/config/nextworkspace"/* "$TARGET_DIR/config/nextworkspace/"
|
||||
fi
|
||||
|
||||
# Restore .env from backup
|
||||
if [ -f "$BACKUP_DIR/.env" ]; then
|
||||
cp "$BACKUP_DIR/.env" "$TARGET_DIR/.env"
|
||||
chmod 644 "$TARGET_DIR/.env"
|
||||
maybe_sudo cp "$BACKUP_DIR/.env" "$TARGET_DIR/.env"
|
||||
maybe_sudo chmod 644 "$TARGET_DIR/.env"
|
||||
fi
|
||||
|
||||
# ============================================================
|
||||
# 7. GENERATE config files with placeholder substitution
|
||||
# (install + destroy modes; update skips to keep secrets stable)
|
||||
# Write to /tmp first, then sudo cp to target
|
||||
# ============================================================
|
||||
GEN_DIR=$(mktemp -d)
|
||||
trap "rm -rf '$GEN_DIR'" EXIT
|
||||
|
||||
if [ "$MODE" != "update" ]; then
|
||||
echo "[*] Generating config files..."
|
||||
|
||||
# Caddyfile
|
||||
sed -e "s|{DOMAIN}|$DOMAIN|g" -e "s|{TLS_EMAIL}|${TLS_EMAIL:-admin@$DOMAIN}|g" \
|
||||
"$BUILD_DIR/config/caddy/Caddyfile" > "$TARGET_DIR/config/caddy/Caddyfile"
|
||||
"$BUILD_DIR/config/caddy/Caddyfile" > "$GEN_DIR/Caddyfile"
|
||||
|
||||
# Authelia config — preserve existing secrets if present
|
||||
JWT_SECRET="${JWT_SECRET:-$(openssl rand -hex 32)}"
|
||||
|
|
@ -195,7 +192,7 @@ if [ "$MODE" != "update" ]; then
|
|||
-e "s|{SMTP_PORT}|${SMTP_PORT:-587}|g" \
|
||||
-e "s|{SMTP_USER}|${SMTP_USER:-post@nextwks.eu}|g" \
|
||||
-e "s|{SMTP_PASS}|$SMTP_PASS|g" \
|
||||
"$BUILD_DIR/config/authelia/configuration.yml" > "$TARGET_DIR/config/authelia/configuration.yml"
|
||||
"$BUILD_DIR/config/authelia/configuration.yml" > "$GEN_DIR/configuration.yml"
|
||||
|
||||
# Users database
|
||||
ADMIN_PASSWORD_HASH="${ADMIN_PASSWORD_HASH:-}"
|
||||
|
|
@ -204,53 +201,56 @@ if [ "$MODE" != "update" ]; then
|
|||
fi
|
||||
sed -e "s|{ADMIN_PASSWORD_HASH}|$ADMIN_PASSWORD_HASH|g" \
|
||||
-e "s|{TLS_EMAIL}|${TLS_EMAIL:-admin@$DOMAIN}|g" \
|
||||
"$BUILD_DIR/config/authelia/users_database.yml" > "$TARGET_DIR/config/authelia/users_database.yml"
|
||||
"$BUILD_DIR/config/authelia/users_database.yml" > "$GEN_DIR/users_database.yml"
|
||||
|
||||
# Copy generated configs to target
|
||||
maybe_sudo cp "$GEN_DIR/Caddyfile" "$TARGET_DIR/config/caddy/Caddyfile"
|
||||
maybe_sudo cp "$GEN_DIR/configuration.yml" "$TARGET_DIR/config/authelia/configuration.yml"
|
||||
maybe_sudo cp "$GEN_DIR/users_database.yml" "$TARGET_DIR/config/authelia/users_database.yml"
|
||||
|
||||
# Persist generated secrets so --destroy is idempotent
|
||||
if [ -f "$BACKUP_DIR/.env" ]; then
|
||||
# Update existing .env with any new secrets
|
||||
sed -i "/^JWT_SECRET=/d; /^SESSION_SECRET=/d; /^ADMIN_PASSWORD_HASH=/d" "$BACKUP_DIR/.env" 2>/dev/null || true
|
||||
maybe_sudo sed -i "/^JWT_SECRET=/d; /^SESSION_SECRET=/d; /^ADMIN_PASSWORD_HASH=/d" "$BACKUP_DIR/.env" 2>/dev/null || true
|
||||
fi
|
||||
echo "JWT_SECRET=$JWT_SECRET" >> "$BACKUP_DIR/.env"
|
||||
echo "SESSION_SECRET=$SESSION_SECRET" >> "$BACKUP_DIR/.env"
|
||||
[ -n "$ADMIN_PASSWORD_HASH" ] && echo "ADMIN_PASSWORD_HASH=$ADMIN_PASSWORD_HASH" >> "$BACKUP_DIR/.env"
|
||||
chmod 600 "$BACKUP_DIR/.env"
|
||||
maybe_sudo bash -c "echo 'JWT_SECRET=$JWT_SECRET' >> '$BACKUP_DIR/.env'"
|
||||
maybe_sudo bash -c "echo 'SESSION_SECRET=$SESSION_SECRET' >> '$BACKUP_DIR/.env'"
|
||||
[ -n "$ADMIN_PASSWORD_HASH" ] && maybe_sudo bash -c "echo 'ADMIN_PASSWORD_HASH=$ADMIN_PASSWORD_HASH' >> '$BACKUP_DIR/.env'"
|
||||
maybe_sudo chmod 600 "$BACKUP_DIR/.env"
|
||||
fi
|
||||
|
||||
# ============================================================
|
||||
# 8. FIX OWNERSHIP — chown to the non-root user
|
||||
# 8. FIX OWNERSHIP — all files in TARGET_DIR/BACKUP_DIR to user
|
||||
# ============================================================
|
||||
RUN_USER="${SUDO_USER:-${USER}}"
|
||||
echo "[*] Setting file ownership to $RUN_USER..."
|
||||
chown -R "$RUN_UID:$RUN_GID" "$TARGET_DIR" 2>/dev/null || true
|
||||
chown -R "$RUN_UID:$RUN_GID" "$BACKUP_DIR" 2>/dev/null || true
|
||||
maybe_sudo chown -R "$RUN_USER:" "$TARGET_DIR" 2>/dev/null || true
|
||||
maybe_sudo chown -R "$RUN_USER:" "$BACKUP_DIR" 2>/dev/null || true
|
||||
|
||||
# ============================================================
|
||||
# 9. STOP old containers (all modes)
|
||||
# 9. STOP old containers (all modes, as root)
|
||||
# ============================================================
|
||||
echo "[*] Stopping any previous containers..."
|
||||
for c in caddy authelia launcher; do
|
||||
podman stop "$c" 2>/dev/null && echo " stopped $c" || true
|
||||
podman rm "$c" 2>/dev/null && echo " removed $c" || true
|
||||
maybe_sudo podman stop "$c" 2>/dev/null && echo " stopped $c" || true
|
||||
maybe_sudo podman rm "$c" 2>/dev/null && echo " removed $c" || true
|
||||
done
|
||||
sleep 1
|
||||
|
||||
# ============================================================
|
||||
# 10. PORT CHECK — before deploy
|
||||
# ============================================================
|
||||
if ss -tlnp 2>/dev/null | grep -q ':80 '; then
|
||||
if maybe_sudo ss -tlnp 2>/dev/null | grep -q ':80 '; then
|
||||
echo "=============================================="
|
||||
echo "[WARN] Port 80 is already in use."
|
||||
ss -tlnp 2>/dev/null | grep ':80 '
|
||||
maybe_sudo ss -tlnp 2>/dev/null | grep ':80 '
|
||||
echo ""
|
||||
echo " This is usually Caddy from a previous run."
|
||||
echo " Run this to stop it, then re-run deploy:"
|
||||
echo " podman stop caddy && podman rm caddy"
|
||||
echo "=============================================="
|
||||
echo ""
|
||||
read -p "Stop the container on port 80 now? [y/N]: " KILL
|
||||
if [ "$KILL" = "y" ] || [ "$KILL" = "Y" ]; then
|
||||
podman stop caddy 2>/dev/null || true
|
||||
podman rm caddy 2>/dev/null || true
|
||||
maybe_sudo podman stop caddy 2>/dev/null || true
|
||||
maybe_sudo podman rm caddy 2>/dev/null || true
|
||||
sleep 2
|
||||
echo "[OK] Port 80 freed."
|
||||
else
|
||||
|
|
@ -260,35 +260,34 @@ if ss -tlnp 2>/dev/null | grep -q ':80 '; then
|
|||
fi
|
||||
|
||||
# ============================================================
|
||||
# 11. DEPLOY stack
|
||||
# 11. DEPLOY stack (as root — needs port 80/443)
|
||||
# ============================================================
|
||||
echo "[*] Deploying containers on $NETWORK_NAME..."
|
||||
|
||||
# Ensure network exists
|
||||
podman network create "$NETWORK_NAME" 2>/dev/null || true
|
||||
maybe_sudo podman network create "$NETWORK_NAME" 2>/dev/null || true
|
||||
|
||||
# AUTHELIA_SECRET is SESSION_SECRET (Authelia session.secret)
|
||||
AUTHELIA_SECRET="${SESSION_SECRET:-}"
|
||||
if [ -z "$AUTHELIA_SECRET" ]; then
|
||||
# Update mode — extract from existing config
|
||||
AUTHELIA_SECRET=$(sed -n '/^session:/,/^[a-z]/p' "$TARGET_DIR/config/authelia/configuration.yml" \
|
||||
AUTHELIA_SECRET=$(maybe_sudo sed -n '/^session:/,/^[a-z]/p' "$TARGET_DIR/config/authelia/configuration.yml" \
|
||||
| grep 'secret:' | awk '{print $2}' 2>/dev/null || echo "")
|
||||
fi
|
||||
|
||||
# Generate compose file with substituted secret
|
||||
sed -e "s|{AUTHELIA_SECRET}|$AUTHELIA_SECRET|g" \
|
||||
"$BUILD_DIR/compose/stack.yaml" > "$TARGET_DIR/compose/stack.yaml"
|
||||
"$BUILD_DIR/compose/stack.yaml" > "$GEN_DIR/stack.yaml"
|
||||
maybe_sudo cp "$GEN_DIR/stack.yaml" "$TARGET_DIR/compose/stack.yaml"
|
||||
|
||||
podman-compose -f "$TARGET_DIR/compose/stack.yaml" down 2>/dev/null || true
|
||||
maybe_sudo podman-compose -f "$TARGET_DIR/compose/stack.yaml" down 2>/dev/null || true
|
||||
sleep 1
|
||||
podman-compose -f "$TARGET_DIR/compose/stack.yaml" up -d 2>&1 || echo "[WARN] Stack deploy had issues"
|
||||
maybe_sudo podman-compose -f "$TARGET_DIR/compose/stack.yaml" up -d 2>&1 || echo "[WARN] Stack deploy had issues"
|
||||
|
||||
# ============================================================
|
||||
# 11. HEALTH CHECK
|
||||
# 12. HEALTH CHECK
|
||||
# ============================================================
|
||||
echo "[*] Running health check..."
|
||||
for i in $(seq 1 $HEALTH_CHECK_RETRIES); do
|
||||
HEALTH=$(podman exec launcher curl -sf http://127.0.0.1:9000/health 2>/dev/null || echo "")
|
||||
HEALTH=$(maybe_sudo podman exec launcher curl -sf http://127.0.0.1:9000/health 2>/dev/null || echo "")
|
||||
if [ "$HEALTH" = "OK" ]; then
|
||||
echo "[OK] NextWorkspace launcher is healthy"
|
||||
echo "[OK] https://$DOMAIN/"
|
||||
|
|
@ -300,8 +299,8 @@ done
|
|||
echo "[FAIL] Health check failed — launcher did not respond"
|
||||
echo ""
|
||||
echo "--- Container status ---"
|
||||
podman ps -a --filter "name=caddy|authelia|launcher" 2>/dev/null || true
|
||||
maybe_sudo podman ps -a --filter "name=caddy|authelia|launcher" 2>/dev/null || true
|
||||
echo ""
|
||||
echo "--- Launcher logs (last 20 lines) ---"
|
||||
podman logs launcher --tail 20 2>/dev/null || echo " (no logs)"
|
||||
maybe_sudo podman logs launcher --tail 20 2>/dev/null || echo " (no logs)"
|
||||
exit 1
|
||||
|
|
|
|||
Loading…
Reference in a new issue