feat: initial commit
This commit is contained in:
1 parent
353f62e257
commit
c6852c6f1b
62 files changed
+5911
-2
No files matched your search
Vendored
BIN
Binary file not shown.
@@ -0,0 +1,64 @@
|
||||
// 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),
|
||||
}
|
||||
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.
|
||||
// TUNNEL_ADDR overrides it entirely; TUNNEL_PORT overrides just the port.
|
||||
// Set TUNNEL_ADDR=off to disable the direct 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"))
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
// Command server is the host: it serves the admin web panel and acts as the
|
||||
// rendezvous point that NAT'd agents dial into over HTTP long-polling.
|
||||
//
|
||||
// TLS is terminated by nginx in front of this process, so it listens on plain
|
||||
// HTTP on localhost by default (see deploy/nginx.conf.example).
|
||||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"log"
|
||||
"net/http"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/x-grk/longpolling-remote-access/internal/server"
|
||||
)
|
||||
|
||||
func main() {
|
||||
addr := flag.String("addr", envOr("LPRA_ADDR", "127.0.0.1:8091"), "listen address (behind nginx)")
|
||||
dbPath := flag.String("db", envOr("LPRA_DB", "lpra.db"), "path to SQLite database")
|
||||
distDir := flag.String("dist", envOr("LPRA_DIST", "dist"), "directory with agent binaries served at /download/")
|
||||
tunnelAddr := flag.String("tunnel", envOr("LPRA_TUNNEL_ADDR", ":8446"), "listen address for the direct TCP+UDP forward tunnel (empty disables)")
|
||||
insecureCookies := flag.Bool("insecure-cookies", envOr("LPRA_INSECURE_COOKIES", "") == "1",
|
||||
"do not mark cookies Secure (plain-HTTP local testing only)")
|
||||
flag.Parse()
|
||||
|
||||
db, err := server.OpenDB(*dbPath)
|
||||
if err != nil {
|
||||
log.Fatalf("open db: %v", err)
|
||||
}
|
||||
defer db.Close()
|
||||
|
||||
srv, err := server.New(server.Options{
|
||||
DB: db,
|
||||
DistDir: *distDir,
|
||||
SecureCookies: !*insecureCookies,
|
||||
TunnelAddr: *tunnelAddr,
|
||||
})
|
||||
if err != nil {
|
||||
log.Fatalf("init server: %v", err)
|
||||
}
|
||||
|
||||
httpSrv := &http.Server{
|
||||
Addr: *addr,
|
||||
Handler: srv.Handler(),
|
||||
// No ReadTimeout/WriteTimeout: long-poll connections are intentionally
|
||||
// long-lived. nginx bounds them instead.
|
||||
ReadHeaderTimeout: 15 * time.Second,
|
||||
IdleTimeout: 130 * time.Second,
|
||||
}
|
||||
log.Printf("lpra server listening on %s (db=%s dist=%s)", *addr, *dbPath, *distDir)
|
||||
if err := httpSrv.ListenAndServe(); err != nil {
|
||||
log.Fatalf("serve: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func envOr(key, def string) string {
|
||||
if v := os.Getenv(key); v != "" {
|
||||
return v
|
||||
}
|
||||
return def
|
||||
}
|
||||
Reference in new issue
Block a user