Pulled a fresh copy of docs.rw's own Remnawave-vs-Marzban comparison table (not working from memory of an earlier read) to check what's still genuinely different after tonight's run of fixes — most rows already match or beat both panels (multi-admin, 2FA, HWID limits, backup/restore, host sorting, config validation, node autonomy, on-hold status as of a few commits ago). One concrete, bounded, unclaimed row: "Security measures in documentation" lists CF zero trust / custom path / Telegram OAuth / 2FA for Remnawave, nothing for Marzban. We already had 2FA and rate-limiting; custom path was the missing, actually implementable piece — everything else in that row is deployment guidance, not panel code. New ADMIN_PATH env var (config.py, defaults to "admin" — every existing install keeps working exactly as before with zero action needed). The page-serving route moves to whatever path is configured; root() on PANEL_DOMAIN only falls through to serving admin.html when ADMIN_PATH is still the default, otherwise it shows the same branded landing page every other domain gets — so a scanner or a human guessing "/admin" finds nothing once this is set, not even a redirect that confirms something lives there. Deliberately scoped to ONLY the page route. /admin/api/* stays fixed — it's already behind real cookie+session auth (verified this while auditing: every mutating admin route either calls require_admin() or the equivalent _require_current_admin(), checked programmatically via ast rather than trusting my memory of having added the check everywhere — found nothing actually missing, which is itself worth knowing, not just assumed). Moving the API namespace too would be a much bigger, riskier rewrite of every @app decorator in the file for no real security gain over what auth already provides. Deliberately NOT exposed in the Settings UI, unlike almost everything else made live-editable tonight. This one genuinely needs a process restart to take effect (FastAPI resolves routes at import time, not per-request), and a typo saved through the UI followed by a restart is a real self-lockout risk with no web-based way back — same tier as PANEL_DOMAIN/SUB_DOMAIN, which are also .env-only for the same reason. .env + SSH is the correct blast radius for a setting that can lock you out. Verification: config.py's normalization (strip slashes, empty/lone- slash/repeated-slash input all falling back to "admin" rather than accidentally producing a route at bare "/") tested directly — 8 cases. AST-extracted the updated root() out of api.py (still can't import the module locally) and exercised its actual branching with a mocked FileResponse/legal/request — confirmed the default case is byte-for- byte the old behavior and the custom-path case stops serving admin.html on PANEL_DOMAIN's root. Added a dedicated CI step that does what only a real FastAPI import can prove: with ADMIN_PATH set, /xyz123secret is a registered route, plain /admin is NOT (not just supplemented — actually gone), and /admin/api/login is untouched. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
78 lines
3.2 KiB
Text
78 lines
3.2 KiB
Text
# Copy this to .env and fill in real values. Never commit .env.
|
|
|
|
# Shown to clients everywhere: site, bot, subscription page, offer/privacy, panel
|
|
# login. Also editable live from Настройки in the admin panel, no restart needed.
|
|
BRAND_NAME=MBS Panel
|
|
|
|
# From @BotFather
|
|
BOT_TOKEN=123456789:AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
|
BOT_USERNAME=YourBot_robot
|
|
|
|
# Telegram user IDs allowed into the bot's admin menu and the web panel, comma-separated
|
|
ADMIN_IDS=111111111,222222222
|
|
|
|
# Web panel login password — must be a real random string, min 8 chars
|
|
# (panel refuses to start on "change-me"/"admin"/etc). Generate one: openssl rand -base64 12
|
|
# Change it any time with: mbs pass
|
|
ADMIN_PANEL_PASSWORD=
|
|
|
|
# Domains — point all three A records at this server's IP (see README)
|
|
PANEL_DOMAIN=panel.example.com
|
|
SUB_DOMAIN=sub.example.com
|
|
SITE_DOMAIN=example.com
|
|
|
|
# Optional: move the admin login off the well-known /admin path (e.g. to a
|
|
# random string) so it doesn't show up to anyone scanning for /admin,
|
|
# /login etc. Leave unset for the default. This is on top of the existing
|
|
# rate-limiting and 2FA, not instead of them. Requires `mbs restart` to
|
|
# take effect (it's a route, not a setting the running process can pick up
|
|
# live) — write it down somewhere before you restart, there's no UI for
|
|
# this on purpose, only .env + SSH can get you back in if you forget it.
|
|
ADMIN_PATH=admin
|
|
|
|
# Reality identity for the local node (this same box). Generate with:
|
|
# /usr/local/bin/xray x25519
|
|
# XRAY_PUBLIC_KEY is the "Password (PublicKey)" line; keep the matching
|
|
# private key only inside /usr/local/etc/xray/config.json (never here).
|
|
XRAY_PUBLIC_KEY=
|
|
REALITY_SNI=www.wildberries.ru
|
|
|
|
# Any 16-hex-char string per transport, e.g.: openssl rand -hex 8
|
|
XRAY_SHORT_ID_TCP=
|
|
XRAY_SHORT_ID_GRPC=
|
|
XRAY_SHORT_ID_XHTTP=
|
|
|
|
# Public hostname clients connect to for the local node (A record -> this server)
|
|
DE1_ADDRESS=de1.example.com
|
|
|
|
# Payments — off by default, bot keeps handing out free subscriptions on button press.
|
|
# Flip to true only once at least one provider below is configured and its webhook is live.
|
|
# All of this (toggle, prices, provider keys) is also editable live from the admin panel
|
|
# (Платежи tab) after first boot — no need to hand-edit this file or restart afterwards.
|
|
PAYMENTS_ENABLED=false
|
|
|
|
# Prices in RUB per plan (whole numbers). Only used when PAYMENTS_ENABLED=true.
|
|
PRICE_7D=150
|
|
PRICE_1M=399
|
|
PRICE_3M=999
|
|
PRICE_6M=1799
|
|
PRICE_1Y=2999
|
|
|
|
# ЮKassa — https://yookassa.ru, shop id + secret key from your account settings.
|
|
# Webhook to add in their dashboard: https://<PANEL_DOMAIN>/payments/webhook/yookassa
|
|
YOOKASSA_ENABLED=false
|
|
YOOKASSA_SHOP_ID=
|
|
YOOKASSA_SECRET_KEY=
|
|
|
|
# Platega — https://platega.io, merchant id + secret from your manager.
|
|
# Webhook to add in their dashboard: https://<PANEL_DOMAIN>/payments/webhook/platega
|
|
PLATEGA_ENABLED=false
|
|
PLATEGA_MERCHANT_ID=
|
|
PLATEGA_SECRET=
|
|
|
|
# Device limit (HWID) — off by default. Requires the VPN client app to send an
|
|
# x-hwid header on subscription fetch (Happ/v2rayTun-class apps do this); clients
|
|
# that don't send it get refused once enabled, so only flip this on if your users'
|
|
# apps actually support it. Also editable live from Настройки in the admin panel.
|
|
HWID_LIMIT_ENABLED=false
|
|
HWID_FALLBACK_LIMIT=3
|