first commit
Agent Release / build (push) Has been cancelled
Server Deploy / deploy (push) Has been cancelled

This commit is contained in:
domrichardson
2026-06-15 13:58:45 +01:00
commit c9868b2108
55 changed files with 11076 additions and 0 deletions
+129
View File
@@ -0,0 +1,129 @@
package agentsync
import (
"fmt"
"log"
"net"
"os"
"runtime"
"strings"
"time"
"github.com/mrhid6/keymanager/agent/internal/config"
grpcclient "github.com/mrhid6/keymanager/agent/internal/grpc"
"github.com/mrhid6/keymanager/agent/internal/keys"
)
func Run(cfg *config.Config) error {
client, err := grpcclient.New(cfg.ServerURL, cfg.TLS)
if err != nil {
return fmt.Errorf("dial grpc: %w", err)
}
defer client.Close()
// Register if we have a pre-reg token
if cfg.PreRegToken != "" {
log.Println("registering with server...")
hostname, _ := os.Hostname()
ipAddress := localIP()
osInfo := fmt.Sprintf("%s %s", runtime.GOOS, runtime.GOARCH)
agentToken, err := client.Register(cfg.ServerID, cfg.PreRegToken, hostname, ipAddress, osInfo)
if err != nil {
return fmt.Errorf("registration failed: %w", err)
}
cfg.AgentToken = agentToken
cfg.PreRegToken = ""
if err := config.Save(cfg); err != nil {
return fmt.Errorf("save config: %w", err)
}
log.Println("registration successful")
// Reconnect with potentially updated state
client.Close()
client, err = grpcclient.New(cfg.ServerURL, cfg.TLS)
if err != nil {
return fmt.Errorf("reconnect: %w", err)
}
}
if cfg.AgentToken == "" {
return fmt.Errorf("no agent token available — registration required")
}
ticker := time.NewTicker(cfg.PollInterval)
defer ticker.Stop()
// Run immediately on startup
if err := poll(client, cfg); err != nil {
log.Printf("poll error: %v", err)
}
for range ticker.C {
if err := poll(client, cfg); err != nil {
log.Printf("poll error: %v", err)
}
}
return nil
}
func poll(client *grpcclient.Client, cfg *config.Config) error {
desired, err := client.SyncKeys(cfg.ServerID, cfg.AgentToken)
if err != nil {
return fmt.Errorf("SyncKeys: %w", err)
}
current, err := keys.ReadAuthorizedKeys()
if err != nil {
return fmt.Errorf("read authorized_keys: %w", err)
}
if !keys.StateChanged(current, desired) {
log.Println("authorized_keys unchanged, skipping write")
return nil
}
if err := keys.WriteAuthorizedKeys(desired); err != nil {
return fmt.Errorf("write authorized_keys: %w", err)
}
log.Printf("authorized_keys updated (%d keys)", len(desired))
return nil
}
func localIP() string {
addrs, err := net.InterfaceAddrs()
if err != nil {
return ""
}
for _, addr := range addrs {
if ipNet, ok := addr.(*net.IPNet); ok && !ipNet.IP.IsLoopback() {
if ipNet.IP.To4() != nil {
return ipNet.IP.String()
}
}
}
return ""
}
// GenerateAndUpload generates an SSH keypair and uploads the public key to the server.
func GenerateAndUpload(cfg *config.Config, label string) error {
client, err := grpcclient.New(cfg.ServerURL, cfg.TLS)
if err != nil {
return err
}
defer client.Close()
keyPath := fmt.Sprintf("/root/.ssh/keymanager_%s", strings.ReplaceAll(label, " ", "_"))
pubKey, err := keys.GenerateKeyPair(keyPath, label)
if err != nil {
return err
}
keyID, err := client.UploadGeneratedKey(cfg.ServerID, cfg.AgentToken, pubKey, label)
if err != nil {
return err
}
log.Printf("uploaded generated key %s (key_id=%s)", label, keyID)
return nil
}