Documentation
¶
Overview ¶
Package webui serves an authenticated, dark-themed web dashboard on port 7777 showing live system metrics, tunnels and their logs.
Index ¶
- func Disable() error
- func Running() bool
- func Save(c Config) error
- func Serve() error
- func TunnelLogs(name string) string
- type Config
- func EnsurePassword() (Config, error)
- func EnsureRunning() (Config, error)
- func Load() Config
- func RegenerateBasePath() (Config, error)
- func RegeneratePassword() (Config, error)
- func SetBasePath(path string) (Config, error)
- func SetCredentials(user, pw string) (Config, error)
- func SetPassword(pw string) (Config, error)
- func SetPort(port int) (Config, error)
- func SetUsername(user string) (Config, error)
- type RatePoint
- type SessionEntry
- type SystemStats
- type TunnelInfo
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func TunnelLogs ¶
TunnelLogs returns the last N journal lines for a tunnel service.
Types ¶
type Config ¶
type Config struct {
Username string `json:"username,omitempty"` // Login username (default: admin)
Password string `json:"password"` // Login password
Port int `json:"port"`
// BasePath is the secret path segment the whole panel lives under, so it
// answers at http://host:7777/<BasePath>/ and at nothing else.
//
// It is not authentication and does not pretend to be — the password is
// still what lets anybody in. What it changes is who ever reaches the
// password prompt. A panel on a known port at "/" is found by anything that
// sweeps the internet within hours of being started, and from then on it is
// answering login attempts from strangers forever. Behind a path nobody can
// guess, those sweeps get a 404 and go away, and the login page is seen by
// people who were told where it is.
//
// Generated on first use and on upgrade, so every panel has one; see
// EnsureBasePath. "/" turns it off for an operator who wants the panel at
// the root, which is the only way to get that now.
BasePath string `json:"base_path,omitempty"`
// HTTPS, when set, serves the panel over TLS instead of plain HTTP.
//
// It is off by default and stays that way on upgrade: a panel reached at
// http://ip:7777 keeps working exactly as it did. Turning it on is a
// deliberate act, because it changes the address people have bookmarked.
//
// TLSDomain switches to Let's Encrypt for that name, which must resolve to
// this server; empty means the generated self-signed certificate, which
// works on a bare IP. Certificates renew themselves either way — an ACME
// one is reissued well before its ninety days are up and picked up on the
// next connection, with no restart.
HTTPS bool `json:"https,omitempty"`
TLSDomain string `json:"tls_domain,omitempty"`
TLSEmail string `json:"tls_email,omitempty"`
// TLSSelfHost is an optional domain or IP to add to the self-signed
// certificate's SANs, for reaching the panel by a name that has no public
// DNS for Let's Encrypt (an internal domain, a host that is only in the
// operator's /etc/hosts). It changes nothing about which addresses already
// work — every local IP and loopback are always included — it only adds one
// the machine cannot discover on its own. Empty is the common case.
TLSSelfHost string `json:"tls_self_host,omitempty"`
}
Config is the persisted web-panel configuration.
func EnsurePassword ¶
EnsurePassword returns the config, generating and saving an 8-digit password and a base path if either is missing.
The base path is generated here rather than only on a fresh install, so a panel that has been running at "/" for a year gets one on the next upgrade. That does move the address: the CLI's Web Panel screen prints the whole URL, including the path, which is where an operator whose bookmark stopped working is told to look — and the address is on the machine they already have a shell on, which is the one place it can be found without being findable by anybody else.
func EnsureRunning ¶
EnsureRunning makes sure a password exists and the web-panel systemd service is installed and running. Safe to call repeatedly (idempotent).
func RegenerateBasePath ¶
RegenerateBasePath moves the panel to a new unguessable path and restarts it.
The path is not a secret that has to be rotated on a schedule — it is not authentication and nothing is signed with it. What it is for is the case where it stopped being unguessable: pasted into a chat, screenshotted, typed on a machine that was not the operator's. Then the old one is worth throwing away, and there has to be a way to do it that does not involve editing JSON.
The restart is what makes it take effect; the running server read its path once, at startup.
func RegeneratePassword ¶
RegeneratePassword creates a new 8-digit password and restarts the panel.
func SetBasePath ¶
SetBasePath persists a path the operator chose, or "/" to put the panel back at the root, and restarts it.
func SetCredentials ¶
SetCredentials persists both username and password and restarts the panel service.
func SetPassword ¶
SetPassword persists a custom password and restarts the panel service so the change takes effect. Used from the CLI (a separate process from the server).
func SetPort ¶
SetPort persists a new panel port and restarts the panel service so it listens there. Used from the CLI (a separate process from the server).
func SetUsername ¶
SetUsername persists a custom username and restarts the panel service so the change takes effect. Used from the CLI (a separate process from the server).
func (Config) PathPrefix ¶
PathPrefix is the panel's base path as a URL prefix: "/x7Kq2p" or "" when the panel is at the root. Always without a trailing slash, so callers build addresses by appending.
type RatePoint ¶
type RatePoint struct {
T int64 `json:"t"` // unix seconds
In float64 `json:"in"` // bytes/s received over the tunnel
Out float64 `json:"out"` // bytes/s sent over the tunnel
}
RatePoint is one sparkline sample: bytes per second at a moment in time.
type SessionEntry ¶
type SessionEntry struct {
ID string `json:"id"`
IP string `json:"ip"`
Created string `json:"created"`
Current bool `json:"current"`
}
SessionEntry is one row of the Settings session list.
type SystemStats ¶
type SystemStats struct {
Hostname string `json:"hostname"`
OS string `json:"os"`
Uptime string `json:"uptime"`
IPv4 string `json:"ipv4"`
IPv6 string `json:"ipv6"`
// Where IPv4 was decided from — "interface" when the machine holds the
// address itself, "echo" when it had to be inferred from how this host
// appears to an outside service. Location and ISP are looked up from this
// address, so when it is the inferred one the panel says so rather than
// presenting a guess as a fact.
IPv4Source string `json:"ipv4Source"`
Location string `json:"location"`
ISP string `json:"isp"`
CPUPercent float64 `json:"cpuPercent"`
CPUCores int `json:"cpuCores"`
Load string `json:"load"`
MemUsed string `json:"memUsed"`
MemTotal string `json:"memTotal"`
MemPercent float64 `json:"memPercent"`
SwapUsed string `json:"swapUsed"`
SwapTotal string `json:"swapTotal"`
SwapPercent float64 `json:"swapPercent"`
DiskUsed string `json:"diskUsed"`
DiskTotal string `json:"diskTotal"`
DiskPercent float64 `json:"diskPercent"`
// Traffic carried by the tunnels — every tunnel's persisted counters added
// up, so the headline figure is the sum of what the cards show rather than a
// larger number nothing on the page accounts for. It used to be the machine's
// NIC counters, which also include ssh, apt and the panel itself, and which
// reset on reboot while the per-tunnel counters survive one.
TotalSent string `json:"totalSent"`
TotalRecv string `json:"totalRecv"`
TotalTraffic string `json:"totalTraffic"`
// Speed stays on the interface counters: it answers "what is this box doing
// right now", which is the question a live rate is read for, and a tunnel's
// own rate is already on its card.
UpSpeed string `json:"upSpeed"`
DownSpeed string `json:"downSpeed"`
// The same five measurements as plain numbers — bytes, and bytes per
// second.
//
// The strings above are formatted for a reader and are what the classic
// panel prints. Anything that has to compute rather than print needs the
// number: the new panel scales a column history against a peak, works out
// each tunnel's share of the total, and animates the headline figure up to
// its value. Number("873 B/s") is NaN, so every one of those silently
// became zero — a page reporting no traffic on a link that was carrying
// it. Sending both is the honest fix; parsing a formatted string back into
// a number in the browser is guesswork about units that will be wrong the
// first time the formatter changes.
UpBps float64 `json:"upBps"`
DownBps float64 `json:"downBps"`
TotalSentBytes uint64 `json:"totalSentBytes"`
TotalRecvBytes uint64 `json:"totalRecvBytes"`
TotalTrafficBytes uint64 `json:"totalTrafficBytes"`
TunnelsTotal int `json:"tunnelsTotal"`
TunnelsRunning int `json:"tunnelsRunning"`
// MonitorRunning reports the hashem-monitor service — the watchdog, the
// Telegram bot and the alerts live there, not in this panel. When it is
// down, dropped tunnels are not restarted and no alert fires, and nothing
// else visibly breaks — which is exactly why the panel must say so.
MonitorRunning bool `json:"monitorRunning"`
// Version is what is running here, so the update notice can say what it is
// asking the operator to move away from rather than only where to.
Version string `json:"version,omitempty"`
// UpdateTag is the newer release the cached background check knows about,
// empty when this version is current. Same source as the CLI's notice and
// the Telegram announcement, so the three can never disagree.
UpdateTag string `json:"updateTag,omitempty"`
// The built-in proxy, when the operator has turned it on. It is off by
// default, so all of this stays empty and the panel shows nothing.
//
// ProxyEnabled and ProxyRunning are deliberately separate. The proxy is a
// service of its own, and a tunnel can be forwarding a port to it while it
// is dead: the tunnel is up, the panel is green, and every connection
// through that port is refused at the far end. Only the two together say
// whether the thing actually answers.
ProxyEnabled bool `json:"proxyEnabled,omitempty"`
ProxyRunning bool `json:"proxyRunning,omitempty"`
ProxyType string `json:"proxyType,omitempty"`
ProxyPort int `json:"proxyPort,omitempty"`
// Congestion is the TCP congestion control the tunnel's own sockets run
// under; CongestionWanted is what they ask for. They differ when the kernel
// does not have the requested algorithm, and the request is silently
// dropped by design — the connection still works, just not as fast on a
// long lossy path, and the presets were tuned expecting it to be there.
// Empty means the question has no answer here (not Linux), so the panel
// says nothing rather than guessing.
Congestion string `json:"congestion,omitempty"`
CongestionWanted string `json:"congestionWanted,omitempty"`
}
SystemStats is the payload for /api/stats.
func GatherSystem ¶
func GatherSystem() SystemStats
GatherSystem collects the current system statistics.
type TunnelInfo ¶
type TunnelInfo struct {
Kind string `json:"kind"`
Name string `json:"name"`
Role string `json:"role"`
// Transport is the raw value the rest of the panel keys off — "tcp",
// "l3/pck", "direct/wss". Kept exactly as it was, because the edit form and
// several capability checks compare against it.
Transport string `json:"transport"`
// Direction and Carrier are the same thing split for the card, which has
// two badges rather than one: whether the tunnel is dialled from Iran or
// from kharej, and what carries it.
//
// Split here rather than in the browser because the browser would have to
// know which prefixes mean what — and would then be a second place that
// has to learn about every new tunnel kind, and the place nobody remembers
// to update. "l3/pck" on a card was the symptom: an internal name, leaking
// out because there was one field where there are two facts.
Direction string `json:"direction"`
Carrier string `json:"carrier"`
Addr string `json:"addr"`
Ports string `json:"ports"`
State string `json:"state"`
Ping int `json:"ping"` // milliseconds, -1 = n/a
PeerLocation string `json:"peerLocation"`
PeerISP string `json:"peerISP"`
BotRelay bool `json:"botRelay"` // has a hidden port used for the Telegram relay
// BotRelayPort is the loopback port that relay listens on. It is shown on
// its own, under its own name, rather than as the raw mapping: the mapping
// reads like something the operator set up and can therefore tidy away,
// and removing it stops the bot for a reason that looks unconnected.
BotRelayPort int `json:"botRelayPort,omitempty"`
Country string `json:"country"` // user-chosen ISO country code (label)
// PeerCountry is the ISO code detected from the peer's address, used for
// the flag. It is separate from Country so a label the user set by hand is
// never silently overwritten by a lookup.
PeerCountry string `json:"peerCountry"`
// TunnelPort is the port clients dial, pulled out of the bind address
// because ":1231" is what matters and "0.0.0.0:1231" is noise.
TunnelPort string `json:"tunnelPort"`
// ServiceDown says the tunnel is up and delivering into nothing: the
// service it forwards to, on the machine at the other end, is refusing
// every connection.
//
// State stays "online", because the tunnel is. This is the sentence beside
// it that says why nothing works anyway — the reading an operator used to
// have to go and find in the far machine's journal.
ServiceDown string `json:"serviceDown,omitempty"`
// Node is the managed server holding this tunnel's other end, when this
// panel built both. Empty for a tunnel whose far end was set up by hand,
// which is a real and ordinary thing to have.
//
// The panel needs it to know there is a second side it can act on at all —
// a log to read there, a speed test that can start a receiver — without
// asking the fleet about every tunnel on every poll.
Node string `json:"node,omitempty"`
PeerName string `json:"peerName,omitempty"`
// From the tunnel's metrics snapshot (empty when none has been written yet).
Uptime string `json:"uptime,omitempty"`
BytesIn string `json:"bytesIn,omitempty"`
BytesOut string `json:"bytesOut,omitempty"`
// InBytes/OutBytes/TotalBytes are the same three as numbers, for the same
// reason as the system totals above: the panel sorts tunnels by what they
// have carried and draws each one's share of the busiest, and neither is
// possible with "200.0 MiB".
InBytes uint64 `json:"inBytes,omitempty"`
OutBytes uint64 `json:"outBytes,omitempty"`
TotalBytes uint64 `json:"totalBytes,omitempty"`
// BytesTotal is the two added. The card shows all three on one line, and a
// sum of two already-formatted strings is not something the browser can do.
BytesTotal string `json:"bytesTotal,omitempty"`
/* TrafficMeasured says whether the figures here are a reading or a
silence.
*
* A measured zero is a fact - the tunnel is up and has carried nothing yet
* - and no figures at all is a different fact, and the two are the same
* JSON: omitempty drops a zero. The strings above do distinguish them
* ("0 B" is present, absence is not), but only a reader that knows to look
* can tell, so the reading is stated rather than implied. Absent whenever
* nothing could be measured. */
TrafficMeasured bool `json:"trafficMeasured,omitempty"`
// KCP link-quality counters; nil on every other transport.
KCP *metrics.KCPStats `json:"kcp,omitempty"`
// KCPLossPercent is derived from the counters above: how much of the sent
// traffic needed resending — the honest answer to "is this link lossy?".
KCPLossPercent float64 `json:"kcpLossPercent,omitempty"`
// Pool is the client's connection pool; nil on a server tunnel and on the
// transports that do not keep one.
Pool *metrics.PoolStats `json:"pool,omitempty"`
// From the tunnel's own config.
Preset string `json:"preset,omitempty"` // display label: Balance / Turbo / Aggressive / Custom
MaxConnections int `json:"maxConnections,omitempty"` // 0 = unlimited
BandwidthMbps int `json:"bandwidthMbps,omitempty"` // 0 = unlimited
ProxyProtocol bool `json:"proxyProtocol,omitempty"`
LoadBalance bool `json:"loadBalance,omitempty"`
FallbackAddrs []string `json:"fallbackAddrs,omitempty"`
// CertType is "letsencrypt" or "self-signed", only for wss/wssmux servers.
CertDomain string `json:"certDomain,omitempty"`
CertType string `json:"certType,omitempty"`
// CertExpiry is the NotAfter date of the certificate on disk, when it can
// be read. ACME certificates renew themselves, so no expiry is shown.
CertExpiry string `json:"certExpiry,omitempty"`
// Rates is the recent transfer speed of this tunnel, oldest first, for the
// dashboard's sparkline. Derived from successive metrics snapshots.
Rates []RatePoint `json:"rates,omitempty"`
}
TunnelInfo is one row for /api/tunnels.
Source Files
¶
- config.go
- farservice.go
- handlers_adopt.go
- handlers_app.go
- handlers_cert.go
- handlers_confhist.go
- handlers_frp.go
- handlers_gre.go
- handlers_grefrp.go
- handlers_monitoring.go
- handlers_nodes.go
- handlers_restorepoints.go
- handlers_security.go
- handlers_settings.go
- handlers_speedtest.go
- handlers_stack.go
- handlers_stack_remote.go
- handlers_tunnels.go
- nodeprobe.go
- panel.go
- panelsecurity.go
- prometheus.go
- server.go
- stats.go