Skip to content

Commit 046468a

Browse files
authored
Insecure mode (#52)
Insecure mode This change adds an insecure flag that dictates the implementation of the host key validation callback.
1 parent 656f581 commit 046468a

File tree

5 files changed

+99
-42
lines changed

5 files changed

+99
-42
lines changed

cli/cli.go

+18-15
Original file line numberDiff line numberDiff line change
@@ -15,20 +15,21 @@ type App struct {
1515
args []string
1616
flag *flag.FlagSet
1717

18-
Command string
19-
Local HostInput
20-
Remote HostInput
21-
Server HostInput
22-
Key string
23-
Verbose bool
24-
Help bool
25-
Version bool
26-
Alias string
27-
Start string
28-
AliasDelete bool
29-
Detach bool
30-
Stop string
31-
AliasList bool
18+
Command string
19+
Local HostInput
20+
Remote HostInput
21+
Server HostInput
22+
Key string
23+
Verbose bool
24+
Help bool
25+
Version bool
26+
Alias string
27+
Start string
28+
AliasDelete bool
29+
Detach bool
30+
Stop string
31+
AliasList bool
32+
InsecureMode bool
3233
}
3334

3435
// New creates a new instance of App.
@@ -55,6 +56,8 @@ func (c *App) Parse() error {
5556
f.BoolVar(&c.Version, "version", false, "display the mole version")
5657
f.BoolVar(&c.Detach, "detach", false, "(optional) run process in background")
5758
f.StringVar(&c.Stop, "stop", "", "stop background process")
59+
f.BoolVar(&c.InsecureMode, "insecure", false, "(optional) skip host key validation when connecting to ssh server")
60+
5861
f.Parse(c.args[1:])
5962

6063
if c.Help {
@@ -110,7 +113,7 @@ func (c App) Validate() error {
110113
// use the tool.
111114
func (c *App) PrintUsage() {
112115
fmt.Fprintf(os.Stderr, "%s\n\n", `usage:
113-
mole [-v] [-detach] [-local [<host>]:<port>] -remote [<host>]:<port> -server [<user>@]<host>[:<port>] [-key <key_path>]
116+
mole [-v] [-insecure] [-detach] [-local [<host>]:<port>] -remote [<host>]:<port> -server [<user>@]<host>[:<port>] [-key <key_path>]
114117
mole -alias <alias_name> [-v] [-local [<host>]:<port>] -remote [<host>]:<port> -server [<user>@]<host>[:<port>] [-key <key_path>]
115118
mole -alias <alias_name> -delete
116119
mole -start <alias_name>

cmd/mole/main.go

+2
Original file line numberDiff line numberDiff line change
@@ -214,6 +214,8 @@ func start(app cli.App) error {
214214
return err
215215
}
216216

217+
s.Insecure = app.InsecureMode
218+
217219
log.Debugf("server: %s", s)
218220

219221
t := tunnel.New(app.Local.String(), s, app.Remote.String())

test-env/README.md

+3-3
Original file line numberDiff line numberDiff line change
@@ -83,9 +83,9 @@ The ssh authentication key files, `test-env/key` and `test-env/key,pub` will
8383
```sh
8484
$ make test-env
8585
<lots of output messages here>
86-
$ mole -remote 192.168.33.11:80 -server [email protected]:22122 -key test-env/key
87-
INFO[0000] listening on local address local_address="127.0.0.1:50640"
88-
$ curl 127.0.0.1:50640
86+
$ mole -insecure -local :21112 -remote 192.168.33.11:80 -server [email protected]:22122 -key test-env/key
87+
INFO[0000] listening on local address local_address="127.0.0.1:21112"
88+
$ curl 127.0.0.1:21112
8989
:)
9090
```
9191

tunnel/tunnel.go

+19-9
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@ type Server struct {
2626
Address string
2727
User string
2828
Key string
29+
// Insecure is a flag to indicate if the host keys should be validated.
30+
Insecure bool
2931
}
3032

3133
// NewServer creates a new instance of Server using $HOME/.ssh/config to
@@ -257,7 +259,7 @@ func sshClientConfig(server Server) (*ssh.ClientConfig, error) {
257259
return nil, err
258260
}
259261

260-
callback, err := knownHostsCallback()
262+
clb, err := knownHostsCallback(server.Insecure)
261263
if err != nil {
262264
return nil, err
263265
}
@@ -267,7 +269,7 @@ func sshClientConfig(server Server) (*ssh.ClientConfig, error) {
267269
Auth: []ssh.AuthMethod{
268270
ssh.PublicKeys(signer),
269271
},
270-
HostKeyCallback: callback,
272+
HostKeyCallback: clb,
271273
Timeout: 3 * time.Second,
272274
}, nil
273275
}
@@ -279,17 +281,25 @@ func copyConn(writer, reader net.Conn) {
279281
}
280282
}
281283

282-
func knownHostsCallback() (ssh.HostKeyCallback, error) {
283-
knownHostFile := filepath.Join(os.Getenv("HOME"), ".ssh", "known_hosts")
284+
func knownHostsCallback(insecure bool) (ssh.HostKeyCallback, error) {
285+
var clb func(hostname string, remote net.Addr, key ssh.PublicKey) error
284286

285-
log.Debugf("known_hosts file used: %s", knownHostFile)
287+
if insecure {
288+
clb = func(hostname string, remote net.Addr, key ssh.PublicKey) error {
289+
return nil
290+
}
291+
} else {
292+
var err error
293+
knownHostFile := filepath.Join(os.Getenv("HOME"), ".ssh", "known_hosts")
294+
log.Debugf("known_hosts file used: %s", knownHostFile)
286295

287-
callback, err := knownhosts.New(knownHostFile)
288-
if err != nil {
289-
return nil, fmt.Errorf("error while parsing 'known_hosts' file: %s: %v", knownHostFile, err)
296+
clb, err = knownhosts.New(knownHostFile)
297+
if err != nil {
298+
return nil, fmt.Errorf("error while parsing 'known_hosts' file: %s: %v", knownHostFile, err)
299+
}
290300
}
291301

292-
return callback, nil
302+
return clb, nil
293303
}
294304

295305
func reconcileHostname(givenHostname, resolvedHostname string) string {

tunnel/tunnel_test.go

+57-15
Original file line numberDiff line numberDiff line change
@@ -166,9 +166,10 @@ func TestTunnelOptions(t *testing.T) {
166166

167167
}
168168

169+
//TODO teardown the tunnel
169170
func TestTunnel(t *testing.T) {
170171
expected := "ABC"
171-
tun := prepareTunnel(t)
172+
tun := prepareTunnel(t, false)
172173

173174
select {
174175
case <-tun.Ready:
@@ -191,6 +192,37 @@ func TestTunnel(t *testing.T) {
191192
if expected != response {
192193
t.Errorf("expected: %s, value: %s", expected, response)
193194
}
195+
196+
tun.Stop()
197+
}
198+
199+
func TestInsecureTunnel(t *testing.T) {
200+
expected := "ABC"
201+
tun := prepareTunnel(t, true)
202+
203+
select {
204+
case <-tun.Ready:
205+
t.Log("tunnel is ready to accept connections")
206+
case <-time.After(1 * time.Second):
207+
t.Errorf("no connection after a while")
208+
return
209+
}
210+
211+
resp, err := http.Get(fmt.Sprintf("http://%s/%s", tun.listener.Addr(), expected))
212+
if err != nil {
213+
t.Errorf("error while making local connection: %v", err)
214+
return
215+
}
216+
defer resp.Body.Close()
217+
218+
body, _ := ioutil.ReadAll(resp.Body)
219+
response := string(body)
220+
221+
if expected != response {
222+
t.Errorf("expected: %s, value: %s", expected, response)
223+
}
224+
225+
tun.Stop()
194226
}
195227

196228
func TestRandomLocalPort(t *testing.T) {
@@ -244,13 +276,18 @@ func TestMain(m *testing.M) {
244276

245277
// prepareTunnel creates a Tunnel object making sure all infrastructure
246278
// dependencies (ssh and http servers) are ready.
247-
func prepareTunnel(t *testing.T) *Tunnel {
248-
sshAddr := createSSHServer(keyPath)
249-
generateKnownHosts(sshAddr.String(), publicKeyPath, knownHostsPath)
250-
s, _ := NewServer("mole", sshAddr.String(), "")
279+
func prepareTunnel(t *testing.T, insecure bool) *Tunnel {
280+
ssh := createSSHServer(keyPath)
281+
srv, _ := NewServer("mole", ssh.Addr().String(), "")
251282

252-
httpAddr := createWebServer()
253-
tun := &Tunnel{local: "127.0.0.1:0", server: s, remote: httpAddr.String(), done: make(chan error), Ready: make(chan bool, 1)}
283+
srv.Insecure = insecure
284+
285+
if !insecure {
286+
generateKnownHosts(ssh.Addr().String(), publicKeyPath, knownHostsPath)
287+
}
288+
289+
web := createWebServer()
290+
tun := &Tunnel{local: "127.0.0.1:0", server: srv, remote: web.Addr().String(), done: make(chan error), Ready: make(chan bool, 1)}
254291

255292
go func(t *testing.T) {
256293
err := tun.Start()
@@ -322,19 +359,24 @@ func get(client http.Client, resource string) (string, error) {
322359
//
323360
// Example: If the request URI is /this-is-a-test, the response will be
324361
// this-is-a-test
325-
func createWebServer() net.Addr {
362+
func createWebServer() net.Listener {
326363

327364
handler := func(w http.ResponseWriter, r *http.Request) {
328365
fmt.Fprintf(w, r.URL.Path[1:])
329366
}
330-
http.HandleFunc("/", handler)
367+
368+
mux := http.NewServeMux()
369+
mux.HandleFunc("/", handler)
370+
371+
server := &http.Server{
372+
Handler: mux,
373+
}
374+
331375
l, _ := net.Listen("tcp", "127.0.0.1:0")
332376

333-
go func(l net.Listener) {
334-
http.Serve(l, nil)
335-
}(l)
377+
go server.Serve(l)
336378

337-
return l.Addr()
379+
return l
338380
}
339381

340382
// createSSHServer starts a SSH server that authenticates connections using
@@ -347,7 +389,7 @@ func createWebServer() net.Addr {
347389
// References:
348390
// https://gist.github.com/jpillora/b480fde82bff51a06238
349391
// https://tools.ietf.org/html/rfc4254#section-7.2
350-
func createSSHServer(keyPath string) net.Addr {
392+
func createSSHServer(keyPath string) net.Listener {
351393
conf := &ssh.ServerConfig{
352394
PublicKeyCallback: func(conn ssh.ConnMetadata, key ssh.PublicKey) (*ssh.Permissions, error) {
353395
return &ssh.Permissions{}, nil
@@ -398,7 +440,7 @@ func createSSHServer(keyPath string) net.Addr {
398440
}
399441
}(l)
400442

401-
return l.Addr()
443+
return l
402444
}
403445

404446
// generateKnownHosts creates a new "known_hosts" file on a given path with a

0 commit comments

Comments
 (0)