Skip to content

Commit 5cf1998

Browse files
authored
Merge pull request #4 from yaronsumel/feature/CVE-2017-3204-fix
CVE-2017-3204-fix: known_hosts validation support
2 parents bbc1357 + 8950054 commit 5cf1998

10 files changed

Lines changed: 78 additions & 39 deletions

File tree

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
.idea/

README.md

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,20 @@
11
# grapes [![Go Report Card](https://goreportcard.com/badge/github.com/yaronsumel/grapes)](https://goreportcard.com/report/github.com/yaronsumel/grapes) [![Build Status](https://travis-ci.org/yaronsumel/grapes.svg?branch=master)](https://travis-ci.org/yaronsumel/grapes) [![Build status](https://ci.appveyor.com/api/projects/status/fnepp81rdi8prawn/branch/master?svg=true)](https://ci.appveyor.com/project/yaronsumel/grapes/branch/master) [![GoDoc](https://godoc.org/github.com/yaronsumel/grapes?status.svg)](https://godoc.org/github.com/yaronsumel/grapes)
22

3-
> ## Warning : grapes currently DOES NOT validate the host key during the handshake ( CVE-2017-3204 ). Next version of grapes will support full host key validation.
4-
53
grapes is lightweight tool designed to distribute commands over ssh with ease.
64

5+
### Update (25/04/2019)
6+
7+
Handshake validation is now in place in order to fix `CVE-2017-3204`, The validation will use the built-in fingerprint list `~/.ssh/known_hosts` as default.
8+
9+
In order to add your ssh server fingerprint to `known_hosts` run the following:
10+
11+
$ ssh-keyscan -H YOURHOST.COM >> ~/.ssh/known_hosts
12+
713
### Installation ###
814

9-
Run (golang v1.8+ required):
15+
Run (golang v1.10+ required):
1016

11-
$ go get -u github.com/yaronsumel/grapes
17+
$ export GO111MODULE=on; go get -u github.com/yaronsumel/grapes
1218

1319
### Usage ###
1420

go.mod

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ module github.com/yaronsumel/graps
22

33
require (
44
github.com/mitchellh/go-homedir v1.1.0
5-
golang.org/x/crypto v0.0.0-20190131182504-b8fe1690c613
6-
golang.org/x/sys v0.0.0-20190203050204-7ae0202eb74c // indirect
5+
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2
76
gopkg.in/yaml.v2 v2.2.2
87
)

go.sum

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
22
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
3-
golang.org/x/crypto v0.0.0-20190131182504-b8fe1690c613 h1:MQ/ZZiDsUapFFiMS+vzwXkCTeEKaum+Do5rINYJDmxc=
4-
golang.org/x/crypto v0.0.0-20190131182504-b8fe1690c613/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
5-
golang.org/x/sys v0.0.0-20190203050204-7ae0202eb74c h1:YeMXU0KQqExdpG959DFhAhfpY8myIsnfqj8lhNFRzzE=
6-
golang.org/x/sys v0.0.0-20190203050204-7ae0202eb74c/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
3+
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2 h1:VklqNMn3ovrHsnt90PveolxSbWFaJdECFbxSq0Mqo2M=
4+
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
5+
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a h1:1BGLXjeY4akVXGgbC9HugT3Jv3hCI0z56oJR5vAMgBU=
6+
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
77
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
88
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
99
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=

grape.go

Lines changed: 23 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,13 @@ package main
22

33
import (
44
"fmt"
5-
"gopkg.in/yaml.v2"
5+
"log"
66
"sync"
7+
8+
"github.com/mitchellh/go-homedir"
9+
"golang.org/x/crypto/ssh"
10+
"golang.org/x/crypto/ssh/knownhosts"
11+
"gopkg.in/yaml.v2"
712
)
813

914
type (
@@ -46,22 +51,34 @@ func newGrape(input *input) *grape {
4651
return &app
4752
}
4853

49-
func (app *grape) run() {
54+
func (app *grape) run(knownHostsPath string) {
55+
56+
knownHostsPath, err := homedir.Expand(knownHostsPath)
57+
if err != nil {
58+
log.Fatal(err)
59+
}
60+
61+
hostKeyCallback, err := knownhosts.New(knownHostsPath)
62+
if err != nil {
63+
log.Fatal(err)
64+
}
65+
5066
for _, server := range app.servers {
5167
if app.input.asyncFlag {
5268
wg.Add(1)
53-
go app.runOnServer(server, &wg)
69+
go app.runOnServer(server, hostKeyCallback, &wg)
5470
} else {
55-
app.runOnServer(server, nil)
71+
app.runOnServer(server, hostKeyCallback, nil)
5672
}
5773
}
5874
if app.input.asyncFlag {
5975
wg.Wait()
6076
}
6177
}
6278

63-
func (app *grape) runOnServer(server server, wg *sync.WaitGroup) {
64-
client, err := app.ssh.newClient(server)
79+
func (app *grape) runOnServer(server server, hostKeyCallback ssh.HostKeyCallback, wg *sync.WaitGroup) {
80+
81+
client, err := app.ssh.newClient(server, hostKeyCallback)
6582
if err != nil {
6683
server.Fatal = err.Error()
6784
} else {

grape_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -110,9 +110,9 @@ func TestRun(t *testing.T) {
110110
},
111111
}
112112
app.input.asyncFlag = true
113-
app.run()
113+
app.run("testFiles/known_hosts")
114114
app.input.asyncFlag = false
115-
app.run()
115+
app.run("testFiles/known_hosts")
116116
}
117117

118118
func TestPrintOutput(t *testing.T) {

main.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import (
66
)
77

88
const (
9-
version = "0.2.2"
9+
version = "0.3.0"
1010
welcome = `
1111
// ____ __________ _____ ___ _____
1212
// / __ / ___/ __ / __ \/ _ \/ ___/
@@ -33,5 +33,5 @@ func main() {
3333

3434
app.verifyAction()
3535

36-
app.run()
36+
app.run("~/.ssh/known_hosts")
3737
}

ssh.go

Lines changed: 5 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ import (
55
"errors"
66
"fmt"
77
"io/ioutil"
8-
"net"
98

109
"golang.org/x/crypto/ssh"
1110
)
@@ -36,7 +35,7 @@ func (gSSH *grapeSSH) newError(errMsg string) sshError {
3635
func (gSSH *grapeSSH) setKey(keyPath keyPath) sshError {
3736
privateBytes, err := ioutil.ReadFile(string(keyPath))
3837
if err != nil {
39-
return gSSH.newError("Could not open idendity file.")
38+
return gSSH.newError("Could not open identity file.")
4039
}
4140
privateKey, err := ssh.ParsePrivateKey(privateBytes)
4241
if err != nil {
@@ -46,21 +45,16 @@ func (gSSH *grapeSSH) setKey(keyPath keyPath) sshError {
4645
return nil
4746
}
4847

49-
func (gSSH *grapeSSH) newClient(server server) (*grapeSSHClient, sshError) {
48+
func (gSSH *grapeSSH) newClient(server server, hostKeyCallback ssh.HostKeyCallback) (*grapeSSHClient, sshError) {
5049
client, err := ssh.Dial("tcp", server.Host, &ssh.ClientConfig{
5150
User: server.User,
5251
Auth: []ssh.AuthMethod{
5352
ssh.PublicKeys(gSSH.keySigner),
5453
},
55-
// CVE-2017-3204
56-
// "fix" for now : InsecureIgnoreHostKey
57-
// gonna validate host key in the next version of grapes
58-
HostKeyCallback: func(hostname string, remote net.Addr, key ssh.PublicKey) error {
59-
return nil
60-
},
54+
HostKeyCallback: hostKeyCallback,
6155
})
6256
if err != nil {
63-
return nil, gSSH.newError("Could not establish ssh connection")
57+
return nil, gSSH.newError(err.Error() + " - could not establish ssh connection")
6458
}
6559
return &grapeSSHClient{client}, nil
6660
}
@@ -71,7 +65,7 @@ func (client *grapeSSHClient) execCommand(cmd command) *sshOutput {
7165
}
7266
session, err := client.NewSession()
7367
if err != nil {
74-
output.Std.Err = "Could not establish ssh session"
68+
output.Std.Err = "could not establish ssh session"
7569
} else {
7670
var stderr, stdout bytes.Buffer
7771
session.Stdout, session.Stderr = &stdout, &stderr

ssh_test.go

Lines changed: 28 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ package main
22

33
import (
44
"testing"
5+
6+
"golang.org/x/crypto/ssh/knownhosts"
57
)
68

79
func TestSetKey(t *testing.T) {
@@ -25,11 +27,17 @@ func TestNewClient(t *testing.T) {
2527
User: "new",
2628
Name: "public",
2729
}
28-
if _, err := gSSH.newClient(s); err != nil {
30+
31+
hostKeyCallback, err := knownhosts.New("testFiles/known_hosts")
32+
if err != nil {
33+
t.FailNow()
34+
}
35+
36+
if _, err := gSSH.newClient(s, hostKeyCallback); err != nil {
2937
t.FailNow()
3038
}
3139
s.Host = "localhost"
32-
if _, err := gSSH.newClient(s); err == nil {
40+
if _, err := gSSH.newClient(s, hostKeyCallback); err == nil {
3341
t.FailNow()
3442
}
3543
}
@@ -45,17 +53,23 @@ func TestExecCommand(t *testing.T) {
4553
User: "new",
4654
Name: "public",
4755
}
48-
client, err = gSSH.newClient(s)
56+
hostKeyCallback, err := knownhosts.New("testFiles/known_hosts")
4957
if err != nil {
50-
t.FailNow()
58+
t.Fatal(err)
59+
}
60+
61+
client, err = gSSH.newClient(s, hostKeyCallback)
62+
if err != nil {
63+
t.Fatal(err)
5164
}
52-
output = client.execCommand("echo")
65+
66+
output = client.execCommand("ls -al")
5367
if output.Std.Err == "could not establish ssh session" {
5468
t.FailNow()
5569
}
5670
// make that panic
5771
client.Close()
58-
output = client.execCommand("echo")
72+
output = client.execCommand("ls -al")
5973
if output.Std.Err != "could not establish ssh session" {
6074
t.FailNow()
6175
}
@@ -76,11 +90,17 @@ func TestExecCommands(t *testing.T) {
7690
User: "new",
7791
Name: "public",
7892
}
79-
client, err = gSSH.newClient(s)
80-
defer client.Close()
93+
94+
hostKeyCallback, err := knownhosts.New("testFiles/known_hosts")
8195
if err != nil {
8296
t.FailNow()
8397
}
98+
99+
client, err = gSSH.newClient(s, hostKeyCallback)
100+
if err != nil {
101+
t.FailNow()
102+
}
103+
defer client.Close()
84104
output := client.execCommands(demoCommands)
85105
for _, v := range output {
86106
if v.Std.Err == "could not establish ssh session" {

testFiles/known_hosts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
|1|uetecC7VBOynrr2sE7ZTSsgY26k=|L/s5sdGvRJgmsp1NECld0Ch6mlM= ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC1BBSaTBrKjpyDmIPt1t9oPvZpIxzm8D2kB/eLUoqXP9Eoicb3niMP8kWv1vhZQPGiQQ/q6sg+c92Bkk0ZiJTW9JdMJuaIfk+VDjGUV1US432mzq4581CFk/Q0HeHehrrAoqCKTcnp65/9UlnP3ljnyS1J0JE40YtjLIXfAeoJJKfKvrMhc1nOqn7NEMcmN8gY5ELr5R5ZUml0CsmurMDeIoeP9Ukf0PAc6uznv7JpL5GY+Eee8xXd8ehClZUY9kkvm6a6LIzPXtG5KXIYbGQkMrcDqWAdYOb2whfOICRbRLu16T8r30NjdHRjDq2kAH7upip4FqWGj96k+8ZmjA3f
2+
|1|Xfc8gDn9tAkrYMeF/FUn29o0tNU=|9UtkdQpQKdsaq6/U0UEzBfoC1jY= ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIJJk3a190w/1TZkzVKORvz/kwyKmFY144lVeDFm80p17

0 commit comments

Comments
 (0)