From 17e2087ebde2d798887e9ab1ca5221dcb64a992a Mon Sep 17 00:00:00 2001 From: Sylvie Crowe <107814465+oneirocosm@users.noreply.github.com> Date: Tue, 5 Mar 2024 20:10:34 -0800 Subject: [PATCH] Update IdentityFile Defaults to Match Documentation (#4) Credit to @virtuald for the original implementation of this fix. * Add support for retrieving all IdentityFile directives via DefaultAll * fix: set IdentityFile defaults to match man page The existing default IdentityFile list was incomplete and out of order. This updates it to match the defaults listed here: https://man7.org/linux/man-pages/man5/ssh_config.5.html --------- Co-authored-by: Dustin Spicuzza --- config.go | 22 +++++++++++++++++----- config_test.go | 3 +-- validators.go | 28 +++++++++++++++++++++++++--- 3 files changed, 43 insertions(+), 10 deletions(-) diff --git a/config.go b/config.go index a26e4d3..7d51cae 100644 --- a/config.go +++ b/config.go @@ -49,6 +49,13 @@ var _ = version type configFinder func() string +type config interface { + getinternal(alias, key string) string +} + +var _ config = &UserSettings{} +var _ config = &Config{} + // UserSettings checks ~/.ssh and /etc/ssh for configuration files. The config // files are parsed and cached the first time Get() or GetStrict() is called. type UserSettings struct { @@ -189,6 +196,10 @@ func (u *UserSettings) Get(alias, key string) string { return val } +func (u *UserSettings) getinternal(alias, key string) string { + return u.Get(alias, key) +} + // GetAll retrieves zero or more directives for key for the given alias. GetAll // returns nil if no value was found, or if IgnoreErrors is false and we could // not parse the configuration file. Use GetStrict to disambiguate the latter @@ -259,11 +270,7 @@ func (u *UserSettings) GetAllStrict(alias, key string) ([]string, error) { if err2 != nil || val2 != nil { return val2, err2 } - // TODO: IdentityFile has multiple default values that we should return. - if def := Default(key); def != "" { - return []string{def}, nil - } - return []string{}, nil + return DefaultAll(key, alias, u), nil } // ConfigFinder will invoke f to try to find a ssh config file in a custom @@ -425,6 +432,11 @@ func (c *Config) Get(alias, key string) (string, error) { return "", nil } +func (c *Config) getinternal(alias, key string) string { + v, _ := c.Get(alias, key) + return v +} + // GetAll returns all values in the configuration that match the alias and // contains key, or nil if none are present. func (c *Config) GetAll(alias, key string) ([]string, error) { diff --git a/config_test.go b/config_test.go index b296ee3..4b06e4e 100644 --- a/config_test.go +++ b/config_test.go @@ -111,8 +111,7 @@ func TestGetIdentities(t *testing.T) { t.Errorf("expected nil err, got %v", err) } if len(val) != len(defaultProtocol2Identities) { - // TODO: return the right values here. - log.Printf("expected defaults, got %v", val) + t.Errorf("expected defaults, got %v", val) } else { for i, v := range defaultProtocol2Identities { if val[i] != v { diff --git a/validators.go b/validators.go index 5977f90..62db53d 100644 --- a/validators.go +++ b/validators.go @@ -15,6 +15,26 @@ func Default(keyword string) string { return defaults[strings.ToLower(keyword)] } +// DefaultAll returns the default value for the given keyword, but as a slice. If +// there is no default for the keyword, nil is returned. +// +// Some multi-valued settings have different defaults based on other settings, so +// you must provide the host alias and a config to retrieve a setting from +func DefaultAll(keyword string, alias string, cfg config) []string { + if strings.ToLower(keyword) == "identityfile" && cfg.getinternal(alias, "Protocol") == "2" { + def := make([]string, len(defaultProtocol2Identities)) + copy(def, defaultProtocol2Identities) + return def + } + + def := Default(keyword) + if def != "" { + return []string{def} + } + + return nil +} + // Arguments where the value must be "yes" or "no" and *only* yes or no. var yesnos = map[string]bool{ strings.ToLower("BatchMode"): true, @@ -163,10 +183,12 @@ var defaults = map[string]string{ // these identities are used for SSH protocol 2 var defaultProtocol2Identities = []string{ - "~/.ssh/id_dsa", - "~/.ssh/id_ecdsa", - "~/.ssh/id_ed25519", "~/.ssh/id_rsa", + "~/.ssh/id_ecdsa", + "~/.ssh/id_ecdsa_sk", + "~/.ssh/id_ed25519", + "~/.ssh/id_ed25519_sk", + "~/.ssh/id_dsa", } // these directives support multiple items that can be collected