Let ssh-client use the SDK's forgekey, and let the checks accept a module's own .ssh (issues 1480, 1484), part 2 of 17

This commit is contained in:
2026-10-20 20:44:30 +00:00
parent 1e1def4d32
commit bf8f47ce66
9 changed files with 98 additions and 35 deletions
@@ -17,13 +17,15 @@ import (
"strings"
"sync"
"time"
"git.novox.be/novox/mesh-sdk/go/forgekey"
)
// Client answers about one home's ~/.ssh.
type Client struct {
Home string
UID int
Run Runner
Run forgekey.Runner
Now func() time.Time
// Settle is how long a path in ~/.ssh is left by the sweep after it changed (sweep.go): zero in tests.
Settle time.Duration
@@ -35,7 +37,7 @@ func NewClient() *Client {
if home == "" {
home, _ = os.UserHomeDir()
}
return &Client{Home: home, UID: os.Getuid(), Run: ExecRunner, Now: time.Now, Settle: time.Minute}
return &Client{Home: home, UID: os.Getuid(), Run: forgekey.ExecRunner, Now: time.Now, Settle: time.Minute}
}
func (c *Client) ssh(name string) string { return filepath.Join(c.Home, ".ssh", name) }
@@ -114,7 +116,7 @@ func matchesAny(host string, patterns []string) bool {
return hit
}
func named(what string, r Ran) error {
func named(what string, r forgekey.Ran) error {
switch {
case r.Err == "ENOENT":
return fmt.Errorf("%s: the program is not installed on this machine", what)
@@ -207,7 +209,7 @@ func (c *Client) Keys(ctx context.Context) ([]Key, error) {
k.AgeDays = int(c.Now().Sub(info.ModTime()).Hours() / 24)
}
if pub, err := os.ReadFile(path + ".pub"); err == nil {
if pk, err := ParseKeyLine(string(pub)); err == nil {
if pk, err := forgekey.ParseKeyLine(string(pub)); err == nil {
k.Type, k.Bits, k.Fingerprint, k.Comment, k.Weak = pk.Type, pk.Bits, pk.Fingerprint, pk.Comment, pk.Weak
}
} else {
@@ -225,7 +227,7 @@ func (c *Client) Keys(ctx context.Context) ([]Key, error) {
var derived string
k.Passphrase, derived = c.passphrase(ctx, path)
if derived != "" {
if pk, err := ParseKeyLine(derived); err == nil {
if pk, err := forgekey.ParseKeyLine(derived); err == nil {
if k.Fingerprint != "" && !k.PublicMissing && pk.Fingerprint != k.Fingerprint {
k.PublicMismatch = fmt.Sprintf("the key is %s; its .pub is %s", pk.Fingerprint, k.Fingerprint)
}
@@ -264,7 +266,7 @@ func (c *Client) passphrase(ctx context.Context, path string) (string, string) {
// AuthorizedKey is one line of authorized_keys, by fingerprint.
type AuthorizedKey struct {
PublicKey
forgekey.PublicKey
Line int `json:"line"`
InMesh bool `json:"in_mesh_region,omitempty"`
}
@@ -309,7 +311,7 @@ func (c *Client) readAuthorized() ([]AuthorizedKey, []int, error) {
case t == "" || strings.HasPrefix(t, "#"):
continue
}
k, err := ParseKeyLine(t)
k, err := forgekey.ParseKeyLine(t)
if err != nil {
bad = append(bad, n+1)
continue
@@ -397,26 +399,26 @@ func atomicWrite(path string, data []byte, mode fs.FileMode) error {
// ---- known_hosts -------------------------------------------------------------------------------
// knownFor is what known_hosts holds for one host (port 22, or [host]:port), by fingerprint.
func (c *Client) knownFor(ctx context.Context, host string, port int) ([]PublicKey, error) {
func (c *Client) knownFor(ctx context.Context, host string, port int) ([]forgekey.PublicKey, error) {
name := host
if port != 22 {
name = fmt.Sprintf("[%s]:%d", host, port)
}
file := c.ssh("known_hosts")
if _, err := os.Stat(file); os.IsNotExist(err) {
return []PublicKey{}, nil
return []forgekey.PublicKey{}, nil
}
r := c.Run(ctx, nil, "ssh-keygen", "-F", name, "-f", file)
if r.Err != "" {
return nil, named("ssh-keygen -F", r)
}
// Exit 1 with nothing printed is "not found".
keys := []PublicKey{}
keys := []forgekey.PublicKey{}
for _, l := range strings.Split(r.Stdout, "\n") {
if l = strings.TrimSpace(l); l == "" || strings.HasPrefix(l, "#") {
continue
}
if k, err := ParseKeyLine(l); err == nil {
if k, err := forgekey.ParseKeyLine(l); err == nil {
k.Comment, k.Options = "", ""
keys = append(keys, k)
}
@@ -425,17 +427,17 @@ func (c *Client) knownFor(ctx context.Context, host string, port int) ([]PublicK
}
// scan asks a host for its keys now.
func (c *Client) scan(ctx context.Context, host string, port int) ([]PublicKey, []string, error) {
func (c *Client) scan(ctx context.Context, host string, port int) ([]forgekey.PublicKey, []string, error) {
r := c.Run(ctx, nil, "ssh-keyscan", "-T", "5", "-p", fmt.Sprint(port), host)
if r.Err != "" {
return nil, nil, named("ssh-keyscan", r)
}
keys, lines := []PublicKey{}, []string{}
keys, lines := []forgekey.PublicKey{}, []string{}
for _, l := range strings.Split(r.Stdout, "\n") {
if l = strings.TrimSpace(l); l == "" || strings.HasPrefix(l, "#") {
continue
}
if k, err := ParseKeyLine(l); err == nil {
if k, err := forgekey.ParseKeyLine(l); err == nil {
k.Comment, k.Options = "", ""
keys = append(keys, k)
lines = append(lines, l)
@@ -455,7 +457,7 @@ func orElse(s, def string) string {
}
// compare says how what is known stands against what the host offers now.
func compare(known, live []PublicKey) string {
func compare(known, live []forgekey.PublicKey) string {
if len(known) == 0 {
return "not known: the first connection would ask"
}