package main import ( "log" "os" "os/signal" "strconv" "syscall" "github.com/nixc/reverse-ssh-traefik/internal/server" ) func envOr(key, fallback string) string { if v := os.Getenv(key); v != "" { return v } return fallback } func envRequired(key string) string { v := os.Getenv(key) if v == "" { log.Fatalf("Required environment variable %s is not set", key) } return v } func envInt(key string, fallback int) int { v := os.Getenv(key) if v == "" { return fallback } n, err := strconv.Atoi(v) if err != nil { log.Printf("WARN: invalid %s=%q, using default %d", key, v, fallback) return fallback } return n } func main() { log.SetFlags(log.LstdFlags | log.Lshortfile) log.Println("tunnel-server starting") // SSH server config (for accepting tunnel clients). sshPort := envOr("SSH_PORT", "2222") hostKeyPath := envOr("SSH_HOST_KEY", "/keys/host_key") authKeysPath := envOr("AUTHORIZED_KEYS", "/keys/authorized_keys") portStart := envInt("PORT_RANGE_START", 10000) portEnd := envInt("PORT_RANGE_END", 10100) // Swarm manager SSH config (for updating service labels). traefikHost := envRequired("TRAEFIK_SSH_HOST") traefikUser := envOr("TRAEFIK_SSH_USER", "root") traefikKey := envRequired("TRAEFIK_SSH_KEY") serviceName := envOr("SWARM_SERVICE_NAME", "better-argo-tunnels_tunnel-server") entrypoint := envOr("TRAEFIK_ENTRYPOINT", "websecure") certResolver := envOr("TRAEFIK_CERT_RESOLVER", "letsencryptresolver") // Load the SSH key for connecting to the Swarm manager. traefikSigner, err := server.LoadSigner(traefikKey) if err != nil { log.Fatalf("Failed to load Traefik SSH key: %v", err) } log.Printf("Loaded Swarm manager SSH key") // Initialize port pool. pool := server.NewPortPool(portStart, portEnd) log.Printf("Port pool: %d-%d (%d ports)", portStart, portEnd, portEnd-portStart+1) // Initialize label manager (Swarm service update via SSH). labels, err := server.NewLabelManager( traefikHost, traefikUser, traefikSigner, serviceName, entrypoint, certResolver, ) if err != nil { log.Fatalf("Failed to init label manager: %v", err) } defer labels.Close() // Initialize SSH server for tunnel clients. sshSrv, err := server.NewSSHServer(hostKeyPath, authKeysPath, pool, labels) if err != nil { log.Fatalf("Failed to init SSH server: %v", err) } // Handle graceful shutdown. sigCh := make(chan os.Signal, 1) signal.Notify(sigCh, syscall.SIGINT, syscall.SIGTERM) go func() { sig := <-sigCh log.Printf("Received signal %s, shutting down", sig) os.Exit(0) }() // Start SSH server. addr := "0.0.0.0:" + sshPort if err := sshSrv.ListenAndServe(addr); err != nil { log.Fatalf("SSH server error: %v", err) } }