Documentation
¶
Overview ¶
CaptchaBroker mediates VK Calls captcha challenges between the inbound runners (the "asker") and the operator's browser (the "solver"). When an inbound's auth chain hits a captcha:
- The vkcalls AdminWebviewCaptchaSolver calls Broker.Register, gets back a proxy URL + a wait channel
- Broker mounts a per-challenge reverse-proxy at /captcha-proxy/<id>/ using vkauth.MountAdminCaptchaProxy, so the operator's browser can navigate to the challenge with the same JS-shim experience as AutoProxy.
- Admin UI surfaces a pending-captcha badge with the proxy URL.
- Operator opens it, solves the I-am-not-a-robot click, the JS shim posts the captured success_token to /captcha-proxy/<id>/local-captcha-result. The proxy's onToken callback fires the wait channel; the solver returns; auth retries.
- Tokens expire fast (~1 min) — Broker.expireOldChallenges() sweeps stale entries and closes their channels so nobody hangs.
Concurrent challenges are isolated by a per-id URL path (/captcha-proxy/{id}). Multiple inbounds can be solving captchas at once — they get their own entries in Pending() and their own open-in-browser links.
Package admin is the HTTP control panel for goloom-wg-server.
Endpoints:
GET / dashboard HTML
GET /static/style.css styling
GET /login login form
POST /login username+password → session cookie
POST /logout revoke session cookie
GET /api/admin/state { username, is_default_password }
POST /api/admin/password change password (current+new)
GET /api/inbounds list (JSON)
POST /api/inbounds create
DELETE /api/inbounds/{id} remove
POST /api/inbounds/{id}/toggle pause/resume
GET /api/inbounds/{id}/client.conf wg client config
GET /api/inbounds/{id}/connstr goloom:// connection string
GET /api/inbounds/{id}/qr.png QR PNG of the connection string
GET /api/inbounds/{id}/vkturn-link vkturnproxy:// link (anton48 clients)
GET /api/inbounds/{id}/vkturn-qr.png QR PNG of the vkturnproxy:// link
GET /api/inbounds/{id}/history per-inbound traffic samples
GET /api/system/wg-interfaces live wg interface list
Auth is username + password (bcrypt-hashed) stored in a JSON file alongside the YAML config. On first start a random "admin" password is generated and printed to the log; the operator changes it via the dashboard. Sessions are in-memory (revoked on server restart).
Index ¶
- type CaptchaBroker
- func (b *CaptchaBroker) AttachToMux(mux *http.ServeMux)
- func (b *CaptchaBroker) Pending() []PendingCaptcha
- func (b *CaptchaBroker) Register(ctx context.Context, ch sfu.VKCaptchaChallenge, tag string) (string, <-chan string)
- func (b *CaptchaBroker) SetFingerprintSink(sink vkauth.CaptureSink, lg *log.Logger)
- type CredentialStore
- type Credentials
- type Options
- type PendingCaptcha
- type Server
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type CaptchaBroker ¶
type CaptchaBroker struct {
// contains filtered or unexported fields
}
CaptchaBroker is the admin-side captcha mediator. Implements vkauth.AdminCaptchaBroker.
func NewCaptchaBroker ¶
func NewCaptchaBroker() *CaptchaBroker
NewCaptchaBroker returns a fresh broker. AttachToMux must be called once before Register can be used.
func (*CaptchaBroker) AttachToMux ¶
func (b *CaptchaBroker) AttachToMux(mux *http.ServeMux)
AttachToMux installs the broker's HTTP routes on mux:
GET /captcha-proxy/{id}/... — per-challenge reverse-proxy +
JS shim, mounted dynamically by
[Register].
GET /api/captcha/pending — JSON list of pending challenges
Must be called exactly once. Subsequent Register calls require this.
func (*CaptchaBroker) Pending ¶
func (b *CaptchaBroker) Pending() []PendingCaptcha
Pending returns a snapshot of the in-flight challenges for the admin UI / API.
func (*CaptchaBroker) Register ¶
func (b *CaptchaBroker) Register(ctx context.Context, ch sfu.VKCaptchaChallenge, tag string) (string, <-chan string)
Register implements vkauth.AdminCaptchaBroker. Called by vkauth.AdminWebviewCaptchaSolver each time an inbound auth chain hits a captcha challenge.
func (*CaptchaBroker) SetFingerprintSink ¶
func (b *CaptchaBroker) SetFingerprintSink(sink vkauth.CaptureSink, lg *log.Logger)
SetFingerprintSink включает перехват browser_fp/device/UA на captcha-proxy для всех будущих challenges. Должно вызываться один раз при старте сервера, до первого Register; thread-safe но менять sink в runtime нет смысла. Передай nil чтобы выключить.
type CredentialStore ¶
type CredentialStore struct {
// contains filtered or unexported fields
}
CredentialStore wraps Credentials with a path and a mutex so that the password-change endpoint can persist atomically.
func LoadOrInit ¶
func LoadOrInit(path string) (store *CredentialStore, defaultPassword string, err error)
LoadOrInit returns a store backed by `path`. If the file does not exist, a fresh `admin` user is created with a 16-byte hex-encoded random password and IsDefaultPassword=true. The plaintext default password is returned (only on first init) so the caller can print it once for the operator — it cannot be recovered later.
func (*CredentialStore) ChangePassword ¶
func (s *CredentialStore) ChangePassword(newPassword string) error
ChangePassword swaps the stored hash. Caller is responsible for verifying the *current* password through Verify first.
func (*CredentialStore) State ¶
func (s *CredentialStore) State() Credentials
State returns a non-secret snapshot for the UI (no password hash).
func (*CredentialStore) Verify ¶
func (s *CredentialStore) Verify(username, password string) bool
Verify returns true if `password` matches the stored hash for `username`. Constant-time username comparison; bcrypt is naturally constant-time.
type Credentials ¶
type Credentials struct {
Username string `json:"username"`
PasswordHash string `json:"password_hash"`
// IsDefaultPassword stays true until the operator changes the
// auto-generated bootstrap password. The dashboard renders a "change
// me" banner while this is set.
IsDefaultPassword bool `json:"is_default_password"`
}
Credentials describes who can sign into the admin panel. Stored on disk separately from the YAML config (bcrypt hash + flags), so panel- initiated password changes don't churn the YAML on every save.
type Options ¶
type Options struct {
Listen string
Credentials *CredentialStore
TLSCert string
TLSKey string
AutoSelfSigned bool
Manager *inbound.Manager
Provisioner *wgprovision.Provisioner
Logger *log.Logger
// PublicEndpointHint fills in [Peer] Endpoint when generating
// client wg-quick configs. For the goloom architecture this is
// always loopback because the user runs the joiner locally; a
// future direct-WG mode would use the VPS's public IP here.
PublicEndpointHint string
// PublicURL — внешний URL admin-сервера. Используется для построения
// public ссылок на UI (admin dashboard, client connstrs где нужно
// показать оператору). Пример: "https://vps.example.com:9443".
// Если пусто — fallback на Listen со схемой https.
PublicURL string
// CaptchaBroker, when non-nil, exposes the VK Calls admin-webview
// captcha solver via /captcha-proxy/<id>/* and /api/captcha/*.
// The broker is also passed into the inbound.Manager so VK
// inbounds with captcha_mode=admin-webview can delegate solves to
// the operator's browser. nil disables the admin-webview path
// (auto/none modes still work via the Manager's other knobs).
CaptchaBroker *CaptchaBroker
}
type PendingCaptcha ¶
type PendingCaptcha struct {
ID string `json:"id"`
InboundTag string `json:"inbound_tag,omitempty"`
ProxyURL string `json:"proxy_url"`
RegisteredAt time.Time `json:"registered_at"`
ExpiresAt time.Time `json:"expires_at"`
}
PendingCaptcha is the JSON-friendly view of an in-flight challenge.
Source Files
¶
Directories
¶
| Path | Synopsis |
|---|---|
|
components
templ: version: v0.3.1001
|
templ: version: v0.3.1001 |
|
pages
templ: version: v0.3.1001
|
templ: version: v0.3.1001 |