Skip to content

Commit 3818d56

Browse files
baijumclaude
andcommitted
Fix 5 deployment bugs found during WIT bootstrap
- Add cron package to apt-get (needed for crontab) - Copy SSH keys from sudo caller to deploy user instead of empty touch - Move SSH hardening after deploy user setup to prevent lockout - Fix postgres/redis data dir ownership (UID 999, not deploy) - Add CREATE DATABASE to create-app-credentials.sh - Add Caddyfile container-name substitution in deploy-blue-green.sh Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 68580f5 commit 3818d56

3 files changed

Lines changed: 49 additions & 20 deletions

File tree

infrastructure/bootstrap-server.sh

Lines changed: 31 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ echo
4848

4949
echo "Installing system packages..."
5050
apt-get update -qq
51-
apt-get install -y -qq git curl ufw vnstat unattended-upgrades apt-listchanges fail2ban > /dev/null
51+
apt-get install -y -qq git curl ufw vnstat unattended-upgrades apt-listchanges fail2ban cron > /dev/null
5252
info "System packages installed (git, curl, ufw, vnstat, unattended-upgrades, fail2ban)"
5353

5454
# --- Firewall ---
@@ -93,23 +93,6 @@ EOF
9393
info "fail2ban configured (SSH jail: maxretry=5, bantime=3600s, findtime=600s)"
9494
fi
9595

96-
# --- SSH Hardening ---
97-
98-
SSHD_HARDENING="/etc/ssh/sshd_config.d/99-towlion-hardening.conf"
99-
if [[ -f "$SSHD_HARDENING" ]]; then
100-
info "SSH hardening config already exists"
101-
else
102-
cat > "$SSHD_HARDENING" <<'EOF'
103-
PermitRootLogin no
104-
PasswordAuthentication no
105-
MaxAuthTries 3
106-
X11Forwarding no
107-
EOF
108-
109-
systemctl restart sshd
110-
info "SSH hardened (no root login, no password auth, MaxAuthTries=3, no X11)"
111-
fi
112-
11396
# --- Unattended Upgrades ---
11497

11598
UA_CONFIG="/etc/apt/apt.conf.d/50unattended-upgrades"
@@ -185,7 +168,16 @@ fi
185168
DEPLOY_SSH_DIR="/home/deploy/.ssh"
186169
if [[ ! -d "$DEPLOY_SSH_DIR" ]]; then
187170
mkdir -p "$DEPLOY_SSH_DIR"
188-
touch "$DEPLOY_SSH_DIR/authorized_keys"
171+
if [[ -n "${SUDO_USER:-}" ]] && [[ -f "/home/${SUDO_USER}/.ssh/authorized_keys" ]]; then
172+
cp "/home/${SUDO_USER}/.ssh/authorized_keys" "$DEPLOY_SSH_DIR/authorized_keys"
173+
info "Copied SSH keys from ${SUDO_USER} to deploy user"
174+
elif [[ -f /root/.ssh/authorized_keys ]]; then
175+
cp /root/.ssh/authorized_keys "$DEPLOY_SSH_DIR/authorized_keys"
176+
info "Copied SSH keys from root to deploy user"
177+
else
178+
touch "$DEPLOY_SSH_DIR/authorized_keys"
179+
warn "No SSH keys found to copy — add keys to /home/deploy/.ssh/authorized_keys manually"
180+
fi
189181
chmod 700 "$DEPLOY_SSH_DIR"
190182
chmod 600 "$DEPLOY_SSH_DIR/authorized_keys"
191183
chown -R deploy:deploy "$DEPLOY_SSH_DIR"
@@ -194,6 +186,23 @@ else
194186
info "SSH directory already exists for deploy user"
195187
fi
196188

189+
# --- SSH Hardening ---
190+
191+
SSHD_HARDENING="/etc/ssh/sshd_config.d/99-towlion-hardening.conf"
192+
if [[ -f "$SSHD_HARDENING" ]]; then
193+
info "SSH hardening config already exists"
194+
else
195+
cat > "$SSHD_HARDENING" <<'EOF'
196+
PermitRootLogin no
197+
PasswordAuthentication no
198+
MaxAuthTries 3
199+
X11Forwarding no
200+
EOF
201+
202+
systemctl restart sshd
203+
info "SSH hardened (no root login, no password auth, MaxAuthTries=3, no X11)"
204+
fi
205+
197206
# --- Directories ---
198207

199208
for dir in \
@@ -212,6 +221,9 @@ chown -R 472:472 /data/grafana
212221
chown -R 10001:10001 /data/loki
213222
# Prometheus runs as nobody (UID 65534) inside its container
214223
chown -R 65534:65534 /data/prometheus
224+
# Postgres and Redis run as UID 999 inside their containers
225+
chown -R 999:999 /data/postgres
226+
chown -R 999:999 /data/redis
215227
info "Directory structure created (/data/*, /opt/apps, /opt/platform)"
216228

217229
# --- Docker Network ---

infrastructure/create-app-credentials.sh

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,18 @@ DB_PASSWORD=$(openssl rand -base64 24)
4747
S3_PASSWORD=$(openssl rand -base64 24)
4848
JWT_SECRET=$(openssl rand -base64 32)
4949

50+
# PostgreSQL database creation (idempotent)
51+
info "Checking PostgreSQL database..."
52+
if docker compose -f /opt/platform/docker-compose.yml exec -T postgres \
53+
psql -U postgres -tc "SELECT 1 FROM pg_database WHERE datname = '${APP_DB}'" | grep -q 1; then
54+
warn "PostgreSQL database '${APP_DB}' already exists, skipping creation"
55+
else
56+
info "Creating PostgreSQL database '${APP_DB}'..."
57+
docker compose -f /opt/platform/docker-compose.yml exec -T postgres \
58+
psql -U postgres -c "CREATE DATABASE ${APP_DB}"
59+
info "PostgreSQL database created"
60+
fi
61+
5062
# PostgreSQL user creation (idempotent)
5163
info "Checking PostgreSQL user..."
5264
if docker compose -f /opt/platform/docker-compose.yml exec -T postgres psql -U postgres -tc "SELECT 1 FROM pg_roles WHERE rolname = '${APP_USER}'" | grep -q 1; then

infrastructure/deploy-blue-green.sh

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -190,7 +190,12 @@ fi
190190

191191
# Step 10: Write Caddyfile and reload Caddy
192192
info "Step 8/8: Swapping traffic to new slot..."
193-
echo "$CADDYFILE_CONTENT" > "/opt/platform/caddy-apps/${APP_NAME}.caddy"
193+
echo "$CADDYFILE_CONTENT" \
194+
| sed "s/${APP_NAME}-\([a-z]*\)-1/${APP_NAME}-${NEXT_SLOT}-\1-1/g" \
195+
| sed "s/\bapp:/${APP_NAME}-${NEXT_SLOT}-app-1:/g" \
196+
| sed "s/\bfrontend:/${APP_NAME}-${NEXT_SLOT}-frontend-1:/g" \
197+
| sed "s/{\\\$APP_DOMAIN}/${APP_DOMAIN}/g" \
198+
> "/opt/platform/caddy-apps/${APP_NAME}.caddy"
194199
docker compose -f "$PLATFORM_COMPOSE" exec -T caddy caddy reload --config /etc/caddy/Caddyfile
195200

196201
# Step 11: Verify external health

0 commit comments

Comments
 (0)