Some checks failed
CI Docker Mining Proof / Linux agent hashrate proof (push) Has been cancelled
181 lines
4.7 KiB
Go
181 lines
4.7 KiB
Go
package api
|
|
|
|
import (
|
|
"encoding/json"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
|
|
"crypto-miner-server/internal/atlas"
|
|
"crypto-miner-server/internal/db"
|
|
"crypto-miner-server/internal/models"
|
|
|
|
"github.com/gorilla/websocket"
|
|
)
|
|
|
|
func connectTestAgentWithIP(t *testing.T, hub *WSHub, agentID, clientIP string) *websocket.Conn {
|
|
t.Helper()
|
|
srv := httptest.NewServer(http.HandlerFunc(hub.HandleAgentWS))
|
|
t.Cleanup(srv.Close)
|
|
|
|
wsURL := "ws" + strings.TrimPrefix(srv.URL, "http")
|
|
hdr := http.Header{"X-Forwarded-For": {clientIP}}
|
|
conn, _, err := websocket.DefaultDialer.Dial(wsURL, hdr)
|
|
if err != nil {
|
|
t.Fatalf("dial agent ws: %v", err)
|
|
}
|
|
t.Cleanup(func() { _ = conn.Close() })
|
|
|
|
authAgentConn(t, conn, map[string]interface{}{
|
|
"agent_id": agentID,
|
|
"hostname": "test-host",
|
|
"platform": "windows",
|
|
"version": "1.0",
|
|
})
|
|
|
|
deadline := time.Now().Add(2 * time.Second)
|
|
for time.Now().Before(deadline) {
|
|
if hub.isAgentConnected(agentID) {
|
|
return conn
|
|
}
|
|
time.Sleep(10 * time.Millisecond)
|
|
}
|
|
t.Fatal("agent not connected after auth")
|
|
return nil
|
|
}
|
|
|
|
func TestAuthResponseAtlasLanGossipPolicy(t *testing.T) {
|
|
database, err := db.New(t.TempDir())
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
t.Cleanup(func() { _ = database.Close() })
|
|
hub := NewWSHub(database)
|
|
hub.SetServerPolicy(ServerPolicy{AtlasLanGossipEnabled: true})
|
|
|
|
conn, _ := dialAgentWS(t, hub)
|
|
resp := authAgentConn(t, conn, map[string]interface{}{
|
|
"agent_id": "gossip-policy-agent",
|
|
"hostname": "host",
|
|
"platform": "windows",
|
|
"version": "test",
|
|
})
|
|
var body map[string]interface{}
|
|
if err := json.Unmarshal(resp.Payload, &body); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
enabled, ok := body["atlas_lan_gossip_enabled"].(bool)
|
|
if !ok || !enabled {
|
|
t.Fatalf("atlas_lan_gossip_enabled = %#v", body["atlas_lan_gossip_enabled"])
|
|
}
|
|
}
|
|
|
|
func TestAtlasGossipRelaySameSubnet(t *testing.T) {
|
|
database, err := db.New(t.TempDir())
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
t.Cleanup(func() { _ = database.Close() })
|
|
hub := NewWSHub(database)
|
|
hub.SetServerPolicy(ServerPolicy{AtlasLanGossipEnabled: true})
|
|
|
|
for _, spec := range []struct {
|
|
id string
|
|
ip string
|
|
}{
|
|
{"gossip-a", "192.168.50.10"},
|
|
{"gossip-b", "192.168.50.20"},
|
|
{"gossip-other-subnet", "192.168.51.10"},
|
|
} {
|
|
if err := database.UpsertAgent(&models.Agent{
|
|
ID: spec.id, Name: spec.id, Platform: "windows", Status: "online",
|
|
IP: spec.ip, LastSeen: time.Now(),
|
|
}); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
|
|
connA := connectTestAgentWithIP(t, hub, "gossip-a", "192.168.50.10")
|
|
connB := connectTestAgentWithIP(t, hub, "gossip-b", "192.168.50.20")
|
|
connC := connectTestAgentWithIP(t, hub, "gossip-other-subnet", "192.168.51.10")
|
|
|
|
recvCh := make(chan Message, 2)
|
|
go readUntilType(connB, "atlas_gossip", recvCh)
|
|
go readUntilType(connC, "atlas_gossip", recvCh)
|
|
|
|
payload, _ := json.Marshal(map[string]interface{}{
|
|
"hints": []atlas.GossipHint{
|
|
{Tier: "docker", Condition: "no_docker", Reason: "pull failed"},
|
|
},
|
|
})
|
|
if err := connA.WriteJSON(Message{Type: "atlas_gossip", Payload: payload}); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
select {
|
|
case msg := <-recvCh:
|
|
var body struct {
|
|
Hints []atlas.AtlasSkip `json:"hints"`
|
|
}
|
|
if err := json.Unmarshal(msg.Payload, &body); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if len(body.Hints) != 1 || body.Hints[0].Tier != "docker" {
|
|
t.Fatalf("unexpected relay hints: %+v", body.Hints)
|
|
}
|
|
case <-time.After(2 * time.Second):
|
|
t.Fatal("sibling on same /24 did not receive atlas_gossip")
|
|
}
|
|
|
|
select {
|
|
case <-recvCh:
|
|
t.Fatal("agent on different /24 should not receive atlas_gossip")
|
|
case <-time.After(300 * time.Millisecond):
|
|
}
|
|
}
|
|
|
|
func TestAtlasGossipDisabledNoRelay(t *testing.T) {
|
|
database, err := db.New(t.TempDir())
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
t.Cleanup(func() { _ = database.Close() })
|
|
hub := NewWSHub(database)
|
|
hub.SetServerPolicy(ServerPolicy{AtlasLanGossipEnabled: false})
|
|
|
|
connA := connectTestAgentWithIP(t, hub, "gossip-off-a", "10.10.0.1")
|
|
connB := connectTestAgentWithIP(t, hub, "gossip-off-b", "10.10.0.2")
|
|
|
|
recvCh := make(chan Message, 1)
|
|
go readUntilType(connB, "atlas_gossip", recvCh)
|
|
|
|
payload, _ := json.Marshal(map[string]interface{}{
|
|
"hints": []atlas.GossipHint{{Tier: "wsl", Condition: "defender_on"}},
|
|
})
|
|
if err := connA.WriteJSON(Message{Type: "atlas_gossip", Payload: payload}); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
select {
|
|
case msg := <-recvCh:
|
|
t.Fatalf("unexpected relay when disabled: %+v", msg)
|
|
case <-time.After(400 * time.Millisecond):
|
|
}
|
|
}
|
|
|
|
func readUntilType(conn *websocket.Conn, wantType string, out chan<- Message) {
|
|
deadline := time.Now().Add(3 * time.Second)
|
|
for time.Now().Before(deadline) {
|
|
var msg Message
|
|
if err := conn.ReadJSON(&msg); err != nil {
|
|
return
|
|
}
|
|
if msg.Type == wantType {
|
|
out <- msg
|
|
return
|
|
}
|
|
}
|
|
}
|