Add safe external HAProxy deployment flow.
Make bootstrap skip local HAProxy by default and support remote HAProxy config backup, validation, reload, and rollback over SSH. Made-with: Cursor
This commit is contained in:
@@ -19,7 +19,7 @@
|
|||||||
- `scripts/setup-ufw.sh` — базовая настройка UFW под эту схему портов.
|
- `scripts/setup-ufw.sh` — базовая настройка UFW под эту схему портов.
|
||||||
- `scripts/safe-apply-ssh-hardening.sh` — безопасное применение SSH hardening.
|
- `scripts/safe-apply-ssh-hardening.sh` — безопасное применение SSH hardening.
|
||||||
- `scripts/safe-apply-ssh-lockdown.sh` — безопасное применение строгого SSH lockdown-профиля.
|
- `scripts/safe-apply-ssh-lockdown.sh` — безопасное применение строгого SSH lockdown-профиля.
|
||||||
- `scripts/bootstrap-own-git.sh` — one-shot установка стека (deps + Gitea + HAProxy + fail2ban + timer).
|
- `scripts/bootstrap-own-git.sh` — one-shot установка стека (deps + Gitea + опционально HAProxy + fail2ban + timer).
|
||||||
- `.env.example` — шаблон переменных окружения для миграции.
|
- `.env.example` — шаблон переменных окружения для миграции.
|
||||||
- `Makefile` — быстрые команды установки/проверки/миграции.
|
- `Makefile` — быстрые команды установки/проверки/миграции.
|
||||||
- `docs/troubleshooting.md` — типовые проблемы и диагностика.
|
- `docs/troubleshooting.md` — типовые проблемы и диагностика.
|
||||||
@@ -51,17 +51,17 @@ cd /opt/gitea
|
|||||||
sudo docker compose up -d
|
sudo docker compose up -d
|
||||||
```
|
```
|
||||||
|
|
||||||
Или one-shot bootstrap:
|
Или one-shot bootstrap (без локального HAProxy, если он уже есть отдельно):
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
chmod +x scripts/bootstrap-own-git.sh
|
chmod +x scripts/bootstrap-own-git.sh
|
||||||
sudo OWGIT_REPO_DIR=/opt/owngit scripts/bootstrap-own-git.sh
|
sudo OWGIT_REPO_DIR=/opt/owngit ENABLE_HAPROXY=false scripts/bootstrap-own-git.sh
|
||||||
```
|
```
|
||||||
|
|
||||||
Сначала безопасная проверка шагов без изменений:
|
Сначала безопасная проверка шагов без изменений:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
OWGIT_REPO_DIR=/opt/owngit scripts/bootstrap-own-git.sh --dry-run
|
sudo OWGIT_REPO_DIR=/opt/owngit ENABLE_HAPROXY=false scripts/bootstrap-own-git.sh --dry-run
|
||||||
```
|
```
|
||||||
|
|
||||||
Полезные переменные bootstrap:
|
Полезные переменные bootstrap:
|
||||||
@@ -69,11 +69,65 @@ OWGIT_REPO_DIR=/opt/owngit scripts/bootstrap-own-git.sh --dry-run
|
|||||||
```bash
|
```bash
|
||||||
SERVER_DOMAIN=git.papatramp.ru
|
SERVER_DOMAIN=git.papatramp.ru
|
||||||
GITEA_LAN_IP=192.168.128.100
|
GITEA_LAN_IP=192.168.128.100
|
||||||
|
ENABLE_HAPROXY=false
|
||||||
|
ENABLE_REMOTE_HAPROXY_DEPLOY=false
|
||||||
|
REMOTE_HAPROXY_HOST=192.168.128.65
|
||||||
|
REMOTE_HAPROXY_PORT=22
|
||||||
|
REMOTE_HAPROXY_USER=papatramp
|
||||||
|
# REMOTE_HAPROXY_PASSWORD='ваш_пароль_ssh'
|
||||||
INSTALL_UFW=false
|
INSTALL_UFW=false
|
||||||
ENABLE_FAIL2BAN=true
|
ENABLE_FAIL2BAN=true
|
||||||
ENABLE_BACKUP_TIMER=true
|
ENABLE_BACKUP_TIMER=true
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Безопасный порядок для чистого Ubuntu 24.04
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo mkdir -p /opt && cd /opt
|
||||||
|
sudo git clone https://github.com/PTah/owngit.git
|
||||||
|
cd /opt/owngit && sudo chmod +x scripts/bootstrap-own-git.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
Сначала прогон без изменений:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo OWGIT_REPO_DIR=/opt/owngit ENABLE_HAPROXY=false scripts/bootstrap-own-git.sh --dry-run
|
||||||
|
```
|
||||||
|
|
||||||
|
Потом реальный запуск:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo OWGIT_REPO_DIR=/opt/owngit ENABLE_HAPROXY=false scripts/bootstrap-own-git.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
После установки быстрая проверка:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker ps
|
||||||
|
sudo systemctl status haproxy --no-pager
|
||||||
|
curl -I http://127.0.0.1:3000
|
||||||
|
```
|
||||||
|
|
||||||
|
Если HAProxy у вас на отдельном узле (например, `192.168.128.65`), проверяйте его сервис на этом узле и обновите там backend на новый IP Gitea (`:3000`) и SSH proxy (`:2222`).
|
||||||
|
|
||||||
|
### Автообновление удаленного HAProxy по SSH
|
||||||
|
|
||||||
|
`bootstrap-own-git.sh` умеет подключаться к удаленному HAProxy-серверу, делать backup текущего `/etc/haproxy/haproxy.cfg`, загружать новый конфиг, валидировать его и делать `reload` (с откатом при ошибке).
|
||||||
|
|
||||||
|
Пример запуска для вашего случая:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo OWGIT_REPO_DIR=/opt/owngit \
|
||||||
|
ENABLE_HAPROXY=false \
|
||||||
|
ENABLE_REMOTE_HAPROXY_DEPLOY=true \
|
||||||
|
REMOTE_HAPROXY_HOST=192.168.128.65 \
|
||||||
|
REMOTE_HAPROXY_USER=papatramp \
|
||||||
|
REMOTE_HAPROXY_PASSWORD='ваш_пароль_ssh' \
|
||||||
|
scripts/bootstrap-own-git.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
Рекомендуется сначала сделать dry-run с теми же переменными.
|
||||||
|
|
||||||
Положить `app.ini`:
|
Положить `app.ini`:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
@@ -91,6 +145,8 @@ openssl rand -hex 32
|
|||||||
|
|
||||||
## 2) Установка HAProxy конфига
|
## 2) Установка HAProxy конфига
|
||||||
|
|
||||||
|
Если HAProxy уже работает на отдельном сервере (например, `192.168.128.65`), этот раздел применяется на том сервере HAProxy, а не на хосте с Gitea.
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
sudo cp haproxy/haproxy.cfg /etc/haproxy/haproxy.cfg
|
sudo cp haproxy/haproxy.cfg /etc/haproxy/haproxy.cfg
|
||||||
sudo haproxy -c -f /etc/haproxy/haproxy.cfg
|
sudo haproxy -c -f /etc/haproxy/haproxy.cfg
|
||||||
|
|||||||
+104
-13
@@ -17,6 +17,12 @@ SERVER_DOMAIN="${SERVER_DOMAIN:-git.papatramp.ru}"
|
|||||||
GITEA_LAN_IP="${GITEA_LAN_IP:-192.168.128.100}"
|
GITEA_LAN_IP="${GITEA_LAN_IP:-192.168.128.100}"
|
||||||
OWGIT_REPO_DIR="${OWGIT_REPO_DIR:-/opt/owngit}"
|
OWGIT_REPO_DIR="${OWGIT_REPO_DIR:-/opt/owngit}"
|
||||||
GITEA_BASE_DIR="${GITEA_BASE_DIR:-/opt/gitea}"
|
GITEA_BASE_DIR="${GITEA_BASE_DIR:-/opt/gitea}"
|
||||||
|
ENABLE_HAPROXY="${ENABLE_HAPROXY:-false}"
|
||||||
|
ENABLE_REMOTE_HAPROXY_DEPLOY="${ENABLE_REMOTE_HAPROXY_DEPLOY:-false}"
|
||||||
|
REMOTE_HAPROXY_HOST="${REMOTE_HAPROXY_HOST:-192.168.128.65}"
|
||||||
|
REMOTE_HAPROXY_PORT="${REMOTE_HAPROXY_PORT:-22}"
|
||||||
|
REMOTE_HAPROXY_USER="${REMOTE_HAPROXY_USER:-papatramp}"
|
||||||
|
REMOTE_HAPROXY_PASSWORD="${REMOTE_HAPROXY_PASSWORD:-}"
|
||||||
INSTALL_UFW="${INSTALL_UFW:-false}"
|
INSTALL_UFW="${INSTALL_UFW:-false}"
|
||||||
ENABLE_FAIL2BAN="${ENABLE_FAIL2BAN:-true}"
|
ENABLE_FAIL2BAN="${ENABLE_FAIL2BAN:-true}"
|
||||||
ENABLE_BACKUP_TIMER="${ENABLE_BACKUP_TIMER:-true}"
|
ENABLE_BACKUP_TIMER="${ENABLE_BACKUP_TIMER:-true}"
|
||||||
@@ -66,13 +72,17 @@ require_root() {
|
|||||||
|
|
||||||
require_paths() {
|
require_paths() {
|
||||||
local missing=0
|
local missing=0
|
||||||
for p in \
|
local required_paths=(
|
||||||
"$OWGIT_REPO_DIR/gitea/docker-compose.yml" \
|
"$OWGIT_REPO_DIR/gitea/docker-compose.yml" \
|
||||||
"$OWGIT_REPO_DIR/gitea/app.ini.example" \
|
"$OWGIT_REPO_DIR/gitea/app.ini.example" \
|
||||||
"$OWGIT_REPO_DIR/haproxy/haproxy.cfg" \
|
|
||||||
"$OWGIT_REPO_DIR/systemd/gitea-backup.service" \
|
"$OWGIT_REPO_DIR/systemd/gitea-backup.service" \
|
||||||
"$OWGIT_REPO_DIR/systemd/gitea-backup.timer" \
|
"$OWGIT_REPO_DIR/systemd/gitea-backup.timer" \
|
||||||
"$OWGIT_REPO_DIR/fail2ban/jail.d/owngit.local"; do
|
"$OWGIT_REPO_DIR/fail2ban/jail.d/owngit.local"
|
||||||
|
)
|
||||||
|
if [[ "$ENABLE_HAPROXY" == "true" || "$ENABLE_REMOTE_HAPROXY_DEPLOY" == "true" ]]; then
|
||||||
|
required_paths+=("$OWGIT_REPO_DIR/haproxy/haproxy.cfg")
|
||||||
|
fi
|
||||||
|
for p in "${required_paths[@]}"; do
|
||||||
if [[ ! -f "$p" ]]; then
|
if [[ ! -f "$p" ]]; then
|
||||||
err "Не найден файл: $p"
|
err "Не найден файл: $p"
|
||||||
missing=1
|
missing=1
|
||||||
@@ -84,17 +94,26 @@ require_paths() {
|
|||||||
install_packages() {
|
install_packages() {
|
||||||
log "Устанавливаю зависимости..."
|
log "Устанавливаю зависимости..."
|
||||||
run_cmd apt-get update
|
run_cmd apt-get update
|
||||||
run_cmd apt-get install -y \
|
local packages=(
|
||||||
ca-certificates curl jq git openssl \
|
ca-certificates curl jq git openssl
|
||||||
docker.io docker-compose-plugin \
|
docker.io docker-compose-plugin openssh-client
|
||||||
haproxy
|
)
|
||||||
|
if [[ "$ENABLE_HAPROXY" == "true" ]]; then
|
||||||
|
packages+=(haproxy)
|
||||||
|
fi
|
||||||
|
if [[ "$ENABLE_REMOTE_HAPROXY_DEPLOY" == "true" && -n "$REMOTE_HAPROXY_PASSWORD" ]]; then
|
||||||
|
packages+=(sshpass)
|
||||||
|
fi
|
||||||
|
run_cmd apt-get install -y "${packages[@]}"
|
||||||
ok "Пакеты установлены"
|
ok "Пакеты установлены"
|
||||||
}
|
}
|
||||||
|
|
||||||
setup_dirs() {
|
setup_dirs() {
|
||||||
log "Создаю рабочие директории..."
|
log "Создаю рабочие директории..."
|
||||||
run_cmd mkdir -p "$GITEA_BASE_DIR/data/gitea/conf"
|
run_cmd mkdir -p "$GITEA_BASE_DIR/data/gitea/conf"
|
||||||
run_cmd mkdir -p /etc/haproxy/certs
|
if [[ "$ENABLE_HAPROXY" == "true" ]]; then
|
||||||
|
run_cmd mkdir -p /etc/haproxy/certs
|
||||||
|
fi
|
||||||
ok "Директории готовы"
|
ok "Директории готовы"
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -113,6 +132,10 @@ deploy_gitea() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
deploy_haproxy() {
|
deploy_haproxy() {
|
||||||
|
if [[ "$ENABLE_HAPROXY" != "true" ]]; then
|
||||||
|
warn "HAProxy пропущен (ENABLE_HAPROXY=false). Используйте существующий внешний HAProxy."
|
||||||
|
return
|
||||||
|
fi
|
||||||
log "Устанавливаю конфиг HAProxy..."
|
log "Устанавливаю конфиг HAProxy..."
|
||||||
run_cmd cp -a "$OWGIT_REPO_DIR/haproxy/haproxy.cfg" /etc/haproxy/haproxy.cfg
|
run_cmd cp -a "$OWGIT_REPO_DIR/haproxy/haproxy.cfg" /etc/haproxy/haproxy.cfg
|
||||||
|
|
||||||
@@ -138,6 +161,66 @@ deploy_haproxy() {
|
|||||||
ok "HAProxy применен и перезагружен"
|
ok "HAProxy применен и перезагружен"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
remote_ssh_cmd() {
|
||||||
|
local cmd="$1"
|
||||||
|
local ssh_target="${REMOTE_HAPROXY_USER}@${REMOTE_HAPROXY_HOST}"
|
||||||
|
local ssh_opts=(-p "$REMOTE_HAPROXY_PORT" -o StrictHostKeyChecking=accept-new -o ConnectTimeout=10)
|
||||||
|
if [[ "$DRY_RUN" == "true" ]]; then
|
||||||
|
echo "[DRY] ssh ${ssh_target} ${cmd}"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
if [[ -n "$REMOTE_HAPROXY_PASSWORD" ]]; then
|
||||||
|
SSHPASS="$REMOTE_HAPROXY_PASSWORD" sshpass -e ssh "${ssh_opts[@]}" "$ssh_target" "$cmd"
|
||||||
|
else
|
||||||
|
ssh "${ssh_opts[@]}" "$ssh_target" "$cmd"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
remote_scp_upload() {
|
||||||
|
local src="$1"
|
||||||
|
local dst="$2"
|
||||||
|
local ssh_target="${REMOTE_HAPROXY_USER}@${REMOTE_HAPROXY_HOST}"
|
||||||
|
local scp_opts=(-P "$REMOTE_HAPROXY_PORT" -o StrictHostKeyChecking=accept-new -o ConnectTimeout=10)
|
||||||
|
if [[ "$DRY_RUN" == "true" ]]; then
|
||||||
|
echo "[DRY] scp ${src} ${ssh_target}:${dst}"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
if [[ -n "$REMOTE_HAPROXY_PASSWORD" ]]; then
|
||||||
|
SSHPASS="$REMOTE_HAPROXY_PASSWORD" sshpass -e scp "${scp_opts[@]}" "$src" "${ssh_target}:${dst}"
|
||||||
|
else
|
||||||
|
scp "${scp_opts[@]}" "$src" "${ssh_target}:${dst}"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
deploy_remote_haproxy() {
|
||||||
|
if [[ "$ENABLE_REMOTE_HAPROXY_DEPLOY" != "true" ]]; then
|
||||||
|
warn "Удаленный деплой HAProxy пропущен (ENABLE_REMOTE_HAPROXY_DEPLOY=false)"
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
log "Обновляю HAProxy удаленно на ${REMOTE_HAPROXY_HOST}:${REMOTE_HAPROXY_PORT}..."
|
||||||
|
|
||||||
|
local ts backup_cfg remote_tmp
|
||||||
|
ts="$(date +%Y%m%d-%H%M%S)"
|
||||||
|
backup_cfg="/etc/haproxy/haproxy.cfg.bak.${ts}"
|
||||||
|
remote_tmp="/tmp/owngit-haproxy.cfg.${ts}"
|
||||||
|
|
||||||
|
remote_ssh_cmd "sudo cp -a /etc/haproxy/haproxy.cfg '${backup_cfg}'"
|
||||||
|
remote_scp_upload "$OWGIT_REPO_DIR/haproxy/haproxy.cfg" "$remote_tmp"
|
||||||
|
remote_ssh_cmd "sudo mv '${remote_tmp}' /etc/haproxy/haproxy.cfg && sudo chown root:root /etc/haproxy/haproxy.cfg && sudo chmod 644 /etc/haproxy/haproxy.cfg"
|
||||||
|
remote_ssh_cmd "sudo touch /etc/haproxy/allowed_ips.lst /etc/haproxy/allowed_ips_knock.lst"
|
||||||
|
|
||||||
|
if remote_ssh_cmd "sudo haproxy -c -f /etc/haproxy/haproxy.cfg"; then
|
||||||
|
remote_ssh_cmd "sudo systemctl reload haproxy"
|
||||||
|
ok "Удаленный HAProxy обновлен, backup: ${backup_cfg}"
|
||||||
|
else
|
||||||
|
warn "Валидация удаленного конфига не прошла, выполняю откат..."
|
||||||
|
remote_ssh_cmd "sudo cp -a '${backup_cfg}' /etc/haproxy/haproxy.cfg"
|
||||||
|
remote_ssh_cmd "sudo haproxy -c -f /etc/haproxy/haproxy.cfg && sudo systemctl reload haproxy"
|
||||||
|
err "Откат выполнен. Проверьте haproxy/haproxy.cfg перед повтором."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
deploy_fail2ban() {
|
deploy_fail2ban() {
|
||||||
if [[ "$ENABLE_FAIL2BAN" != "true" ]]; then
|
if [[ "$ENABLE_FAIL2BAN" != "true" ]]; then
|
||||||
warn "Fail2ban пропущен (ENABLE_FAIL2BAN=false)"
|
warn "Fail2ban пропущен (ENABLE_FAIL2BAN=false)"
|
||||||
@@ -178,6 +261,12 @@ maybe_setup_ufw() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
print_checklist() {
|
print_checklist() {
|
||||||
|
local haproxy_host_hint
|
||||||
|
if [[ "$ENABLE_HAPROXY" == "true" ]]; then
|
||||||
|
haproxy_host_hint="локальном сервере"
|
||||||
|
else
|
||||||
|
haproxy_host_hint="внешнем HAProxy"
|
||||||
|
fi
|
||||||
cat <<EOF
|
cat <<EOF
|
||||||
|
|
||||||
==================== POST-INSTALL CHECKLIST ====================
|
==================== POST-INSTALL CHECKLIST ====================
|
||||||
@@ -189,14 +278,15 @@ print_checklist() {
|
|||||||
2) Проверьте DNS:
|
2) Проверьте DNS:
|
||||||
- ${SERVER_DOMAIN} должен резолвиться на HAProxy (WAN/LAN по вашей схеме).
|
- ${SERVER_DOMAIN} должен резолвиться на HAProxy (WAN/LAN по вашей схеме).
|
||||||
|
|
||||||
3) Замените временный TLS сертификат:
|
3) HAProxy/TLS:
|
||||||
- /etc/haproxy/certs/papatramp.pem
|
- если ENABLE_HAPROXY=true: замените /etc/haproxy/certs/papatramp.pem и выполните systemctl reload haproxy
|
||||||
- и reload: systemctl reload haproxy
|
- если ENABLE_HAPROXY=false: обновите backend/SSH-proxy на ${haproxy_host_hint}
|
||||||
|
- если ENABLE_REMOTE_HAPROXY_DEPLOY=true: удаленный /etc/haproxy/haproxy.cfg уже обновлен с backup/rollback
|
||||||
|
|
||||||
4) Проверки:
|
4) Проверки:
|
||||||
- curl -I http://${GITEA_LAN_IP}:3000
|
- curl -I http://${GITEA_LAN_IP}:3000
|
||||||
- curl -I https://${SERVER_DOMAIN}
|
- после обновления внешнего HAProxy: curl -I https://${SERVER_DOMAIN}
|
||||||
- nc -zv ${SERVER_DOMAIN} 2222
|
- после обновления внешнего HAProxy: nc -zv ${SERVER_DOMAIN} 2222
|
||||||
|
|
||||||
5) Запустите preflight:
|
5) Запустите preflight:
|
||||||
- chmod +x ${OWGIT_REPO_DIR}/scripts/preflight-check.sh
|
- chmod +x ${OWGIT_REPO_DIR}/scripts/preflight-check.sh
|
||||||
@@ -236,6 +326,7 @@ main() {
|
|||||||
setup_dirs
|
setup_dirs
|
||||||
deploy_gitea
|
deploy_gitea
|
||||||
deploy_haproxy
|
deploy_haproxy
|
||||||
|
deploy_remote_haproxy
|
||||||
deploy_fail2ban
|
deploy_fail2ban
|
||||||
deploy_backup_timer
|
deploy_backup_timer
|
||||||
maybe_setup_ufw
|
maybe_setup_ufw
|
||||||
|
|||||||
Reference in New Issue
Block a user