aboutsummaryrefslogtreecommitdiff
path: root/lib/ssh
diff options
context:
space:
mode:
authorShulhan <ms@kilabit.info>2023-12-26 02:25:30 +0700
committerShulhan <ms@kilabit.info>2023-12-26 02:37:15 +0700
commitbbefc9b4cd94a0688c45245ff2b2386ddb76d32e (patch)
treee6cd54ed6df4186d12bfe049dea44e19f4858173 /lib/ssh
parentb12a886ce9150bce62a78e0c621a6bcdd845f212 (diff)
downloadpakakeh.go-bbefc9b4cd94a0688c45245ff2b2386ddb76d32e.tar.xz
ssh/config: refactoring the Config merge
This changes rename method [Config.Prepend] to [Config.Merge]. The way that how the other Config merged is changed. Instead of appending all of other's sections into the current Config, append the other Config instance to the current instance of Config. During [Config.Get] the top Config will be evaluated first, and then the other Config is evaluated in order of Merge.
Diffstat (limited to 'lib/ssh')
-rw-r--r--lib/ssh/config/config.go33
-rw-r--r--lib/ssh/config/config_test.go39
-rw-r--r--lib/ssh/config/parser_test.go3
-rw-r--r--lib/ssh/config/section.go25
-rw-r--r--lib/ssh/config/testdata/config2
-rw-r--r--lib/ssh/config/testdata/config_get_test.txt1
-rw-r--r--lib/ssh/config/testdata/config_merge_test.txt15
-rw-r--r--lib/ssh/config/testdata/sub/config6
-rw-r--r--lib/ssh/config/testdata/sub/include4
9 files changed, 109 insertions, 19 deletions
diff --git a/lib/ssh/config/config.go b/lib/ssh/config/config.go
index 14613b37..6f252398 100644
--- a/lib/ssh/config/config.go
+++ b/lib/ssh/config/config.go
@@ -40,6 +40,9 @@ type Config struct {
homeDir string
sections []*Section
+
+ // others Config, as result of [Config.Merge].
+ others []*Config
}
// newConfig create new SSH Config instance from file.
@@ -156,6 +159,16 @@ func (cfg *Config) Get(host string) (section *Section) {
section.merge(hostMatch)
}
}
+
+ var (
+ other *Config
+ subsec *Section
+ )
+ for _, other = range cfg.others {
+ subsec = other.Get(host)
+ section.merge(subsec)
+ }
+
section.setDefaults()
if host != `` && section.Field[KeyHostname] == `` {
@@ -165,16 +178,18 @@ func (cfg *Config) Get(host string) (section *Section) {
return section
}
-// Prepend other Config's sections to this Config.
-// The other's sections will be at the top of the list.
+// Merge other Config as part of this Config.
+// This function can be used to combine multiple SSH config files into one.
//
-// This function can be useful if we want to load another SSH config file
-// without using Include directive.
-func (cfg *Config) Prepend(other *Config) {
- newSections := make([]*Section, 0, len(cfg.sections)+len(other.sections))
- newSections = append(newSections, other.sections...)
- newSections = append(newSections, cfg.sections...)
- cfg.sections = newSections
+// For example after the user's "~/.ssh/config" has been loaded, we can
+// merge it with system "/etc/ssh/ssh_config".
+// During [Config.Get] the top Config will be evaluated first, and then the
+// other Config is evaluated in order of Merge-d.
+func (cfg *Config) Merge(other *Config) {
+ if other == nil {
+ return
+ }
+ cfg.others = append(cfg.others, other)
}
// loadEnvironments get all environments variables and store it in the map for
diff --git a/lib/ssh/config/config_test.go b/lib/ssh/config/config_test.go
index bf9aed9b..95c6adc4 100644
--- a/lib/ssh/config/config_test.go
+++ b/lib/ssh/config/config_test.go
@@ -115,6 +115,45 @@ func TestConfigGet(t *testing.T) {
}
}
+func TestConfigMerge(t *testing.T) {
+ var (
+ tdata *test.Data
+ err error
+ )
+
+ tdata, err = test.LoadData(`testdata/config_merge_test.txt`)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ var cfg *Config
+
+ cfg, err = Load(`./testdata/sub/config`)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ var topcfg *Config
+
+ topcfg, err = Load(`./testdata/config`)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ cfg.Merge(topcfg)
+
+ var (
+ host = `my.example.local`
+ gotSection = cfg.Get(host)
+
+ buf bytes.Buffer
+ )
+
+ gotSection.WriteTo(&buf)
+
+ test.Assert(t, host, string(tdata.Output[host]), buf.String())
+}
+
func TestParseKeyValue(t *testing.T) {
cases := []struct {
line string
diff --git a/lib/ssh/config/parser_test.go b/lib/ssh/config/parser_test.go
index 1bb6f0e1..bcbded43 100644
--- a/lib/ssh/config/parser_test.go
+++ b/lib/ssh/config/parser_test.go
@@ -75,7 +75,7 @@ func TestReadLines(t *testing.T) {
`User test`,
`IdentityFile ~/.ssh/notexist`,
`Host *.example.local`,
- `Include sub/config`,
+ `Include sub/include`,
`Host foo.local`,
`Hostname 127.0.0.3`,
`Port 28022`,
@@ -123,6 +123,7 @@ func TestConfigParser_load(t *testing.T) {
`Hostname 127.0.0.2`,
`User wildcard`,
`IdentityFile ~/.ssh/notexist`,
+ `UserKnownHostsFile ~/.ssh/known_hosts_example_local`,
`Host foo.local`,
`Hostname 127.0.0.3`,
`Port 28022`,
diff --git a/lib/ssh/config/section.go b/lib/ssh/config/section.go
index 1e624220..6fe4877e 100644
--- a/lib/ssh/config/section.go
+++ b/lib/ssh/config/section.go
@@ -667,6 +667,16 @@ func (section *Section) MarshalText() (text []byte, err error) {
}
continue
}
+ if key == KeyUserKnownHostsFile {
+ for _, val = range section.knownHostsFile {
+ buf.WriteString(` `)
+ buf.WriteString(key)
+ buf.WriteByte(' ')
+ buf.WriteString(section.pathFold(val))
+ buf.WriteByte('\n')
+ }
+ continue
+ }
buf.WriteString(` `)
buf.WriteString(key)
@@ -688,13 +698,18 @@ func (section *Section) WriteTo(w io.Writer) (n int64, err error) {
return int64(c), err
}
-// pathFold replace the home directory prefix with '~'.
+// pathFold remove the path prefix from input file "in", start from the
+// "config" directory and then the user's home directory.
func (section *Section) pathFold(in string) (out string) {
- if !strings.HasPrefix(in, section.homeDir) {
- return in
+ if filepath.HasPrefix(in, section.dir) {
+ out, _ = filepath.Rel(section.dir, in)
+ return out
+ }
+ if filepath.HasPrefix(in, section.homeDir) {
+ out, _ = filepath.Rel(section.homeDir, in)
+ return `~/` + out
}
- out = `~` + in[len(section.homeDir):]
- return out
+ return in
}
// pathUnfold expand the file to make it absolute.
diff --git a/lib/ssh/config/testdata/config b/lib/ssh/config/testdata/config
index 58873a35..c61f9c08 100644
--- a/lib/ssh/config/testdata/config
+++ b/lib/ssh/config/testdata/config
@@ -8,7 +8,7 @@ Host example.local
# comment
Host *.example.local
- Include sub/config
+ Include sub/include
Host foo.local
Hostname 127.0.0.3
diff --git a/lib/ssh/config/testdata/config_get_test.txt b/lib/ssh/config/testdata/config_get_test.txt
index fcab297b..7aa28784 100644
--- a/lib/ssh/config/testdata/config_get_test.txt
+++ b/lib/ssh/config/testdata/config_get_test.txt
@@ -28,6 +28,7 @@ Host my.example.local
identityfile ~/.ssh/notexist
port 22
user wildcard
+ userknownhostsfile ~/.ssh/known_hosts_example_local
xauthlocation /usr/X11R6/bin/xauth
<<< foo.local
diff --git a/lib/ssh/config/testdata/config_merge_test.txt b/lib/ssh/config/testdata/config_merge_test.txt
new file mode 100644
index 00000000..ef058e1c
--- /dev/null
+++ b/lib/ssh/config/testdata/config_merge_test.txt
@@ -0,0 +1,15 @@
+Test data for [Config.Merge].
+
+<<< my.example.local
+Host my.example.local
+ challengeresponseauthentication yes
+ checkhostip yes
+ connectionattempts 1
+ hostname 127.0.0.2
+ identityfile my-example-local
+ identityfile ~/.ssh/notexist
+ port 22
+ user wildcard
+ userknownhostsfile my_known_hosts
+ userknownhostsfile ~/.ssh/known_hosts_example_local
+ xauthlocation /usr/X11R6/bin/xauth
diff --git a/lib/ssh/config/testdata/sub/config b/lib/ssh/config/testdata/sub/config
index 91ba986f..9a1d5bf6 100644
--- a/lib/ssh/config/testdata/sub/config
+++ b/lib/ssh/config/testdata/sub/config
@@ -1,3 +1,3 @@
- Hostname 127.0.0.2
- User wildcard
- IdentityFile ~/.ssh/notexist
+Host my.example.local
+ UserKnownHostsFile my_known_hosts
+ IdentityFile my-example-local
diff --git a/lib/ssh/config/testdata/sub/include b/lib/ssh/config/testdata/sub/include
new file mode 100644
index 00000000..10f7705f
--- /dev/null
+++ b/lib/ssh/config/testdata/sub/include
@@ -0,0 +1,4 @@
+ Hostname 127.0.0.2
+ User wildcard
+ IdentityFile ~/.ssh/notexist
+ UserKnownHostsFile ~/.ssh/known_hosts_example_local