Deploy default status page to /var/www/statuspage

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
mrleo1nidandClaude Opus 5.5 committed 2026-10-06 11:45:11 +03:00
1 parent ad4400f3d3
commit daf572e1f2
3 files changed
+39 -24

No files matched your search

+7 -6
View File
@@ -24,17 +24,18 @@ curl -fsSL https://gitea.hsrv.site/mrleo1nid/vps_installer/raw/branch/main/setup
5. fail2ban (jail sshd, ваш текущий IP в ignoreip)
6. UFW: SSH, 80, 443
7. Блокировка IP по внешнему списку — ipset + cron *(можно отключить)*
13. Запрет входа root по SSH, блокировка пароля root *(можно отключить)*
8. Заглушка 503 в `/var/www/statuspage/index.html` (из `statuspage/` репозитория; существующий файл не перезаписывается)
14. Запрет входа root по SSH, блокировка пароля root *(можно отключить)*
Опционально (только установка):
| # | ПО | Версия | Состояние после скрипта |
|---|----|--------|-------------------------|
| 8 | Docker + Compose | из репо Docker | запущен, пользователь в группе `docker` |
| 9 | Telegraf | из репо InfluxData | установлен, **выключен** до настройки `telegraf.conf` |
| 10 | Nebula | **1.10.3** | бинарники + юнит, **не запущен** до появления сертификатов |
| 11 | 3x-ui | **v2.8.11** | официальный установщик (интерактивный), порт панели открыт в UFW |
| 12 | Caddy | из репо Caddy | установлен со стандартным Caddyfile |
| 9 | Docker + Compose | из репо Docker | запущен, пользователь в группе `docker` |
| 10 | Telegraf | из репо InfluxData | установлен, **выключен** до настройки `telegraf.conf` |
| 11 | Nebula | **1.10.3** | бинарники + юнит, **не запущен** до появления сертификатов |
| 12 | 3x-ui | **v2.8.11** | официальный установщик (интерактивный), порт панели открыт в UFW |
| 13 | Caddy | из репо Caddy | установлен со стандартным Caddyfile |
Версии 3x-ui и Nebula задаются в начале `setup.sh` (`XUI_VERSION`, `NEBULA_VERSION`).
В конце скрипт выводит список того, что осталось настроить.
+31 -18
View File
@@ -22,6 +22,8 @@ set -Eeuo pipefail
XUI_VERSION="v2.8.11"
NEBULA_VERSION="1.10.3"
REPO_RAW="https://gitea.hsrv.site/mrleo1nid/vps_installer/raw/branch/main"
STATUSPAGE_DIR="/var/www/statuspage"
LOG_FILE="/var/log/vps-setup.log"
DEFAULT_BLOCKLIST_URL="https://raw.githubusercontent.com/jahlib/somebody-knocked-my-ssh/refs/heads/main/ban.txt"
BAN_SCRIPT_URL="https://raw.githubusercontent.com/jahlib/somebody-knocked-my-ssh/refs/heads/main/ban.sh"
@@ -297,9 +299,20 @@ else
step "7. Блокировка IP — пропущено"
fi
# ---------- 8. Docker ----------
# ---------- 8. Статус-страница ----------
step "8. Статус-страница $STATUSPAGE_DIR"
if [[ -f $STATUSPAGE_DIR/index.html ]]; then
info "$STATUSPAGE_DIR/index.html уже есть — не трогаю"
else
install -d -m 755 "$STATUSPAGE_DIR"
curl -fsSL "$REPO_RAW/statuspage/index.html" -o "$STATUSPAGE_DIR/index.html"
chmod 644 "$STATUSPAGE_DIR/index.html"
ok "Заглушка положена в $STATUSPAGE_DIR/index.html"
fi
# ---------- 9. Docker ----------
if [[ $INSTALL_DOCKER == y ]]; then
step "8. Docker + Docker Compose"
step "9. Docker + Docker Compose"
for pkg in docker.io docker-doc docker-compose docker-compose-v2 podman-docker containerd runc; do
dpkg -s "$pkg" &>/dev/null && apt-get remove -y -q "$pkg"
done
@@ -316,12 +329,12 @@ if [[ $INSTALL_DOCKER == y ]]; then
info "$NEW_USER добавлен в группу docker"
warn "Docker публикует порты в обход UFW — биндите на 127.0.0.1:PORT или фильтруйте в DOCKER-USER"
else
step "8. Docker — пропущено"
step "9. Docker — пропущено"
fi
# ---------- 9. Telegraf ----------
# ---------- 10. Telegraf ----------
if [[ $INSTALL_TELEGRAF == y ]]; then
step "9. Telegraf"
step "10. Telegraf"
mkdir -p /etc/apt/keyrings
curl -fsSL https://repos.influxdata.com/influxdata-archive.key \
| gpg --batch --yes --dearmor -o /etc/apt/keyrings/influxdata.gpg
@@ -349,12 +362,12 @@ EOF
ok "$(telegraf --version) установлен, сервис выключен до настройки"
todo "Telegraf: заполнить /etc/telegraf/telegraf.conf → telegraf --config /etc/telegraf/telegraf.conf --test → systemctl enable --now telegraf"
else
step "9. Telegraf — пропущено"
step "10. Telegraf — пропущено"
fi
# ---------- 10. Nebula ----------
# ---------- 11. Nebula ----------
if [[ $INSTALL_NEBULA == y ]]; then
step "10. Nebula $NEBULA_VERSION"
step "11. Nebula $NEBULA_VERSION"
NEBULA_TMP=$(mktemp -d)
curl -fsSL "https://github.com/slackhq/nebula/releases/download/v$NEBULA_VERSION/nebula-linux-$ARCH.tar.gz" \
-o "$NEBULA_TMP/nebula.tar.gz"
@@ -379,13 +392,13 @@ if [[ $INSTALL_NEBULA == y ]]; then
ok "$(nebula -version | head -n1), юнит установлен, сервис не запущен (нет сертификатов)"
todo "Nebula: положить ca.crt, <node>.crt, <node>.key и config.yml в /etc/nebula (пример: config.yml.example), chmod 600 /etc/nebula/*.key → systemctl enable --now nebula"
else
step "10. Nebula — пропущено"
step "11. Nebula — пропущено"
fi
# ---------- 11. 3x-ui ----------
# ---------- 12. 3x-ui ----------
# ставим до Caddy: установщику 3x-ui для сертификата Let's Encrypt нужен свободный порт 80
if [[ $INSTALL_3XUI == y ]]; then
step "11. 3x-ui $XUI_VERSION"
step "12. 3x-ui $XUI_VERSION"
XUI_INSTALLER=$(mktemp)
curl -fsSL "https://raw.githubusercontent.com/mhsanaei/3x-ui/$XUI_VERSION/install.sh" -o "$XUI_INSTALLER"
info "Дальше — вопросы установщика 3x-ui (порт панели, SSL)."
@@ -404,12 +417,12 @@ if [[ $INSTALL_3XUI == y ]]; then
fi
rm -f "$XUI_INSTALLER"
else
step "11. 3x-ui — пропущено"
step "12. 3x-ui — пропущено"
fi
# ---------- 12. Caddy ----------
# ---------- 13. Caddy ----------
if [[ $INSTALL_CADDY == y ]]; then
step "12. Caddy"
step "13. Caddy"
apt_install debian-keyring debian-archive-keyring apt-transport-https
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/gpg.key' \
| gpg --batch --yes --dearmor -o /usr/share/keyrings/caddy-stable-archive-keyring.gpg
@@ -420,12 +433,12 @@ if [[ $INSTALL_CADDY == y ]]; then
ok "Caddy $(caddy version | awk '{print $1}') установлен (стандартный Caddyfile)"
todo "Caddy: заполнить /etc/caddy/Caddyfile → caddy validate --config /etc/caddy/Caddyfile && systemctl reload caddy"
else
step "12. Caddy — пропущено"
step "13. Caddy — пропущено"
fi
# ---------- 13. Отключение доступа root ----------
# ---------- 14. Отключение доступа root ----------
if [[ $HARDEN_SSH == y ]]; then
step "13. Отключение доступа root"
step "14. Отключение доступа root"
# вход новым пользователем — по паролю, поэтому PasswordAuthentication включён явно
cat >"$SSHD_HARDENING" <<'EOF'
PermitRootLogin no
@@ -439,7 +452,7 @@ EOF
passwd -l root >/dev/null
ok "Вход root по SSH запрещён, пароль root заблокирован"
else
step "13. Отключение доступа root — пропущено"
step "14. Отключение доступа root — пропущено"
fi
# ---------- итог ----------
+1
View File
@@ -0,0 +1 @@
<head><meta charset=UTF-8><meta name=viewport content="width=device-width, initial-scale=1.0"><title>503</title><link rel=preconnect href=https://cdn.jsdelivr.net><link rel="shortcut icon"type=image/x-icon href=data:image/x-icon;,><style>.button:hover,.terminal{box-shadow:0 0 20px #0f42}*{margin:0;padding:0;box-sizing:border-box}:root{--bg:#000;--terminal-bg:#0a0a0a;--border:#1a1a1a;--text-primary:#fff;--text-secondary:#666;--text-muted:#333;--green:#0f4;--red:#f04;--yellow:#fc0;--blue:#08f;--cyan:#0ff}.container,body,body::after,body::before,html{width:100%;height:100%}body,html{overflow:hidden;background:var(--bg);color:var(--text-primary);font-family:Arial,sans-serif;-webkit-font-smoothing:antialiased}body::after,body::before{content:'';position:fixed;top:0;left:0;pointer-events:none}body::before{background:repeating-linear-gradient(0deg,#0002,#0002 1px,transparent 1px,transparent 2px);z-index:1000;animation:8s linear infinite scanline}@keyframes scanline{to{transform:translateY(10px)}}body::after{background:#1210101A;opacity:0;z-index:999;animation:.15s infinite flicker}@keyframes flicker{0%{opacity:.27861}5%{opacity:.34769}10%{opacity:.23604}15%{opacity:.90626}20%{opacity:.18128}25%{opacity:.83891}30%{opacity:.65583}35%{opacity:.67807}40%{opacity:.26559}45%{opacity:.84693}50%{opacity:.96019}55%{opacity:.08594}60%{opacity:.20313}65%{opacity:.71988}70%{opacity:.53455}75%{opacity:.37288}80%{opacity:.71428}85%{opacity:.70419}90%{opacity:.7003}95%{opacity:.36108}100%{opacity:.24387}}.container{position:relative;z-index:10;display:flex;flex-direction:column;align-items:center;justify-content:center;padding:max(20px,3vw)}.content{max-width:1200px;width:100%}.header{text-align:center;margin-bottom:max(30px,3vh);animation:.5s ease-out fadeIn}.error-code{font-size:max(40px,min(8vw,6vh));font-weight:700;letter-spacing:.1em;color:var(--red);text-shadow:0 0 10px var(--red);margin-bottom:max(10px,1vh)}.error-title,.info-label{color:var(--text-secondary);letter-spacing:.05em;text-transform:uppercase}.error-title{font-size:max(14px,min(2vw,1.8vh));font-weight:400}.info-bar{display:grid;grid-template-columns:repeat(auto-fit,minmax(200px,1fr));gap:max(15px,1.5vw);margin-bottom:max(20px,2vh);animation:.5s ease-out .2s backwards fadeIn}.info-item,.terminal{background:var(--terminal-bg);border:1px solid var(--border)}.info-item{padding:max(12px,1vw) max(20px,.4vw);display:flex;flex-direction:column;justify-content:space-between;align-items:center;transition:border-color .3s}.info-item:hover{border-color:var(--text-secondary)}.info-label{font-size:max(11px,.9vw)}.info-value{font-size:max(12px,1vw);color:var(--text-primary);font-weight:500}.button:hover,.log-level-success,.status-ok{color:var(--green)}.log-level-warn,.status-warning{color:var(--yellow)}.log-level-error,.status-error{color:var(--red)}.terminal{position:relative;overflow:hidden;animation:.5s ease-out .4s backwards fadeIn}.terminal-header{background:var(--border);padding:max(10px,1vw) max(15px,1.5vw);display:flex;align-items:center;gap:8px;border-bottom:1px solid var(--border)}.terminal-dot{width:12px;height:12px;border-radius:50%}.terminal-dot:first-child{background:var(--red)}.terminal-dot:nth-child(2){background:var(--yellow)}.terminal-dot:nth-child(3){background:var(--green)}.terminal-title{margin-left:10px;font-size:max(11px,.9vw);color:var(--text-secondary)}.terminal-body{padding:max(12px,2vw);height:max(400px,40vh);overflow-y:auto;font-size:max(12px,1vw);line-height:1.6;scrollbar-width:thin;scrollbar-color:var(--border) transparent}.terminal-body::-webkit-scrollbar{width:8px}.terminal-body::-webkit-scrollbar-thumb{background:var(--border);border-radius:4px}.terminal-body::-webkit-scrollbar-thumb:hover{background:var(--text-muted)}.log-line{margin-bottom:4px;opacity:0;animation:.6s ease-out forwards logAppear;white-space:pre-wrap;word-break:break-word}@keyframes logAppear{from{opacity:0;transform:translateX(-10px)}to{opacity:1;transform:translateX(0)}}.log-timestamp{color:var(--text-muted)}.log-level-info{color:var(--blue)}.log-message{color:var(--text-secondary)}.button-container{margin-top:max(20px,2vh);text-align:center;animation:.5s ease-out .6s backwards fadeIn}.button{display:inline-flex;align-items:center;gap:10px;padding:max(12px,1.2vw) max(24px,2.4vw);background:0;color:var(--text-primary);border:1px solid var(--border);font-size:max(13px,1vw);font-weight:500;cursor:pointer;transition:.3s;letter-spacing:.1em;text-transform:uppercase}.button:hover{border-color:var(--green)}.button svg{width:16px;height:16px;transition:transform .6s}.button:hover svg{transform:rotate(360deg)}@keyframes fadeIn{from{opacity:0;transform:translateY(10px)}to{opacity:1;transform:translateY(0)}}@media(max-width:1380px){.error-code{font-size:6vw}.error-title{font-size:2.2vw}.info-label{font-size:max(14px,.9vw)}.terminal-body{height:50vh;font-size:1.8vw;padding:8px}.terminal-header{padding:8px 12px}.terminal-dot{width:10px;height:10px}.terminal-title{font-size:max(14px,.9vLine truncated