diff --git a/internal/api/handlers_machines.go b/internal/api/handlers_machines.go index 6baf583..0bed76e 100644 --- a/internal/api/handlers_machines.go +++ b/internal/api/handlers_machines.go @@ -4,6 +4,7 @@ import ( "context" "database/sql" "encoding/json" + "fmt" "log/slog" "net/http" "os" @@ -367,45 +368,33 @@ func (h *MachineHandler) DeployKeys(w http.ResponseWriter, r *http.Request) { } } - pairRepo := models.NewSyncPairRepository(h.db) - allPairs, err := pairRepo.GetAll() + allMachines, err := repo.GetAll() if err != nil { - slog.Warn("failed to fetch sync pairs for auto-detect", "error", err) + slog.Warn("failed to fetch machines for auto-detect", "error", err) } var keys []sshmanager.DeployKey + seenKeys := make(map[string]bool) + knownHostsHosts := []string{} - for _, pair := range allPairs { - if pair.SourceMachineID != nil && *pair.SourceMachineID == m.ID { - if pair.DestMachineID != nil { - destMachine, err := repo.GetByID(*pair.DestMachineID) - if err == nil && destMachine.SSHKeyID != nil { - skRepo := models.NewSSHKeyRepository(h.db) - sk, err := skRepo.GetByID(*destMachine.SSHKeyID) - if err == nil && sk.PrivateKeyPath != "" { - keys = append(keys, sshmanager.DeployKey{ - LocalPath: sk.PrivateKeyPath, - RemotePath: "/var/lib/syncserver/ssh/keys/" + filepath.Base(sk.PrivateKeyPath), - Mode: 0600, - }) - } - } - } + for _, other := range allMachines { + if other.ID == m.ID { + continue } - if pair.DestMachineID != nil && *pair.DestMachineID == m.ID { - if pair.SourceMachineID != nil { - srcMachine, err := repo.GetByID(*pair.SourceMachineID) - if err == nil && srcMachine.SSHKeyID != nil { - skRepo := models.NewSSHKeyRepository(h.db) - sk, err := skRepo.GetByID(*srcMachine.SSHKeyID) - if err == nil && sk.PrivateKeyPath != "" { - keys = append(keys, sshmanager.DeployKey{ - LocalPath: sk.PrivateKeyPath, - RemotePath: "/var/lib/syncserver/ssh/keys/" + filepath.Base(sk.PrivateKeyPath), - Mode: 0600, - }) - } + knownHostsHosts = append(knownHostsHosts, fmt.Sprintf("%s:%d", other.Host, other.Port)) + if other.SSHKeyID != nil { + skRepo := models.NewSSHKeyRepository(h.db) + sk, err := skRepo.GetByID(*other.SSHKeyID) + if err == nil && sk.PrivateKeyPath != "" { + if seenKeys[sk.PrivateKeyPath] { + continue } + seenKeys[sk.PrivateKeyPath] = true + keys = append(keys, sshmanager.DeployKey{ + LocalPath: sk.PrivateKeyPath, + RemotePath: "/var/lib/syncserver/ssh/keys/" + filepath.Base(sk.PrivateKeyPath), + Mode: 0600, + }) } } } @@ -422,7 +411,7 @@ func (h *MachineHandler) DeployKeys(w http.ResponseWriter, r *http.Request) { m.Port, m.SSHUser, keys, - req.KnownHostsHost, + knownHostsHosts, req.IncludeServerKey, ) if err != nil { diff --git a/internal/sshmanager/deploy.go b/internal/sshmanager/deploy.go index 252d69e..21f18bf 100644 --- a/internal/sshmanager/deploy.go +++ b/internal/sshmanager/deploy.go @@ -26,7 +26,7 @@ type DeployResult struct { Errors []string } -func DeployKeysToMachine(ctx context.Context, serverKeyPath, serverPubKey string, host string, port int, user string, keys []DeployKey, knownHostsHost string, addServerPubKey bool) (*DeployResult, error) { +func DeployKeysToMachine(ctx context.Context, serverKeyPath, serverPubKey string, host string, port int, user string, keys []DeployKey, knownHostsHosts []string, addServerPubKey bool) (*DeployResult, error) { result := &DeployResult{Success: true, Messages: []string{}, Errors: []string{}} addr := fmt.Sprintf("%s:%d", host, port) @@ -141,22 +141,24 @@ func DeployKeysToMachine(ctx context.Context, serverKeyPath, serverPubKey string result.Messages = append(result.Messages, fmt.Sprintf("Uploaded %s to %s:%s", filepath.Base(k.LocalPath), host, k.RemotePath)) } - if knownHostsHost != "" { - session2, err := conn.NewSession() - if err != nil { - result.Errors = append(result.Errors, fmt.Sprintf("session for ssh-keyscan: %v", err)) - result.Success = false - } else { + if len(knownHostsHosts) > 0 { + for _, khHost := range knownHostsHosts { + session2, err := conn.NewSession() + if err != nil { + result.Errors = append(result.Errors, fmt.Sprintf("session for ssh-keyscan %s: %v", khHost, err)) + result.Success = false + continue + } session2.Stdout = &stdout session2.Stderr = &stderr - err := session2.Run(fmt.Sprintf("ssh-keyscan -H -p %d %s 2>/dev/null >> %s/known_hosts", port, knownHostsHost, remoteSSHDir)) + err = session2.Run(fmt.Sprintf("ssh-keyscan -H -p %s 2>/dev/null >> %s/known_hosts", khHost, remoteSSHDir)) session2.Close() if err != nil { - slog.Warn("deploy: ssh-keyscan failed", "host", knownHostsHost, "port", port, "error", err) - result.Errors = append(result.Errors, fmt.Sprintf("ssh-keyscan %s: %v (stderr: %s)", knownHostsHost, err, stderr.String())) + slog.Warn("deploy: ssh-keyscan failed", "host", khHost, "error", err) + result.Errors = append(result.Errors, fmt.Sprintf("ssh-keyscan %s: %v (stderr: %s)", khHost, err, stderr.String())) result.Success = false } else { - result.Messages = append(result.Messages, fmt.Sprintf("Populated known_hosts with %s:%d", knownHostsHost, port)) + result.Messages = append(result.Messages, fmt.Sprintf("Populated known_hosts with %s", khHost)) } } }