The direct tunnel port (8446 by default) and the host agents dial for it can be set in Settings. The server moves its listeners at once and agents pick the address up from the panel: on connect and with every poll, unless TUNNEL_ADDR / TUNNEL_PORT pin it locally. ufw: run with a C locale (the output is translated), do not adopt rules that existed before the panel, skip loopback-only listeners, re-open what saved forwards need on start and when the integration is turned on, move the shared tunnel rule with the port and drop it when no direct forward is left. The last ufw error is shown in Settings. The forwards page shows the real tunnel and panel ports.
68 lines
1.9 KiB
Go
68 lines
1.9 KiB
Go
// Command agent is the Linux client installed on machines without a public IP.
|
|
// It runs as a root systemd service, dials the server over HTTP long-polling,
|
|
// and opens a root PTY when an admin requests a terminal in the web panel.
|
|
package main
|
|
|
|
import (
|
|
"context"
|
|
"log"
|
|
"net"
|
|
"net/url"
|
|
"os"
|
|
"os/signal"
|
|
"syscall"
|
|
|
|
"github.com/x-grk/longpolling-remote-access/internal/agent"
|
|
)
|
|
|
|
func main() {
|
|
host := envOr("HOST", "https://REDACTED:8445")
|
|
cfg := agent.Config{
|
|
Host: host,
|
|
EnrollSecret: os.Getenv("ENROLL_SECRET"),
|
|
Name: os.Getenv("AGENT_NAME"),
|
|
StatePath: envOr("STATE_PATH", "/etc/lpagent/state.json"),
|
|
TunnelAddr: tunnelAddr(host),
|
|
// A locally set address wins over the one from the panel settings.
|
|
TunnelPinned: os.Getenv("TUNNEL_ADDR") != "" || os.Getenv("TUNNEL_PORT") != "",
|
|
}
|
|
if cfg.EnrollSecret == "" {
|
|
// Only required for the very first run (before enrollment).
|
|
if _, err := os.Stat(cfg.StatePath); os.IsNotExist(err) {
|
|
log.Fatal("ENROLL_SECRET is required for first enrollment")
|
|
}
|
|
}
|
|
|
|
ctx, stop := signal.NotifyContext(context.Background(), syscall.SIGINT, syscall.SIGTERM)
|
|
defer stop()
|
|
|
|
if err := agent.New(cfg).Run(ctx); err != nil {
|
|
log.Fatalf("agent: %v", err)
|
|
}
|
|
}
|
|
|
|
func envOr(key, def string) string {
|
|
if v := os.Getenv(key); v != "" {
|
|
return v
|
|
}
|
|
return def
|
|
}
|
|
|
|
// tunnelAddr derives the direct-tunnel address (host:8446) from the server URL.
|
|
// It is only the starting point: the agent follows the host and port set in
|
|
// the panel settings, unless TUNNEL_ADDR (the whole address) or TUNNEL_PORT
|
|
// (just the port) pins it here. Set TUNNEL_ADDR=off to disable the tunnel.
|
|
func tunnelAddr(hostURL string) string {
|
|
if v := os.Getenv("TUNNEL_ADDR"); v != "" {
|
|
if v == "off" {
|
|
return ""
|
|
}
|
|
return v
|
|
}
|
|
u, err := url.Parse(hostURL)
|
|
if err != nil || u.Hostname() == "" {
|
|
return ""
|
|
}
|
|
return net.JoinHostPort(u.Hostname(), envOr("TUNNEL_PORT", "8446"))
|
|
}
|