Skip to content

Commit

Permalink
Merge pull request #105 from moul/improvements
Browse files Browse the repository at this point in the history
Improvements
  • Loading branch information
moul committed Feb 3, 2016
2 parents 8a91506 + 2cfbb44 commit c7542cd
Show file tree
Hide file tree
Showing 10 changed files with 223 additions and 25 deletions.
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -256,6 +256,9 @@ Get a released version on: https://github.com/moul/advanced-ssh-config/releases
### master (unreleased)

* Avoid exiting when an included file contains errors ([#95](https://github.com/moul/advanced-ssh-config/issues/95))
* Anonymize paths in `assh info`
* Support of `assh proxy --dry-run` option
* Fix: do not resolve variables in hostnames twice ([#103](https://github.com/moul/advanced-ssh-config/issues/103))

[Full commits list](https://github.com/moul/advanced-ssh-config/compare/v2.1.0...master)

Expand Down
4 changes: 4 additions & 0 deletions pkg/commands/commands.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,10 @@ var Commands = []cli.Command{
Value: 0,
Usage: "SSH destination port",
},
cli.BoolFlag{
Name: "dry-run",
Usage: "Only show how assh would connect but don't actually do it",
},
},
},
/*
Expand Down
5 changes: 4 additions & 1 deletion pkg/commands/info.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"fmt"
"os"
"runtime"
"strings"

"github.com/bugsnag/osext"
"github.com/codegangsta/cli"
Expand All @@ -25,8 +26,10 @@ func cmdInfo(c *cli.Context) {
fmt.Printf("OS/Arch: %s/%s\n", runtime.GOOS, runtime.GOARCH)
fmt.Println("")
fmt.Printf("RC files:\n")
homeDir := config.GetHomeDir()
for _, filename := range conf.IncludedFiles() {
fmt.Printf("- %s\n", filename)
relativeFilename := strings.Replace(filename, homeDir, "~", -1)
fmt.Printf("- %s\n", relativeFilename)
}
fmt.Println("")
fmt.Println("Statistics:")
Expand Down
52 changes: 36 additions & 16 deletions pkg/commands/proxy.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,20 @@ import (
)

func cmdProxy(c *cli.Context) {
Logger.Debugf("assh args: %s", c.Args())

if len(c.Args()) < 1 {
Logger.Fatalf("assh: \"proxy\" requires 1 argument. See 'assh proxy --help'.")
}

// dry-run option
// Setting the 'ASSH_DRYRUN=1' environment variable,
// so 'assh' can use gateways using sub-SSH commands.
if c.Bool("dry-run") == true {
os.Setenv("ASSH_DRYRUN", "1")
}
dryRun := os.Getenv("ASSH_DRYRUN") == "1"

conf, err := config.Open()
if err != nil {
Logger.Fatalf("Cannot open configuration file: %v", err)
Expand All @@ -44,7 +54,7 @@ func cmdProxy(c *cli.Context) {
}

Logger.Debugf("Proxying")
err = proxy(host, conf)
err = proxy(host, conf, dryRun)
if err != nil {
Logger.Fatalf("Proxy error: %v", err)
}
Expand All @@ -66,36 +76,37 @@ func prepareHostControlPath(host, gateway *config.Host) error {
return os.MkdirAll(gatewayControlPath, 0700)
}

func proxy(host *config.Host, conf *config.Config) error {
func proxy(host *config.Host, conf *config.Config, dryRun bool) error {
if len(host.Gateways) > 0 {
Logger.Debugf("Trying gateways: %s", host.Gateways)
for _, gateway := range host.Gateways {
if gateway == "direct" {
err := proxyDirect(host)
err := proxyDirect(host, dryRun)
if err != nil {
Logger.Errorf("Failed to use 'direct' connection")
Logger.Errorf("Failed to use 'direct' connection: %v", err)
}
} else {
hostCopy := host.Clone()
gatewayHost := conf.GetGatewaySafe(gateway)

err := prepareHostControlPath(host, gatewayHost)
err := prepareHostControlPath(hostCopy, gatewayHost)
if err != nil {
return err
}

if host.ProxyCommand == "" {
host.ProxyCommand = "nc %h %p"
if hostCopy.ProxyCommand == "" {
hostCopy.ProxyCommand = "nc %h %p"
}
// FIXME: dynamically add "-v" flags

if err = hostPrepare(host); err != nil {
if err = hostPrepare(hostCopy); err != nil {
return err
}

command := "ssh %name -- " + host.ExpandString(host.ProxyCommand)
command := "ssh %name -- " + hostCopy.ExpandString(hostCopy.ProxyCommand)

Logger.Debugf("Using gateway '%s': %s", gateway, command)
err = proxyCommand(gatewayHost, command)
err = proxyCommand(gatewayHost, command, dryRun)
if err == nil {
return nil
}
Expand All @@ -106,23 +117,28 @@ func proxy(host *config.Host, conf *config.Config) error {
}

Logger.Debugf("Connecting without gateway")
return proxyDirect(host)
return proxyDirect(host, dryRun)
}

func proxyDirect(host *config.Host) error {
func proxyDirect(host *config.Host, dryRun bool) error {
if host.ProxyCommand != "" {
return proxyCommand(host, host.ProxyCommand)
return proxyCommand(host, host.ProxyCommand, dryRun)
}
return proxyGo(host)
return proxyGo(host, dryRun)
}

func proxyCommand(host *config.Host, command string) error {
func proxyCommand(host *config.Host, command string, dryRun bool) error {
command = host.ExpandString(command)
args, err := shlex.Split(command)
Logger.Debugf("ProxyCommand: %s", command)
if err != nil {
return err
}

if dryRun {
return fmt.Errorf("dry-run: Execute %s", args)
}

spawn := exec.Command(args[0], args[1:]...)
spawn.Stdout = os.Stdout
spawn.Stdin = os.Stdin
Expand Down Expand Up @@ -168,12 +184,16 @@ func hostPrepare(host *config.Host) error {
return nil
}

func proxyGo(host *config.Host) error {
func proxyGo(host *config.Host, dryRun bool) error {
Logger.Debugf("Preparing host object")
if err := hostPrepare(host); err != nil {
return err
}

if dryRun {
return fmt.Errorf("dry-run: Golang native TCP connection to '%s:%s'", host.HostName, host.Port)
}

Logger.Debugf("Connecting to %s:%s", host.HostName, host.Port)
conn, err := net.Dial("tcp", fmt.Sprintf("%s:%s", host.HostName, host.Port))
if err != nil {
Expand Down
12 changes: 11 additions & 1 deletion pkg/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,17 @@ func computeHost(host *Host, config *Config, name string, fullCompute bool) (*Ho
// expands variables in host
// i.e: %h.some.zone -> {name}.some.zone
hostname := strings.Replace(computedHost.HostName, "%h", "%n", -1)
computedHost.HostName = computedHost.ExpandString(hostname)

// ssh resolve '%h' in hostnames
// -> we bypass the string expansion if the input matches
// an already resolved hostname
// See https://github.com/moul/advanced-ssh-config/issues/103
pattern := strings.Replace(hostname, "%n", "*", -1)
if match, _ := path.Match(pattern, computedHost.inputName); match {
computedHost.HostName = computedHost.inputName
} else {
computedHost.HostName = computedHost.ExpandString(hostname)
}
}

return computedHost, nil
Expand Down
22 changes: 19 additions & 3 deletions pkg/config/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,9 @@ hosts:
"*.kkk":
HostName: "%h.kkkkk"
"lll-*":
HostName: "%h.lll"
nnn:
Inherits:
- mmm
Expand Down Expand Up @@ -198,7 +201,7 @@ func TestConfig_LoadConfig(t *testing.T) {
config := New()
err := config.LoadConfig(strings.NewReader(yamlConfig))
So(err, ShouldBeNil)
So(len(config.Hosts), ShouldEqual, 12)
So(len(config.Hosts), ShouldEqual, 13)
So(config.Hosts["aaa"].HostName, ShouldEqual, "1.2.3.4")
So(config.Hosts["aaa"].Port, ShouldEqual, "")
So(config.Hosts["aaa"].User, ShouldEqual, "")
Expand Down Expand Up @@ -357,6 +360,9 @@ func TestConfig_JsonSring(t *testing.T) {
"jjj": {
"HostName": "%h.jjjjj"
},
"lll-*": {
"HostName": "%h.lll"
},
"nnn": {
"User": "nnnn",
"Inherits": [
Expand Down Expand Up @@ -422,6 +428,16 @@ func TestComputeHost(t *testing.T) {
So(err, ShouldBeNil)
So(computed.HostName, ShouldEqual, "test.kkk.kkkkk")
})
Convey("Do not expand variables twice", func() {
host := config.Hosts["lll-*"]
computed, err := computeHost(&host, config, "lll-42", true)
So(err, ShouldBeNil)
So(computed.HostName, ShouldEqual, "lll-42.lll")

computed, err = computeHost(&host, config, "lll-43.lll", true)
So(err, ShouldBeNil)
So(computed.HostName, ShouldEqual, "lll-43.lll")
})
Convey("Expand variables using environment", func() {
host := config.Hosts["bbb"]
So(host.HostName, ShouldEqual, "$ENV_VAR_HOSTNAME")
Expand Down Expand Up @@ -563,7 +579,7 @@ func TestConfig_LoadFiles(t *testing.T) {
So(err, ShouldBeNil)
So(config.includedFiles[file.Name()], ShouldEqual, true)
So(len(config.includedFiles), ShouldEqual, 1)
So(len(config.Hosts), ShouldEqual, 12)
So(len(config.Hosts), ShouldEqual, 13)
So(config.Hosts["aaa"].HostName, ShouldEqual, "1.2.3.4")
So(config.Hosts["aaa"].Port, ShouldEqual, "")
So(config.Hosts["aaa"].User, ShouldEqual, "")
Expand Down Expand Up @@ -591,7 +607,7 @@ func TestConfig_LoadFiles(t *testing.T) {
So(err, ShouldBeNil)
So(config.includedFiles[file.Name()], ShouldEqual, true)
So(len(config.includedFiles), ShouldEqual, 1)
So(len(config.Hosts), ShouldEqual, 12)
So(len(config.Hosts), ShouldEqual, 13)
So(config.Hosts["aaa"].HostName, ShouldEqual, "1.2.3.4")
So(config.Hosts["aaa"].Port, ShouldEqual, "")
So(config.Hosts["aaa"].User, ShouldEqual, "")
Expand Down
7 changes: 7 additions & 0 deletions pkg/config/host.go
Original file line number Diff line number Diff line change
Expand Up @@ -117,10 +117,17 @@ func NewHost(name string) *Host {
}
}

// Name returns the name of a host
func (h *Host) Name() string {
return h.name
}

// Clone returns a copy of an existing Host
func (h *Host) Clone() *Host {
newHost := *h
return &newHost
}

// ApplyDefaults ensures a Host is valid by filling the missing fields with defaults
func (h *Host) ApplyDefaults(defaults *Host) {
// ssh-config fields
Expand Down
16 changes: 16 additions & 0 deletions pkg/config/host_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -75,3 +75,19 @@ func TestHost_ExpandString(t *testing.T) {
So(output, ShouldEqual, expected)
})
}

func TestHost_Clone(t *testing.T) {
Convey("Testing Host.Clone()", t, func() {
a := NewHost("abc")
a.HostName = "1.2.3.4"
a.Port = "42"

b := a.Clone()

So(a, ShouldNotEqual, b)
So(a.HostName, ShouldEqual, "1.2.3.4")
So(b.HostName, ShouldEqual, "1.2.3.4")
So(a.Port, ShouldEqual, "42")
So(b.Port, ShouldEqual, "42")
})
}
16 changes: 12 additions & 4 deletions pkg/config/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,23 @@ import (
"strings"
)

// GetHomeDir returns '~' as a path
func GetHomeDir() string {
if homeDir := os.Getenv("HOME"); homeDir != "" {
return homeDir
}
if homeDir := os.Getenv("USERPROFILE"); homeDir != "" {
return homeDir
}
return ""
}

func expandUser(path string) (string, error) {
// Expand variables
path = os.ExpandEnv(path)

if path[:2] == "~/" {
homeDir := os.Getenv("HOME") // *nix
if homeDir == "" { // Windows
homeDir = os.Getenv("USERPROFILE")
}
homeDir := GetHomeDir()
if homeDir == "" {
return "", errors.New("user home directory not found")
}
Expand Down
Loading

0 comments on commit c7542cd

Please sign in to comment.