diff --git a/FORK-CHANGELOG.md b/FORK-CHANGELOG.md index 92d2914..2b07b43 100644 --- a/FORK-CHANGELOG.md +++ b/FORK-CHANGELOG.md @@ -5,6 +5,14 @@ ## v1.2.6: вопросы при установке +* Набор по умолчанию — **REALITY + AmneziaWG-3.1** (самый совместимый минимум); ответ «все» + ставит полный набор автора. Умолчание задаётся в одном месте (`DEFAULT_PROTOS`). +* **Сайт-заглушка переделан на ИТ-тематику:** десять правдоподобных сайтов (хостинг, DevOps, + мониторинг, разработка, аудит, ИТ-поддержка, колокейшн, заметки про Linux, бэкапы, документация) + вместо авторских «кофеен и пекарен». Ставится случайный, конкретный — `kit local site 7`, + список — `kit local site --list`. Команда обновляет и файл для nginx, и копию страницы внутри + подключения Hysteria2 (`kit net masq on`): если менять файл вручную, 443/tcp и UDP-порт начнут + отвечать разными страницами — это заметно снаружи. * Обёртка спрашивает, **какие протоколы ставить** (нумерованный список, `1,3,11` или «все», Enter — набор автора): невыбранные протоколы не создаются вообще — ни подключения в панели, ни записей в nginx, ни ссылок в подписке. Список и умолчание взяты из самого установщика diff --git a/FORK.md b/FORK.md index 06075f4..26d3c6f 100644 --- a/FORK.md +++ b/FORK.md @@ -9,7 +9,7 @@ 1. Если нужен свой домен для маскировки — заранее направьте его A-запись на IP сервера и освободите порт 80 (иначе Let's Encrypt не выпустит сертификат). Без домена этот шаг пропускается. 2. Одна команда на сервере (от root) — **дальше скрипт спросит сам**: какие протоколы ставить - (нумерованный список: `1,3,11` или «все», Enter — набор автора), какую маскировку и домен, + (Enter — REALITY + AmneziaWG-3.1, «все» — набор автора), какую маскировку и домен, закрывать ли ответ на ping, включать ли автообновления, менять ли порт SSH (Enter — оставить текущий, число — перевести на него), менять ли пароль root: @@ -41,7 +41,8 @@ bash <(curl -fsSL …/scripts/3x-ui-local.sh) --dry-run --domain vpn.example.com | Флаг | Что делает | |---|---| -| `--protocols список` | какие протоколы ставить: `all`, `minimal` или через запятую — `reality,reality2,hy2,xhttp,ws,trojan,vmess,ss,tuic,wg,awg,awg3,mtproto`. Невыбранные не создаются вообще. Без флага — вопрос | +| `--protocols список` | какие протоколы ставить: `all`, `minimal` или через запятую — `reality,reality2,hy2,xhttp,ws,trojan,vmess,ss,tuic,wg,awg,awg3,mtproto`. Невыбранные не создаются вообще. Без флага — вопрос, по умолчанию предлагаются **REALITY + AmneziaWG-3.1** (ответ «все» — набор автора) | +| `--site N\|random\|off` | сайт-заглушка: конкретный из нашего набора (10 ИТ-сайтов), случайный (по умолчанию) или не трогать авторскую страницу (то же — `--no-site`) | | `--yes` | не задавать вопросы (ping закрывается, автообновления включаются, порт SSH и пароль не трогаются) | | `--harden` | закрыть ответ на ping и включить автообновления без вопросов | | `--domain домен` | свой домен для маскировки: сайт и REALITY маскируются под него (A-запись на сервер, свободный 80) | @@ -88,6 +89,8 @@ kit local hardening # сводка по защите сервер | Команда | Что делает | |---|---| +| `kit local site` | поставить случайный сайт-заглушку из нашего набора (10 ИТ-сайтов) и синхронизировать ответ Hysteria2 | +| `kit local site --list` / `kit local site 7` / `--show` | список сайтов с номерами / поставить конкретный / какой стоит сейчас | | `kit local ssh 2222` | добавить порт SSH (прежние продолжают работать; порт сначала открывается в ufw, конфиг проверяется через `sshd -t`) | | `kit local ssh --status` | какие порты SSH слушаются сейчас | | `kit local ssh --close-old 2222` | закрыть прежние порты после того, как новый проверен из второго окна | diff --git a/scripts/3x-ui-local.sh b/scripts/3x-ui-local.sh index edb8946..4773bc1 100644 --- a/scripts/3x-ui-local.sh +++ b/scripts/3x-ui-local.sh @@ -9,10 +9,12 @@ # а можно ничего не передавать — тогда он задаст вопросы сам (в том числе про домен); # 4) спрашивает, какие протоколы ставить: невыбранные не создаются вообще — ни подключения в панели, # ни записей в nginx, ни ссылок в подписке (за это отвечает флаг автора --protocols); -# 5) спрашивает и делает то, чего у автора нет: закрыть ответ на ping, включить обновления +# 5) ставит сайт-заглушку: случайный правдоподобный ИТ-сайт из нашего набора (10 штук; +# --site N — конкретный, --no-site — не трогать авторскую страницу); +# 6) спрашивает и делает то, чего у автора нет: закрыть ответ на ping, включить обновления # безопасности, перевести SSH на другой порт, сменить пароль root (поведение из StressOzz/vps-setup, # но безопаснее); -# 6) в конце: kit fix (время и безопасные починки), kit check, kit local hardening. +# 7) в конце: kit fix (время и безопасные починки), kit check, kit local hardening. # # Запуск: # # одна команда, дальше — вопросы: @@ -73,7 +75,9 @@ usage() { --no-harden не трогать ping и автообновления --ssh-port N перевести SSH на порт N (прежний закроется после проверки входа) --yes ни о чём не спрашивать (для автоматизации) - --no-time не запускать kit fix (время и безопасные починки) + --site N|random|off сайт-заглушка: конкретный из нашего набора, случайный (по умолчанию) или + не трогать авторскую страницу (то же: --no-site) + --no-time не запускать kit fix (время и безопасные починки) --no-check не запускать в конце kit check --verify-only только проверить релиз (подпись и SHA256) и выйти --dry-run показать план и ничего не делать @@ -84,6 +88,7 @@ EOF # ------------------------------------------------------------------ разбор флагов INSTALLER_ARGS=(); harden=auto; ssh_port=""; fix_time=yes; do_check=yes; dry=no; verify_only=no; assume_yes=no +site=random # заглушка: случайный сайт из нашего набора (off — не трогать) while (($#)); do case $1 in --harden) harden=yes ;; @@ -91,6 +96,9 @@ while (($#)); do --ssh-port) ssh_port=${2:-}; shift || die "--ssh-port: нужен номер порта" ;; --ssh-port=*) ssh_port=${1#*=} ;; --yes | -y) assume_yes=yes ;; + --site) site=${2:-random}; shift || die "--site: нужен номер сайта (1..10), random или off" ;; + --site=*) site=${1#*=} ;; + --no-site) site=off ;; --no-time) fix_time=no ;; --no-check) do_check=no ;; --verify-only) verify_only=yes ;; @@ -102,6 +110,11 @@ while (($#)); do done [[ -z $ssh_port ]] || { [[ $ssh_port =~ ^[0-9]{1,5}$ ]] && ((10#$ssh_port >= 1024 && 10#$ssh_port <= 65535)); } \ || die "--ssh-port: нужно число 1024–65535, например 2222" +if [[ $site != off && $site != random ]]; then + # 10 — число сайтов в наборе (scripts/kit-local.sh, KL_SITE_TITLES); добавите сайт — поднимите границу + [[ $site =~ ^[0-9]{1,2}$ ]] && ((10#$site >= 1 && 10#$site <= 10)) \ + || die "--site: random, off или номер 1..10 (список: kit local site --list)" +fi # Если --protocols задан флагом — не спрашиваем и не дублируем его. proto_flag="" @@ -124,11 +137,16 @@ if [[ $dry == yes ]]; then elif [[ $harden == yes ]]; then pre "защита: закрыть ping и включить автообновления (--harden)" else pre "защита: пропускаю (--no-harden)"; fi if [[ -n $proto_flag ]]; then pre "протоколы: ${proto_flag} (задано флагом)" - elif interactive; then pre "протоколы: спрошу (Enter — всё, кроме WireGuard и REALITY-2)" - else pre "протоколы: всё, кроме WireGuard и REALITY-2"; fi + elif interactive; then pre "протоколы: спрошу (Enter — REALITY + AmneziaWG-3.1, «все» — набор автора)" + else pre "протоколы: REALITY + AmneziaWG-3.1 (без терминала вопроса нет)"; fi if [[ -n $ssh_port ]]; then pre "порт SSH: перевожу на $ssh_port (прежний — после проверки входа)" elif interactive; then pre "порт SSH: спрошу (Enter — оставить текущий)"; fi if interactive; then pre "пароль root: спрошу (по умолчанию — не менять)"; fi + case $site in + off) pre "заглушка: оставляю авторскую страницу (--no-site)" ;; + random) pre "заглушка: случайный ИТ-сайт из нашего набора (10 штук)" ;; + *) pre "заглушка: сайт №$site из нашего набора (список: kit local site --list)" ;; + esac [[ $fix_time == yes ]] && pre "после: kit fix (время, безопасные починки)" [[ $do_check == yes ]] && pre "проверка: kit check + kit local hardening" echo @@ -197,7 +215,10 @@ PROTO_HINT=( "AmneziaWG-3.1 AmneziaWG с защитой заголовков: AmneziaVPN" "MTProto прокси для Telegram" ) -DEFAULT_PROTOS=(reality hy2 xhttp ws trojan vmess ss tuic awg awg3 mtproto) +# Наш набор по умолчанию: VLESS REALITY и AmneziaWG-3.1 — самый совместимый минимум. +# Набор автора (флаг --protocols all) доступен ответом «все». +AUTHOR_PROTOS=(reality hy2 xhttp ws trojan vmess ss tuic awg awg3 mtproto) +DEFAULT_PROTOS=(reality awg3) proto_menu() { local i n mark @@ -207,7 +228,8 @@ proto_menu() { [[ " ${DEFAULT_PROTOS[*]} " == *" ${ALL_PROTOS[i]} "* ]] && mark="*" printf ' %s %2d. %s\n' "$mark" "$n" "${PROTO_HINT[i]}" >&2 done - printf ' %s\n' "${D}[* — набор по умолчанию (всё, кроме WireGuard и REALITY-2). Невыбранные протоколы не создаются вообще.]${N}" >&2 + printf ' %s\n' "${D}[* — по умолчанию: REALITY + AmneziaWG-3.1. «все» — набор автора (всё, кроме WireGuard).${N}" >&2 + printf ' %s\n' "${D}[Невыбранные протоколы не создаются вообще: ни подключения, ни записей в nginx, ни ссылок в подписке.]${N}" >&2 } proto_parse() { # ответ → выбранные имена через запятую; код 1, если не разобрал @@ -215,7 +237,7 @@ proto_parse() { # ответ → выбранные имена через зап ans=${ans//[[:space:]]/,} # «1 3 11» и «1,3,11» — одно и то же [[ -n $ans ]] || { printf '%s' "$(IFS=,; echo "${DEFAULT_PROTOS[*]}")"; return 0; } case $ans in - all | все | всё) printf '%s' "$(IFS=,; echo "${DEFAULT_PROTOS[*]}")"; return 0 ;; + all | все | всё) printf '%s' "$(IFS=,; echo "${AUTHOR_PROTOS[*]}")"; return 0 ;; minimal | минимум) printf '%s' "reality"; return 0 ;; esac local IFS=',' @@ -238,7 +260,7 @@ ask_protocols() { # печатает итоговый список через з interactive || { printf '%s' "$(IFS=,; echo "${DEFAULT_PROTOS[*]}")"; return 0; } proto_menu for i in 1 2 3; do - printf '\n%sНомера через запятую (например 1,3,11) или «все»; Enter — набор по умолчанию: %s' "$B" "$N" >&2 + printf '\n%sНомера через запятую (например 1,12) или «все»; Enter — REALITY + AmneziaWG-3.1: %s' "$B" "$N" >&2 ans="" read -r -t 300 ans <"$ASK_SRC" 2>/dev/null || true if picked=$(proto_parse "$ans"); then printf '%s' "$picked"; return 0; fi @@ -281,6 +303,21 @@ ask_yes() { # "вопрос" y|n — по умолчанию [[ $ans =~ ^(y|yes|д|да)$ ]] } +# Сайт-заглушка: у автора это случайные «кофейни и пекарни», у нас — правдоподобный ИТ-сайт. +# Меняем не файл вручную, а командой форка: она же обновляет копию страницы в ответе Hysteria2, +# иначе 443/tcp и UDP-порт начнут отвечать разными страницами (это видно снаружи). +case $site in + off) say "заглушку не трогаю (--no-site): остаётся страница автора" ;; + random) + say "ставлю сайт-заглушку: случайный ИТ-сайт из нашего набора" + kit local site || warn "не удалось поставить заглушку — позже: kit local site" + ;; + *) + say "ставлю сайт-заглушку №$site" + kit local site "$site" || warn "не удалось поставить заглушку — позже: kit local site $site" + ;; +esac + if [[ $fix_time == yes ]]; then say "kit fix: синхронизация времени и безопасные починки (авторская команда, ничего необратимого)" kit fix || warn "kit fix завершился с замечаниями — посмотрите вывод" diff --git a/scripts/kit-local.sh b/scripts/kit-local.sh index 0542de4..b08d501 100644 --- a/scripts/kit-local.sh +++ b/scripts/kit-local.sh @@ -74,6 +74,12 @@ kit_local_help() { kit local masking состояние маскировки (у REALITY должен быть свой домен) kit local hardening сводка по защите сервера и что ещё можно включить + Заглушка (сайт на 443 и ответ Hysteria2 — одна и та же страница) + kit local site поставить случайный сайт из нашего набора (10 штук) + kit local site --list список сайтов с номерами + kit local site 7 поставить конкретный + kit local site --show какой сайт стоит сейчас + Защита сервера (поведение взято из StressOzz/vps-setup, сделано безопаснее) kit local ssh 2222 добавить порт SSH (прежние продолжают работать) kit local ssh --status какие порты SSH слушаются сейчас @@ -452,6 +458,406 @@ EOF return 0 } +# ------------------------------------------------------------------ заглушка: ИТ-сайты (наши) +# Десять правдоподобных ИТ-сайтов: хостинг, DevOps, мониторинг, разработка, безопасность, +# поддержка, сети и колокейшн, заметки про Linux, бэкапы, документация проекта. +# Ставится случайный (kit local site), конкретный — по номеру (kit local site 7). +# Страница отдаётся nginx на 443 при неизвестном SNI и синхронизируется в ответ Hysteria2 +# (kit net masq on) — поэтому менять её нужно этой командой, иначе 443/tcp и UDP-порт начнут +# отвечать разными страницами (это заметно снаружи). + +KL_SITE_FILE=/var/www/kit/index.html +KL_SITE_TITLES=( + "Nordhost — VPS and cloud hosting" + "Circuit Nine — DevOps consulting" + "Cortex Monitor — server monitoring" + "Bluefin Software — custom development" + "Vantage Security — audits and penetration tests" + "Keystone IT — managed systems support" + "Meridian Networks — colocation and transit" + "Lumen Notes — Linux and networking" + "Evercore Backup — backup and recovery" + "Halyard Docs — project documentation" +) +KL_SITE_TOPICS=( + "VPS-хостинг: тарифы, регионы, платформа" + "DevOps-консалтинг: CI/CD, Kubernetes, наблюдаемость" + "мониторинг серверов: метрики, алерты, статус-страница" + "разработка ПО на заказ: портфолио, стек, процесс" + "аудит и пентест: этапы, отчёт, повторная проверка" + "ИТ-поддержка: серверы, сети, рабочие места" + "колокейшн и транзит: стойки, каналы, BGP" + "заметки про Linux, nginx и сети" + "бэкапы и восстановление: политика, проверки, тесты" + "документация проекта: установка, конфигурация, справка" +) + +KL_SITE_CSS=':root{--fg:#1b1f24;--mu:#5b6670;--bd:#e3e6ea;--ac:#1f6feb;--bg:#fff} +*{box-sizing:border-box} +body{margin:0;font:16px/1.6 -apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Helvetica,Arial,sans-serif;color:var(--fg);background:var(--bg)} +.wrap{max-width:980px;margin:0 auto;padding:0 20px} +header{border-bottom:1px solid var(--bd)} +header .wrap{display:flex;align-items:center;justify-content:space-between;height:64px} +.logo{font-weight:700;text-decoration:none;color:var(--fg)} +nav a{color:var(--mu);text-decoration:none;margin-left:18px;font-size:15px} +.hero{padding:72px 0 40px} +.hero h1{font-size:40px;line-height:1.15;margin:0 0 16px;letter-spacing:-.02em} +.hero p{font-size:18px;color:var(--mu);max-width:640px;margin:0 0 28px} +.btn{display:inline-block;background:var(--ac);color:#fff;text-decoration:none;padding:12px 22px;border-radius:6px;font-weight:600} +.cards{display:grid;grid-template-columns:repeat(auto-fit,minmax(240px,1fr));gap:20px;padding:8px 0 44px} +.card{border:1px solid var(--bd);border-radius:8px;padding:22px} +.card h3{margin:0 0 8px;font-size:18px} +.card p{margin:0;color:var(--mu)} +.prose{padding:0 0 56px;max-width:780px} +.prose h2{font-size:22px;margin:34px 0 10px} +.prose p,.prose li{color:var(--mu)} +code{background:#f4f6f8;padding:2px 5px;border-radius:4px;font-size:14px} +footer{border-top:1px solid var(--bd);padding:26px 0;color:var(--mu);font-size:14px} +table{border-collapse:collapse;width:100%} +th,td{border-bottom:1px solid var(--bd);text-align:left;padding:10px 8px;font-size:15px} +ul{padding-left:20px} +@media(max-width:600px){.hero{padding:44px 0 28px}.hero h1{font-size:29px}}' + +kl_site_open() { # заголовок описание бренд «навигация как HTML» + cat < + + + + +$1 + + + + + +
+HTML +} + +kl_site_close() { # бренд + cat <

© $(date +%Y) $1 · Imprint · Privacy · Status

+ + +HTML +} + +kl_site_html() { # номер (с нуля) → страница на stdout + case $1 in + 0) # хостинг + kl_site_open "Nordhost — VPS and cloud hosting" "Virtual servers on NVMe storage in three European regions. Hourly billing, snapshots, private networks and a full API." "Nordhost" 'PlansNetworkDocs' + cat <<'HTML' +
+
+

Virtual servers that stay out of the way

+

KVM instances in Frankfurt, Amsterdam and Zurich. Hourly billing, snapshots, private networking, and an API for everything the control panel can do.

+See plans +
+
+

Starter — €4/mo

1 vCPU, 2 GB RAM, 40 GB NVMe, 20 TB traffic.

+

Standard — €9/mo

2 vCPU, 4 GB RAM, 80 GB NVMe, 40 TB traffic.

+

Compute — €18/mo

4 vCPU, 8 GB RAM, 160 GB NVMe, 100 TB traffic.

+
+
+

Network

+

Every region is connected with redundant 10 GbE uplinks. Traffic inside a region is free, between regions it is billed at cost. IPv6 is included, IPv4 on request.

+

Platform

+ +
+
+HTML + kl_site_close "Nordhost" ;; + 1) # DevOps-консалтинг + kl_site_open "Circuit Nine — DevOps consulting" "We help small teams ship safely: CI/CD, Kubernetes, infrastructure as code and observability." "Circuit Nine" 'WorkApproachAbout' + cat <<'HTML' +
+
+

Ship on Fridays without holding your breath

+

We set up pipelines, infrastructure and monitoring so that releasing is a boring, repeatable event — and so that on-call does not ruin anyone’s week.

+What we do +
+
+

CI/CD

Build pipelines, artefact registries, staging environments and one-command rollbacks.

+

Infrastructure

Terraform and Ansible for servers and cloud, with reviews and tests in the pipeline.

+

Observability

Metrics, logs and traces wired together, with alerts that people actually trust.

+
+
+

How we work

+

A two-week discovery, then small changes with a review after each one. Everything lands in your repository and your accounts — no private tooling, no lock-in.

+ + + + + +
WeekWhat happens
1–2Audit of the current setup, a written report with priorities
3–6Pipelines and infrastructure, pairing with your engineers
7–8Monitoring, runbooks and a handover session
+
+
+HTML + kl_site_close "Circuit Nine" ;; + 2) # мониторинг + kl_site_open "Cortex Monitor — server monitoring" "Uptime, resource and certificate monitoring with alerting to Telegram, e-mail and webhooks." "Cortex Monitor" 'ChecksAlertsPricing' + cat <<'HTML' +
+
+

Know about the outage before your users do

+

HTTP, TCP, ICMP and TLS checks from six locations, host metrics through a lightweight agent, and alerts that reach you in seconds.

+See checks +
+
+

Availability

HTTP and TCP checks with keyword matching, redirects and status codes.

+

Resources

CPU, memory, disk and network of every host on one page.

+

Certificates

Expiry warnings 30, 14 and 3 days ahead, with a list of what needs renewing.

+
+
+

Alerting

+

Alerts are grouped so that a single broken router does not produce forty messages. Quiet hours, maintenance windows and escalation to a second contact are configurable per check.

+

Maybe you do not need us yet

+

If you run one server, an uptime check and a cron job with certificate renewal will do. Come back when there are more of them than you can remember.

+
+
+HTML + kl_site_close "Cortex Monitor" ;; + 3) # разработка ПО + kl_site_open "Bluefin Software — custom development" "A small team building web services, integrations and internal tools for companies that grew out of spreadsheets." "Bluefin Software" 'ServicesStackContact' + cat <<'HTML' +
+
+

Software for the part of the business nobody sells

+

Integrations, back-office tools and web services. We take a process that lives in three spreadsheets and one person’s head, and give it a proper home.

+Services +
+
+

Web services

APIs and admin panels in Python or Go, deployed to your infrastructure.

+

Integrations

Accounting, warehouses, CRM and everything that has an API or a database.

+

Rescue work

Taking over an unfinished project: audit, plan, tidy up, finish.

+
+
+

Stack

+

Python (Django, FastAPI), PostgreSQL, Redis, Docker, and whatever the customer already runs. We keep deployments simple enough that a new colleague can understand them in a day.

+

Team

+

Four engineers and a part-time designer. We take on three projects at a time so that nobody is queued behind somebody else’s deadline.

+
+
+HTML + kl_site_close "Bluefin Software" ;; + 4) # безопасность + kl_site_open "Vantage Security — audits and penetration tests" "External and internal penetration tests, configuration reviews and help with fixing what we find." "Vantage Security" 'ServicesProcessContact' + cat <<'HTML' +
+
+

Find the problems before somebody else does

+

Penetration tests and configuration reviews for web services, networks and cloud accounts — with a report your engineers can act on and a retest when the fixes are in.

+Services +
+
+

External test

Everything reachable from the internet: services, TLS, DNS, mail.

+

Internal test

What an attacker does after getting a foothold in an office or a VPN.

+

Review

Settings, backups, access rights and monitoring — usually the cheapest findings.

+
+
+

Process

+

Scope, rules of engagement and a test window are agreed in writing first. We work against a staging copy where possible, and we never touch production data beyond what the scope allows.

+

Report

+

Findings are ranked by what they let an attacker do, with reproduction steps and concrete fixes. A retest of the fixed issues is included in the price.

+
+
+HTML + kl_site_close "Vantage Security" ;; + 5) # ИТ-поддержка + kl_site_open "Keystone IT — managed systems support" "We look after servers, networks, backups and workstations for offices of ten to two hundred people." "Keystone IT" 'ServicesSupportContact' + cat <<'HTML' +
+
+

An IT department, without hiring one

+

Monitoring, updates, backups and a real person on the phone. Contracts start at a fixed monthly fee, and you can leave with thirty days’ notice.

+What is included +
+
+

Servers

Updates, monitoring, disk space and certificate renewal handled for you.

+

Backups

Nightly copies, offsite storage and a restore test twice a year.

+

Workplaces

Accounts, laptops, printers and the printer driver that never works.

+
+
+

Support

+

Weekdays from 8 to 19, with an on-call line for outages. Response time for a full outage is one hour, for ordinary requests one working day.

+

We keep a written inventory of your systems, so that a new engineer — ours or yours — can take over without a two-week interview with the person who built it.

+
+
+HTML + kl_site_close "Keystone IT" ;; + 6) # сети и колокейшн + kl_site_open "Meridian Networks — colocation and transit" "Rack space, cross-connects and IP transit in two carrier-neutral facilities." "Meridian Networks" 'ColocationTransitContact' + cat <<'HTML' +
+
+

Racks, cross-connects and transit

+

Quarter, half and full racks in Frankfurt and Amsterdam, with remote hands, redundant power and a choice of transit providers.

+Colocation +
+
+

Quarter rack

10 U, 2 kW, 1 Gbps port and one IPv4 prefix.

+

Half rack

22 U, 4 kW and a redundant 10 Gbps pair.

+

Private cage

From eight racks, with your own access control and monitoring.

+
+
+

Transit and peering

+

We announce your prefix over two upstreams and the local internet exchange. BGP communities for traffic engineering, RPKI filtering and a looking glass are available to customers.

+ + + + + +
PortCommitted rate
1 Gbpsfrom 50 Mbps
10 Gbpsfrom 1 Gbps
2×10 Gbpsfrom 2 Gbps, redundant
+
+
+HTML + kl_site_close "Meridian Networks" ;; + 7) # блог + kl_site_open "Lumen Notes — Linux and networking" "Short notes about servers, nginx, storage and networks — written while working, mostly for myself." "Lumen Notes" 'PostsAbout' + cat <<'HTML' +
+
+

Notes from a server room

+

Things I worked out once and do not want to work out again: nginx, systemd, ZFS, routing and the odd shell trick.

+Read the posts +
+
+

Replacing a certificate without dropping connections

+

Reload, not restart. systemctl reload nginx keeps the listeners open and swaps the configuration, so long downloads and websockets survive. A restart does not.

+

Finding what filled the disk

+

du -xh --max-depth=1 / | sort -h first, then follow the biggest branch. Journals under /var/log/journal are a common answer; journalctl --vacuum-size=200M trims them.

+

Checking that a port is really listening

+

ss -ltnp shows TCP listeners and the process behind each one. If something answers on 443 but is not in the list, look for a proxy in front of it.

+

Backup is not a copy

+

A copy on the same disk protects against nothing but a fat finger. The test that matters is a restore, done on purpose, on a machine that is not the original.

+
+
+HTML + kl_site_close "Lumen Notes" ;; + 8) # бэкапы + kl_site_open "Evercore Backup — backup and recovery" "Offsite backups with a documented restore procedure and a restore test every quarter." "Evercore Backup" 'PolicyRestoreContact' + cat <<'HTML' +
+
+

A backup you have never restored is a rumour

+

We copy your servers and databases to a separate location, keep versions, and prove once a quarter that a restore actually works.

+How it works +
+
+

Nightly copies

Files and databases, encrypted before they leave the server.

+

Versions

Hourly for a day, daily for a month, monthly for a year.

+

Restore test

A quarterly restore into a clean machine, with a short report.

+
+
+

Recovery

+

A lost file takes minutes: we restore the version you pick. A lost server takes an hour or two: a prepared image plus your latest copy, booted and checked before it goes back into service.

+

Encryption keys are kept by you as well as by us, so that neither side alone can read the copies.

+
+
+HTML + kl_site_close "Evercore Backup" ;; + 9) # документация проекта + kl_site_open "Halyard Docs — project documentation" "Installation, configuration and reference for a self-hosted service: everything in one place." "Halyard Docs" 'InstallConfigReference' + cat <<'HTML' +
+
+

Documentation

+

Installation, configuration and the reference pages. Written for the current release; older versions keep their own section.

+Start here +
+
+

Installation

+

Download the archive, unpack it into /opt/halyard and run the setup script. The script asks three questions and writes a configuration file next to the binary.

+

Configuration

+

Settings live in /etc/halyard/config.toml. Every option has a default, so a minimal file is often enough. Changes are applied with halyard reload — no restart, no dropped connections.

+
+
+

Reference

+ + + + + +
CommandWhat it does
halyard statusShows the running version and the state of each component
halyard checkValidates the configuration and the listening ports
halyard logsFollows the log with readable timestamps
+

Questions and patches are welcome through the issue tracker; the address is on the contact page.

+
+
+HTML + kl_site_close "Halyard Docs" ;; + *) kl_die "нет такого сайта: $1" ;; + esac +} + +kl_site_current() { # какой из наших сайтов стоит сейчас (пусто — не наш) + local i t + [[ -s $KL_SITE_FILE ]] || return 0 + for i in "${!KL_SITE_TITLES[@]}"; do + t=${KL_SITE_TITLES[i]} + if grep -qF "$t" "$KL_SITE_FILE" 2>/dev/null; then + printf '%s' "$((i + 1)). $t" + return 0 + fi + done + return 0 +} + +kit_local_site() { + kl_args "$@" + local arg=${KL_REST[0]:-} n cur + case $arg in + --list | list) + kl_say "сайты-заглушки в наборе (ставится случайный)" + local i + for i in "${!KL_SITE_TITLES[@]}"; do + printf ' %2d. %-22s %s\n' "$((i + 1))" "${KL_SITE_TITLES[i]%% — *}" "${KL_SITE_TOPICS[i]}" + done + printf ' %s\n' "поставить конкретный: kit local site 7" + return 0 ;; + --show | show | status) + cur=$(kl_site_current) + if [[ -n $cur ]]; then kl_ok "сейчас стоит наш сайт: $cur" + elif [[ -s $KL_SITE_FILE ]]; then kl_ok "сейчас стоит страница автора (случайная из его наборов)" + else kl_warn "файла $KL_SITE_FILE нет"; fi + return 0 ;; + "" | random | --random) n=$((RANDOM % ${#KL_SITE_TITLES[@]})) ;; + *) + [[ $arg =~ ^[0-9]{1,2}$ ]] && ((10#$arg >= 1 && 10#$arg <= ${#KL_SITE_TITLES[@]})) \ + || { kl_die "kit local site [номер 1..${#KL_SITE_TITLES[@]}|--list|--show]"; return 1; } + n=$((10#$arg - 1)) ;; + esac + if kl_dry; then + kl_say "заглушка: ${KL_SITE_TITLES[n]} (${KL_SITE_TOPICS[n]})" + printf ' (dry-run) записал бы %s и синхронизировал бы ответ Hysteria2 (kit net masq on)\n' "$KL_SITE_FILE" + return 0 + fi + kl_need_root || return 1 + kl_say "заглушка: ${KL_SITE_TITLES[n]} (${KL_SITE_TOPICS[n]})" + install -d -m 755 /var/www/kit + [[ -f $KL_SITE_FILE ]] && kl_backup "$KL_SITE_FILE" + if ! kl_site_html "$n" >"$KL_SITE_FILE.new"; then + rm -f "$KL_SITE_FILE.new"; kl_die "не удалось собрать страницу"; return 1 + fi + chmod 644 "$KL_SITE_FILE.new" && mv "$KL_SITE_FILE.new" "$KL_SITE_FILE" \ + || { rm -f "$KL_SITE_FILE.new"; kl_die "не удалось заменить $KL_SITE_FILE"; return 1; } + kl_ok "страница обновлена: $KL_SITE_FILE ($(wc -c <"$KL_SITE_FILE") байт)" + # Порт 443 отдаёт файл через nginx (перезагрузка не нужна). UDP-порт Hysteria2 держит копию + # страницы внутри подключения — её надо обновить, иначе ответы разойдутся. + if kl_have kit; then + if kit net masq on >/dev/null 2>&1; then + kl_ok "Hysteria2 отвечает той же страницей" + else + kl_warn "Hysteria2 не синхронизирован (может, его нет в этой установке) — проверьте: kit net masq on" + fi + fi + kl_ok "посмотреть снаружи: curl -k https:/// ; состояние: kit local site --show, kit check" +} + kit_local_cmd() { case ${1:-help} in help | -h | --help) kit_local_help ;; @@ -464,6 +870,7 @@ kit_local_cmd() { passwd | pass) shift; kit_local_passwd "$@" ;; upgrades | auto) shift; kit_local_upgrades "$@" ;; hardening | hard) shift; kit_local_hardening "$@" ;; + site | stub | stub-site) shift; kit_local_site "$@" ;; *) printf 'Неизвестная команда форка: %s\n' "$1" >&2 kit_local_help >&2