diff --git a/README.md b/README.md index 81219b2..360c338 100644 --- a/README.md +++ b/README.md @@ -19,7 +19,7 @@ - `scripts/setup-ufw.sh` — базовая настройка UFW под эту схему портов. - `scripts/safe-apply-ssh-hardening.sh` — безопасное применение SSH hardening. - `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` — шаблон переменных окружения для миграции. - `Makefile` — быстрые команды установки/проверки/миграции. - `docs/troubleshooting.md` — типовые проблемы и диагностика. @@ -51,17 +51,17 @@ cd /opt/gitea sudo docker compose up -d ``` -Или one-shot bootstrap: +Или one-shot bootstrap (без локального HAProxy, если он уже есть отдельно): ```bash 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 -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: @@ -69,11 +69,65 @@ OWGIT_REPO_DIR=/opt/owngit scripts/bootstrap-own-git.sh --dry-run ```bash SERVER_DOMAIN=git.papatramp.ru 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 ENABLE_FAIL2BAN=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`: ```bash @@ -91,6 +145,8 @@ openssl rand -hex 32 ## 2) Установка HAProxy конфига +Если HAProxy уже работает на отдельном сервере (например, `192.168.128.65`), этот раздел применяется на том сервере HAProxy, а не на хосте с Gitea. + ```bash sudo cp haproxy/haproxy.cfg /etc/haproxy/haproxy.cfg sudo haproxy -c -f /etc/haproxy/haproxy.cfg diff --git a/scripts/bootstrap-own-git.sh b/scripts/bootstrap-own-git.sh index 3a202aa..9a8dfa9 100644 --- a/scripts/bootstrap-own-git.sh +++ b/scripts/bootstrap-own-git.sh @@ -17,6 +17,12 @@ SERVER_DOMAIN="${SERVER_DOMAIN:-git.papatramp.ru}" GITEA_LAN_IP="${GITEA_LAN_IP:-192.168.128.100}" OWGIT_REPO_DIR="${OWGIT_REPO_DIR:-/opt/owngit}" 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}" ENABLE_FAIL2BAN="${ENABLE_FAIL2BAN:-true}" ENABLE_BACKUP_TIMER="${ENABLE_BACKUP_TIMER:-true}" @@ -66,13 +72,17 @@ require_root() { require_paths() { local missing=0 - for p in \ + local required_paths=( "$OWGIT_REPO_DIR/gitea/docker-compose.yml" \ "$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.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 err "Не найден файл: $p" missing=1 @@ -84,17 +94,26 @@ require_paths() { install_packages() { log "Устанавливаю зависимости..." run_cmd apt-get update - run_cmd apt-get install -y \ - ca-certificates curl jq git openssl \ - docker.io docker-compose-plugin \ - haproxy + local packages=( + ca-certificates curl jq git openssl + docker.io docker-compose-plugin openssh-client + ) + 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 "Пакеты установлены" } setup_dirs() { log "Создаю рабочие директории..." 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 "Директории готовы" } @@ -113,6 +132,10 @@ deploy_gitea() { } deploy_haproxy() { + if [[ "$ENABLE_HAPROXY" != "true" ]]; then + warn "HAProxy пропущен (ENABLE_HAPROXY=false). Используйте существующий внешний HAProxy." + return + fi log "Устанавливаю конфиг HAProxy..." run_cmd cp -a "$OWGIT_REPO_DIR/haproxy/haproxy.cfg" /etc/haproxy/haproxy.cfg @@ -138,6 +161,66 @@ deploy_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() { if [[ "$ENABLE_FAIL2BAN" != "true" ]]; then warn "Fail2ban пропущен (ENABLE_FAIL2BAN=false)" @@ -178,6 +261,12 @@ maybe_setup_ufw() { } print_checklist() { + local haproxy_host_hint + if [[ "$ENABLE_HAPROXY" == "true" ]]; then + haproxy_host_hint="локальном сервере" + else + haproxy_host_hint="внешнем HAProxy" + fi cat <