77 lines
1.7 KiB
Go
77 lines
1.7 KiB
Go
package main
|
|
|
|
import (
|
|
"log"
|
|
"os"
|
|
"strconv"
|
|
"time"
|
|
|
|
"github.com/nixc/reverse-ssh-traefik/internal/client"
|
|
)
|
|
|
|
func envRequired(key string) string {
|
|
v := os.Getenv(key)
|
|
if v == "" {
|
|
log.Fatalf("Required environment variable %s is not set", key)
|
|
}
|
|
return v
|
|
}
|
|
|
|
func envOr(key, fallback string) string {
|
|
if v := os.Getenv(key); v != "" {
|
|
return v
|
|
}
|
|
return fallback
|
|
}
|
|
|
|
func main() {
|
|
log.SetFlags(log.LstdFlags | log.Lshortfile)
|
|
log.Println("tunnel-client starting")
|
|
|
|
serverAddr := envRequired("TUNNEL_SERVER")
|
|
domain := envRequired("TUNNEL_DOMAIN")
|
|
keyPath := envOr("TUNNEL_KEY", "/keys/id_ed25519")
|
|
|
|
localPortStr := envOr("TUNNEL_PORT", "8080")
|
|
localPort, err := strconv.Atoi(localPortStr)
|
|
if err != nil {
|
|
log.Fatalf("Invalid TUNNEL_PORT=%q: %v", localPortStr, err)
|
|
}
|
|
|
|
// Load the private key.
|
|
signer, err := client.LoadPrivateKey(keyPath)
|
|
if err != nil {
|
|
log.Fatalf("Failed to load private key: %v", err)
|
|
}
|
|
log.Printf("Loaded key from %s", keyPath)
|
|
|
|
// Reconnect loop.
|
|
backoff := time.Second
|
|
maxBackoff := 30 * time.Second
|
|
|
|
for {
|
|
log.Printf("Connecting to %s (domain=%s, local_port=%d)", serverAddr, domain, localPort)
|
|
|
|
sshClient, err := client.Connect(serverAddr, signer)
|
|
if err != nil {
|
|
log.Printf("Connection failed: %v (retry in %s)", err, backoff)
|
|
time.Sleep(backoff)
|
|
backoff = min(backoff*2, maxBackoff)
|
|
continue
|
|
}
|
|
|
|
// Reset backoff on successful connection.
|
|
backoff = time.Second
|
|
log.Printf("Connected to %s", serverAddr)
|
|
|
|
// Set up the reverse tunnel (blocks until disconnected).
|
|
if err := client.SetupTunnel(sshClient, domain, localPort); err != nil {
|
|
log.Printf("Tunnel error: %v (reconnecting in %s)", err, backoff)
|
|
}
|
|
|
|
sshClient.Close()
|
|
time.Sleep(backoff)
|
|
backoff = min(backoff*2, maxBackoff)
|
|
}
|
|
}
|