Files
kattila.status/agent/security/security.go
2026-04-17 19:23:04 +03:00

101 lines
1.6 KiB
Go

package security
import (
"crypto/hmac"
"crypto/rand"
"crypto/sha256"
"encoding/base64"
"encoding/hex"
"encoding/json"
"log"
"net"
"strings"
"sync"
"time"
"kattila-agent/config"
)
var (
currentPSK string
mu sync.RWMutex
)
// StartKeyPoller checks the DNS record every hour
func StartKeyPoller() {
fetchKey()
ticker := time.NewTicker(1 * time.Hour)
go func() {
for range ticker.C {
fetchKey()
}
}()
}
func fetchKey() {
dnsName := config.Cfg.DNS
if dnsName == "" {
log.Println("security: No DNS configured for PSK")
return
}
txts, err := net.LookupTXT(dnsName)
if err != nil {
log.Printf("security: Failed to lookup TXT for %s: %v", dnsName, err)
return
}
if len(txts) == 0 {
return
}
key := txts[0]
// Remove quotes if present
key = strings.Trim(key, `"`)
mu.Lock()
if currentPSK != key {
log.Println("security: New PSK discovered via DNS")
currentPSK = key
}
mu.Unlock()
}
func GetCurrentPSK() string {
mu.RLock()
defer mu.RUnlock()
return currentPSK
}
// FleetID generates a SHA256 of the PSK to uniquely identify the fleet
func FleetID() string {
psk := GetCurrentPSK()
if psk == "" {
return ""
}
hash := sha256.Sum256([]byte(psk))
return hex.EncodeToString(hash[:])
}
func GenerateNonce() string {
b := make([]byte, 16)
rand.Read(b)
return base64.StdEncoding.EncodeToString(b)
}
func SignPayload(data interface{}) string {
psk := GetCurrentPSK()
if psk == "" {
return ""
}
bytes, err := json.Marshal(data)
if err != nil {
return ""
}
h := hmac.New(sha256.New, []byte(psk))
h.Write(bytes)
return hex.EncodeToString(h.Sum(nil))
}