Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ require (
github.com/gofrs/uuid v3.2.0+incompatible
github.com/hpcloud/tail v1.0.0
github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0 // indirect
github.com/kevinburke/ssh_config v0.0.0-20190630040420-2e50c441276c
github.com/kevinburke/ssh_config v1.1.0
github.com/mitchellh/go-ps v1.0.0 // indirect
github.com/mitchellh/mapstructure v1.4.1
github.com/pelletier/go-buffruneio v0.2.0 // indirect
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0 h1:iQTw/8FWTuc7uia
github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0/go.mod h1:1NbS8ALrpOvjt0rHPNLyCIeMtbizbir8U//inJ+zuB8=
github.com/kevinburke/ssh_config v0.0.0-20190630040420-2e50c441276c h1:VAx3LRNjVNvjtgO7KFRuT/3aye/0zJvwn01rHSfoolo=
github.com/kevinburke/ssh_config v0.0.0-20190630040420-2e50c441276c/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM=
github.com/kevinburke/ssh_config v1.1.0 h1:pH/t1WS9NzT8go394IqZeJTMHVm6Cr6ZJ6AQ+mdNo/o=
github.com/kevinburke/ssh_config v1.1.0/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM=
github.com/konsorten/go-windows-terminal-sequences v1.0.3 h1:CE8S1cTafDpPvMhIxNJKvHsGVBgn1xWYf1NbHQhywc8=
github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
Expand Down
76 changes: 43 additions & 33 deletions tunnel/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -66,12 +66,12 @@ func (r SSHConfigFile) Get(host string) *SSHHost {
user = ""
}

localForward, err := r.getForward("LocalForward", host)
localForwards, err := r.getForwards("LocalForward", host)
if err != nil {
log.Warningf("error reading local forwarding configuration from ssh config file: %v", err)
}

remoteForward, err := r.getForward("RemoteForward", host)
remoteForwards, err := r.getForwards("RemoteForward", host)
if err != nil {
log.Warningf("error reading remote configuration from ssh config file: %v", err)
}
Expand All @@ -84,13 +84,13 @@ func (r SSHConfigFile) Get(host string) *SSHHost {
}

return &SSHHost{
Hostname: hostname,
Port: port,
User: user,
Key: key,
IdentityAgent: identityAgent,
LocalForward: localForward,
RemoteForward: remoteForward,
Hostname: hostname,
Port: port,
User: user,
Key: key,
IdentityAgent: identityAgent,
LocalForwards: localForwards,
RemoteForwards: remoteForwards,
}
}

Expand All @@ -103,34 +103,44 @@ func (r SSHConfigFile) getHostname(host string) string {
return hostname
}

func (r SSHConfigFile) getForward(forwardType, host string) (*ForwardConfig, error) {
c, err := r.sshConfig.Get(host, forwardType)
func (r SSHConfigFile) getForwards(forwardType, host string) ([]*ForwardConfig, error) {
fwds, err := r.sshConfig.GetAll(host, forwardType)
if err != nil {
return nil, err
}

if c == "" {
return nil, nil
}
forwards := []*ForwardConfig{}

for _, c := range fwds {
if c == "" {
continue
}

l := strings.Fields(c)
l := strings.Fields(c)

if len(l) < 2 {
return nil, fmt.Errorf("malformed forwarding configuration on ssh config file: %s", l)
}
if len(l) < 2 {
return nil, fmt.Errorf("malformed forwarding configuration on ssh config file: %s", l)
}

source := l[0]
destination := l[1]
source := l[0]
destination := l[1]

if strings.HasPrefix(source, ":") {
source = fmt.Sprintf("127.0.0.1%s", source)
if strings.HasPrefix(source, ":") {
source = fmt.Sprintf("127.0.0.1%s", source)
}

if source != "" && !strings.Contains(source, ":") {
source = fmt.Sprintf("127.0.0.1:%s", source)
}

forwards = append(forwards, &ForwardConfig{Source: source, Destination: destination})
}

if source != "" && !strings.Contains(source, ":") {
source = fmt.Sprintf("127.0.0.1:%s", source)
if len(forwards) == 0 {
return nil, nil
}

return &ForwardConfig{Source: source, Destination: destination}, nil
return forwards, nil

}

Expand All @@ -154,18 +164,18 @@ func (r SSHConfigFile) getKey(host string) string {

// SSHHost represents a host configuration extracted from a ssh config file.
type SSHHost struct {
Hostname string
Port string
User string
Key string
IdentityAgent string
LocalForward *ForwardConfig
RemoteForward *ForwardConfig
Hostname string
Port string
User string
Key string
IdentityAgent string
LocalForwards []*ForwardConfig
RemoteForwards []*ForwardConfig
}

// String returns a string representation of a SSHHost.
func (h SSHHost) String() string {
return fmt.Sprintf("[hostname=%s, port=%s, user=%s, key=%s, identity_agent=%s, local_forward=%s, remote_forward=%s]", h.Hostname, h.Port, h.User, h.Key, h.IdentityAgent, h.LocalForward, h.RemoteForward)
return fmt.Sprintf("[hostname=%s, port=%s, user=%s, key=%s, identity_agent=%s, local_forward=%v, remote_forward=%v]", h.Hostname, h.Port, h.User, h.Key, h.IdentityAgent, h.LocalForwards, h.RemoteForwards)
}

// ForwardConfig represents either a LocalForward or a RemoteForward configuration
Expand Down
55 changes: 29 additions & 26 deletions tunnel/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,11 @@ Host example2
LocalForward 8080 127.0.0.1:8080
Host example3
LocalForward 9090 127.0.0.1:9090
LocalForward 9091 127.0.0.1:9091
Host example4
RemoteForward 80 127.0.0.1:8080
Host example5
RemoteForward 192.168.1.100:80 my-server:8080

`

c, _ := ssh_config.Decode(strings.NewReader(config))
Expand All @@ -37,51 +37,54 @@ Host example5
{
"example1",
&SSHHost{
Hostname: "172.17.0.1",
Port: "3306",
User: "john",
Key: "/path/.ssh/id_rsa",
LocalForward: nil,
Hostname: "172.17.0.1",
Port: "3306",
User: "john",
Key: "/path/.ssh/id_rsa",
LocalForwards: nil,
},
},
{
"example2",
&SSHHost{
Hostname: "",
Port: "",
User: "",
Key: "",
LocalForward: &ForwardConfig{Source: "127.0.0.1:8080", Destination: "127.0.0.1:8080"},
Hostname: "",
Port: "",
User: "",
Key: "",
LocalForwards: []*ForwardConfig{&ForwardConfig{Source: "127.0.0.1:8080", Destination: "127.0.0.1:8080"}},
},
},
{
"example3",
&SSHHost{
Hostname: "",
Port: "",
User: "",
Key: "",
LocalForward: &ForwardConfig{Source: "127.0.0.1:9090", Destination: "127.0.0.1:9090"},
Hostname: "",
Port: "",
User: "",
Key: "",
LocalForwards: []*ForwardConfig{
&ForwardConfig{Source: "127.0.0.1:9090", Destination: "127.0.0.1:9090"},
&ForwardConfig{Source: "127.0.0.1:9091", Destination: "127.0.0.1:9091"},
},
},
},
{
"example4",
&SSHHost{
Hostname: "",
Port: "",
User: "",
Key: "",
RemoteForward: &ForwardConfig{Source: "127.0.0.1:80", Destination: "127.0.0.1:8080"},
Hostname: "",
Port: "",
User: "",
Key: "",
RemoteForwards: []*ForwardConfig{&ForwardConfig{Source: "127.0.0.1:80", Destination: "127.0.0.1:8080"}},
},
},
{
"example5",
&SSHHost{
Hostname: "",
Port: "",
User: "",
Key: "",
RemoteForward: &ForwardConfig{Source: "192.168.1.100:80", Destination: "my-server:8080"},
Hostname: "",
Port: "",
User: "",
Key: "",
RemoteForwards: []*ForwardConfig{&ForwardConfig{Source: "192.168.1.100:80", Destination: "my-server:8080"}},
},
},
}
Expand Down
7 changes: 7 additions & 0 deletions tunnel/testdata/dotssh/config
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,10 @@ Host hostWithLocalForward
User mole_test
IdentityFile ~/.ssh/id_rsa

Host hostWithTwoLocalForwards
Hostname 127.0.0.1
Port 2222
LocalForward 8080 172.17.0.1:8080
LocalForward 9090 172.17.0.1:9090
User mole_test
IdentityFile ~/.ssh/id_rsa
22 changes: 13 additions & 9 deletions tunnel/tunnel.go
Original file line number Diff line number Diff line change
Expand Up @@ -583,13 +583,17 @@ func buildSSHChannels(serverName, channelType string, source, destination []stri
// if source and destination were not given, try to find the addresses from the
// SSH configuration file.
if len(source) == 0 && len(destination) == 0 {
f, err := getForward(channelType, serverName, cfgPath)
fwds, err := getForwards(channelType, serverName, cfgPath)
if err != nil {
return nil, err
}

source = []string{f.Source}
destination = []string{f.Destination}
source = []string{}
destination = []string{}
for _, f := range fwds {
source = append(source, f.Source)
destination = append(destination, f.Destination)
}
} else {

lSize := len(source)
Expand Down Expand Up @@ -640,8 +644,8 @@ func buildSSHChannels(serverName, channelType string, source, destination []stri
return channels, nil
}

func getForward(channelType, serverName string, cfgPath string) (*ForwardConfig, error) {
var f *ForwardConfig
func getForwards(channelType, serverName string, cfgPath string) ([]*ForwardConfig, error) {
var fwds []*ForwardConfig

cfg, err := NewSSHConfigFile(cfgPath)
if err != nil {
Expand All @@ -651,16 +655,16 @@ func getForward(channelType, serverName string, cfgPath string) (*ForwardConfig,
sh := cfg.Get(serverName)

if channelType == "local" {
f = sh.LocalForward
fwds = sh.LocalForwards
} else if channelType == "remote" {
f = sh.RemoteForward
fwds = sh.RemoteForwards
} else {
return nil, fmt.Errorf("could not retrieve forwarding information from ssh configuration file: unsupported channel type %s", channelType)
}

if f == nil {
if fwds == nil {
return nil, fmt.Errorf("forward config could not be found or has invalid syntax for host %s", serverName)
}

return f, nil
return fwds, nil
}
8 changes: 8 additions & 0 deletions tunnel/tunnel_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -337,6 +337,14 @@ func TestBuildSSHChannels(t *testing.T) {
expected: 1,
expectedError: nil,
},
{
serverName: "hostWithTwoLocalForwards",
source: []string{},
destination: []string{},
config: "testdata/.ssh/config",
expected: 2,
expectedError: nil,
},
{
serverName: "test",
source: []string{":3360", ":8080"},
Expand Down