162 lines
4.0 KiB
Go
162 lines
4.0 KiB
Go
package server
|
|
|
|
import (
|
|
"crypto/rand"
|
|
"encoding/hex"
|
|
"strconv"
|
|
|
|
lpra "github.com/x-grk/longpolling-remote-access"
|
|
)
|
|
|
|
// Config keys stored in the config table.
|
|
const (
|
|
cfgHostAddress = "host_address"
|
|
cfgEnrollSecret = "enroll_secret"
|
|
cfgSetupDone = "setup_done"
|
|
cfgInstallCode = "install_code"
|
|
cfgInstallLen = "install_code_len"
|
|
cfgUfwEnabled = "ufw_enabled"
|
|
cfgTunnelHost = "tunnel_host"
|
|
cfgTunnelPort = "tunnel_port"
|
|
)
|
|
|
|
// DefaultHostAddress is the public URL agents dial by default. It is baked into
|
|
// the rendered install.sh and can be changed from the settings page.
|
|
const DefaultHostAddress = "https://REDACTED:8445"
|
|
|
|
// Version is the server build version (see VERSION.txt). It is the newest
|
|
// version agents update to.
|
|
var Version = lpra.Version
|
|
|
|
// ensureDefaults seeds the host address and a random enroll secret on first boot.
|
|
func (db *DB) ensureDefaults() error {
|
|
host, err := db.GetConfig(cfgHostAddress)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if host == "" {
|
|
if err := db.SetConfig(cfgHostAddress, DefaultHostAddress); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
secret, err := db.GetConfig(cfgEnrollSecret)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if secret == "" {
|
|
if err := db.SetConfig(cfgEnrollSecret, randToken()); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
code, err := db.GetConfig(cfgInstallCode)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if code == "" {
|
|
if err := db.SetConfig(cfgInstallCode, shortCode(db.InstallCodeLen())); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (db *DB) HostAddress() string {
|
|
v, _ := db.GetConfig(cfgHostAddress)
|
|
if v == "" {
|
|
return DefaultHostAddress
|
|
}
|
|
return v
|
|
}
|
|
|
|
func (db *DB) EnrollSecret() string {
|
|
v, _ := db.GetConfig(cfgEnrollSecret)
|
|
return v
|
|
}
|
|
|
|
// InstallCode returns the short code used for the /i/<code> install one-liner.
|
|
func (db *DB) InstallCode() string {
|
|
v, _ := db.GetConfig(cfgInstallCode)
|
|
return v
|
|
}
|
|
|
|
// UfwEnabled reports whether panel-driven ufw integration is on (default yes).
|
|
func (db *DB) UfwEnabled() bool {
|
|
v, _ := db.GetConfig(cfgUfwEnabled)
|
|
return v != "0"
|
|
}
|
|
|
|
func (db *DB) SetUfwEnabled(on bool) error {
|
|
if on {
|
|
return db.SetConfig(cfgUfwEnabled, "1")
|
|
}
|
|
return db.SetConfig(cfgUfwEnabled, "0")
|
|
}
|
|
|
|
// TunnelHost is the host agents dial for the direct tunnel. Empty means the
|
|
// host of the URL they already reach the panel on.
|
|
func (db *DB) TunnelHost() string {
|
|
v, _ := db.GetConfig(cfgTunnelHost)
|
|
return v
|
|
}
|
|
|
|
// TunnelPort is the direct-tunnel port set in Settings, or 0 when it is left
|
|
// at the port of the server's -tunnel address.
|
|
func (db *DB) TunnelPort() int {
|
|
v, _ := db.GetConfig(cfgTunnelPort)
|
|
n, _ := strconv.Atoi(v)
|
|
if n < 1 || n > 65535 {
|
|
return 0
|
|
}
|
|
return n
|
|
}
|
|
|
|
func (db *DB) SetupDone() bool {
|
|
v, _ := db.GetConfig(cfgSetupDone)
|
|
return v == "1"
|
|
}
|
|
|
|
func (db *DB) MarkSetupDone() error { return db.SetConfig(cfgSetupDone, "1") }
|
|
|
|
// randToken returns 32 bytes of cryptographic randomness as hex (64 chars).
|
|
func randToken() string {
|
|
b := make([]byte, 32)
|
|
if _, err := rand.Read(b); err != nil {
|
|
panic("crypto/rand failed: " + err.Error())
|
|
}
|
|
return hex.EncodeToString(b)
|
|
}
|
|
|
|
// Install code length: 3 by default, up to 24.
|
|
const (
|
|
installCodeMin = 3
|
|
installCodeMax = 24
|
|
installCodeDef = 3
|
|
)
|
|
|
|
// InstallCodeLen is how many characters a new install code has.
|
|
func (db *DB) InstallCodeLen() int {
|
|
v, _ := db.GetConfig(cfgInstallLen)
|
|
if n, err := strconv.Atoi(v); err == nil && n >= installCodeMin && n <= installCodeMax {
|
|
return n
|
|
}
|
|
return installCodeDef
|
|
}
|
|
|
|
// shortCode returns a random code of n English letters and digits for the
|
|
// /i/<code> install link. Every character is equally likely.
|
|
func shortCode(n int) string {
|
|
const alphabet = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
|
|
out := make([]byte, 0, n)
|
|
b := make([]byte, 1)
|
|
for len(out) < n {
|
|
if _, err := rand.Read(b); err != nil {
|
|
panic("crypto/rand failed: " + err.Error())
|
|
}
|
|
// 248 = 4 * 62: drop the rest, so no character comes up more often.
|
|
if b[0] < 248 {
|
|
out = append(out, alphabet[int(b[0])%len(alphabet)])
|
|
}
|
|
}
|
|
return string(out)
|
|
}
|