852171cc59
- Ajout d'un conteneur Tailscale côté serveur pour joindre les agents via IPs Tailscale - Configuration Headscale exposé en HTTPS via Caddy (headscale.alfrednobel.edudeploy.com) - Caddy configuré pour les sous-domaines avec TLS on-demand - Middleware et route proxy Next.js pour router les sous-domaines vers les agents - Ajout du champ domain sur Establishment et affichage de l'URL publique dans le dashboard - Agent Windows v0.2.3 avec proxy Tailscale par instance pour contourner Docker Desktop - Templates WordPress/PrestaShop bindés sur 0.0.0.0 pour être accessibles via Tailscale
106 lines
2.2 KiB
Go
106 lines
2.2 KiB
Go
package main
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"log"
|
|
"net"
|
|
"os"
|
|
"io"
|
|
"time"
|
|
|
|
"tailscale.com/tsnet"
|
|
)
|
|
|
|
var globalTSServer *tsnet.Server
|
|
|
|
func startTailscale(dataDir, nodeID, headscaleURL, authKey string) (string, error) {
|
|
// Configure tsnet to use our Headscale server
|
|
os.Setenv("TS_AUTHKEY", authKey)
|
|
os.Setenv("TS_CONTROL_URL", headscaleURL)
|
|
|
|
s := &tsnet.Server{
|
|
Hostname: nodeID,
|
|
Dir: dataDir,
|
|
Logf: log.Printf,
|
|
}
|
|
|
|
if err := s.Start(); err != nil {
|
|
return "", fmt.Errorf("tailscale start: %w", err)
|
|
}
|
|
|
|
globalTSServer = s
|
|
|
|
// Wait for Tailscale to come up and retrieve IP
|
|
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
|
|
defer cancel()
|
|
|
|
lc, err := s.LocalClient()
|
|
if err != nil {
|
|
return "", fmt.Errorf("tailscale local client: %w", err)
|
|
}
|
|
|
|
var tailscaleIP string
|
|
for {
|
|
status, err := lc.Status(ctx)
|
|
if err != nil {
|
|
return "", fmt.Errorf("tailscale status: %w", err)
|
|
}
|
|
if status.Self != nil && len(status.Self.TailscaleIPs) > 0 {
|
|
tailscaleIP = status.Self.TailscaleIPs[0].String()
|
|
break
|
|
}
|
|
select {
|
|
case <-ctx.Done():
|
|
return "", fmt.Errorf("tailscale IP timeout")
|
|
case <-time.After(1 * time.Second):
|
|
}
|
|
}
|
|
|
|
log.Printf("Tailscale started with IP: %s", tailscaleIP)
|
|
return tailscaleIP, nil
|
|
}
|
|
|
|
func startTailscaleProxy(port int) (net.Listener, error) {
|
|
if globalTSServer == nil {
|
|
return nil, fmt.Errorf("tailscale server not started")
|
|
}
|
|
ln, err := globalTSServer.Listen("tcp", fmt.Sprintf(":%d", port))
|
|
if err != nil {
|
|
return nil, fmt.Errorf("tailscale listen on port %d: %w", port, err)
|
|
}
|
|
go func() {
|
|
for {
|
|
conn, err := ln.Accept()
|
|
if err != nil {
|
|
log.Printf("tailscale proxy accept error on port %d: %v", port, err)
|
|
return
|
|
}
|
|
go handleProxyConn(conn, port)
|
|
}
|
|
}()
|
|
log.Printf("Tailscale proxy started on port %d", port)
|
|
return ln, nil
|
|
}
|
|
|
|
func handleProxyConn(src net.Conn, port int) {
|
|
defer src.Close()
|
|
dst, err := net.Dial("tcp", fmt.Sprintf("localhost:%d", port))
|
|
if err != nil {
|
|
log.Printf("tailscale proxy dial localhost:%d error: %v", port, err)
|
|
return
|
|
}
|
|
defer dst.Close()
|
|
|
|
done := make(chan struct{}, 2)
|
|
go func() {
|
|
_, _ = io.Copy(dst, src)
|
|
done <- struct{}{}
|
|
}()
|
|
go func() {
|
|
_, _ = io.Copy(src, dst)
|
|
done <- struct{}{}
|
|
}()
|
|
<-done
|
|
}
|