PortRelay:把内网服务临时、可控地开放给同事
边缘网关 + Agent 的端口中继:Agent 主动连出到网关,把内网目标挂到网关端口上; 通过邀请链接按设备授权,可选审批,首次连接起计时,到期、闲置自动收回。 - portrelay-server:边缘网关(端口池、白名单与授权租约、邀请与授权网页) - portrelay-agent:Agent(yamux 隧道、本地 API 与网页面板、流量统计) - mac-native:SwiftUI 菜单栏 App
This commit is contained in:
commit
86e159b3f0
67 files changed
+13888
No files matched your search
@@ -0,0 +1,125 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"flag"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"net"
|
||||
"os"
|
||||
"os/signal"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"github.com/DeepFal/PortRelay/server"
|
||||
)
|
||||
|
||||
type peekedConn struct {
|
||||
net.Conn
|
||||
r io.Reader
|
||||
}
|
||||
|
||||
func (c *peekedConn) Read(b []byte) (int, error) {
|
||||
return c.r.Read(b)
|
||||
}
|
||||
|
||||
// version 由发布构建通过 -ldflags "-X main.version=..." 注入
|
||||
var version = "dev"
|
||||
|
||||
func envOr(key, def string) string {
|
||||
if v := os.Getenv(key); v != "" {
|
||||
return v
|
||||
}
|
||||
return def
|
||||
}
|
||||
|
||||
func main() {
|
||||
listenAddr := flag.String("listen", ":33000", "Control/relay listen address")
|
||||
portMin := flag.Int("port-min", 33000, "Minimum tunnel port")
|
||||
portMax := flag.Int("port-max", 34000, "Maximum tunnel port")
|
||||
token := flag.String("token", envOr("PORTRELAY_TOKEN", ""), "Shared secret the agent must present (or env PORTRELAY_TOKEN); required")
|
||||
publicHost := flag.String("public-host", "", "Host shown on the web auth page (default: the host the visitor used to open the link)")
|
||||
stateFile := flag.String("state", "/var/lib/portrelay/leases.json", "File that keeps temporary (web auth) leases across restarts; empty disables")
|
||||
trustProxy := flag.Bool("trust-proxy", false, "Trust X-Forwarded-For/X-Real-IP on /auth (only behind a trusted reverse proxy)")
|
||||
showVersion := flag.Bool("version", false, "Print version and exit")
|
||||
flag.Parse()
|
||||
|
||||
if *showVersion {
|
||||
fmt.Println("portrelay-server", version)
|
||||
return
|
||||
}
|
||||
|
||||
if *token == "" {
|
||||
log.Fatalf("Fatal: no token configured; set -token or PORTRELAY_TOKEN (e.g. openssl rand -hex 24)")
|
||||
}
|
||||
if len(*token) < 16 {
|
||||
log.Printf("WARNING: token is shorter than 16 characters; use a long random value")
|
||||
}
|
||||
|
||||
log.Printf("==================================================")
|
||||
log.Printf(" PortRelay Relay Server %s", version)
|
||||
log.Printf(" Control/Web Port: %s", *listenAddr)
|
||||
log.Printf(" Allowed Range: %d - %d", *portMin, *portMax)
|
||||
log.Printf("==================================================")
|
||||
|
||||
mgr := server.NewManager(*portMin, *portMax, *token)
|
||||
mgr.PublicHost = *publicHost
|
||||
mgr.TrustProxyHeaders = *trustProxy
|
||||
if *stateFile != "" {
|
||||
if err := mgr.EnableLeasePersistence(*stateFile); err != nil {
|
||||
log.Printf("WARNING: lease persistence disabled: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
ln, err := net.Listen("tcp", *listenAddr)
|
||||
if err != nil {
|
||||
log.Fatalf("Fatal: failed to listen on %s: %v", *listenAddr, err)
|
||||
}
|
||||
defer ln.Close()
|
||||
|
||||
// Graceful shutdown
|
||||
sigChan := make(chan os.Signal, 1)
|
||||
signal.Notify(sigChan, syscall.SIGINT, syscall.SIGTERM)
|
||||
go func() {
|
||||
<-sigChan
|
||||
log.Printf("\nShutting down PortRelay Server...")
|
||||
_ = ln.Close()
|
||||
os.Exit(0)
|
||||
}()
|
||||
|
||||
for {
|
||||
conn, err := ln.Accept()
|
||||
if err != nil {
|
||||
select {
|
||||
case <-sigChan:
|
||||
return
|
||||
default:
|
||||
log.Printf("Accept error: %v", err)
|
||||
time.Sleep(50 * time.Millisecond)
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
go func(c net.Conn) {
|
||||
// 不发数据的空连接最多占用 10 秒
|
||||
_ = c.SetReadDeadline(time.Now().Add(10 * time.Second))
|
||||
br := bufio.NewReader(c)
|
||||
peek, err := br.Peek(3)
|
||||
if err != nil {
|
||||
_ = c.Close()
|
||||
return
|
||||
}
|
||||
_ = c.SetReadDeadline(time.Time{})
|
||||
if string(peek) == "GET" || string(peek) == "POS" || string(peek) == "HEA" {
|
||||
mgr.HandleHTTP(&peekedConn{Conn: c, r: br})
|
||||
return
|
||||
}
|
||||
|
||||
log.Printf("Incoming relay agent connection from %s", c.RemoteAddr())
|
||||
if err := mgr.HandleSession(&peekedConn{Conn: c, r: br}); err != nil {
|
||||
log.Printf("Session closed for %s: %v", c.RemoteAddr(), err)
|
||||
}
|
||||
}(conn)
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user