Add SSH-managed node registry with connection testing and reauth.
Register hosts under Containers/VMs/Docker with encrypted key storage, and require re-authentication for sensitive account changes.
This commit is contained in:
@@ -0,0 +1,73 @@
|
||||
package auth
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"golang.org/x/crypto/ssh"
|
||||
)
|
||||
|
||||
const defaultSSHPort = "22"
|
||||
const sshDialTimeout = 10 * time.Second
|
||||
|
||||
// TestSSHConnection dials host over SSH using the given private key and username.
|
||||
// Host key verification is intentionally skipped for this connectivity check;
|
||||
// trust-on-first-use / known_hosts can be added later.
|
||||
func TestSSHConnection(hostIP string, username string, privateKeyPEM string, passphrase string) error {
|
||||
hostIP = strings.TrimSpace(hostIP)
|
||||
username = strings.TrimSpace(username)
|
||||
if hostIP == "" {
|
||||
return fmt.Errorf("host IP is required")
|
||||
}
|
||||
if username == "" {
|
||||
return fmt.Errorf("username is required")
|
||||
}
|
||||
|
||||
signer, err := parseSigner(privateKeyPEM, passphrase)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
config := &ssh.ClientConfig{
|
||||
User: username,
|
||||
Auth: []ssh.AuthMethod{
|
||||
ssh.PublicKeys(signer),
|
||||
},
|
||||
HostKeyCallback: ssh.InsecureIgnoreHostKey(),
|
||||
Timeout: sshDialTimeout,
|
||||
}
|
||||
|
||||
address := net.JoinHostPort(hostIP, defaultSSHPort)
|
||||
client, err := ssh.Dial("tcp", address, config)
|
||||
if err != nil {
|
||||
return fmt.Errorf("ssh dial %s: %w", address, err)
|
||||
}
|
||||
defer client.Close()
|
||||
return nil
|
||||
}
|
||||
|
||||
func parseSigner(privateKeyPEM string, passphrase string) (ssh.Signer, error) {
|
||||
trimmed := strings.TrimSpace(privateKeyPEM)
|
||||
if trimmed == "" {
|
||||
return nil, fmt.Errorf("private key is required")
|
||||
}
|
||||
|
||||
var (
|
||||
signer ssh.Signer
|
||||
err error
|
||||
)
|
||||
if strings.TrimSpace(passphrase) == "" {
|
||||
signer, err = ssh.ParsePrivateKey([]byte(trimmed))
|
||||
} else {
|
||||
signer, err = ssh.ParsePrivateKeyWithPassphrase(
|
||||
[]byte(trimmed),
|
||||
[]byte(passphrase),
|
||||
)
|
||||
}
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("parse private key: %w", err)
|
||||
}
|
||||
return signer, nil
|
||||
}
|
||||
Reference in New Issue
Block a user