Skip to content

Commit

Permalink
support known hosts options #50
Browse files Browse the repository at this point in the history
UserKnownHostsFile
GlobalKnownHostsFile
  • Loading branch information
lonnywong committed Oct 29, 2023
1 parent 4df90e1 commit a71ac3a
Show file tree
Hide file tree
Showing 4 changed files with 77 additions and 54 deletions.
5 changes: 3 additions & 2 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,15 @@ require (
github.com/chzyer/readline v1.5.1
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510
github.com/mattn/go-isatty v0.0.20
github.com/mitchellh/go-homedir v1.1.0
github.com/skeema/knownhosts v1.2.1
github.com/stretchr/testify v1.8.4
github.com/trzsz/go-arg v1.5.2
github.com/trzsz/iterm2 v0.1.0
github.com/trzsz/npipe v0.1.0
github.com/trzsz/promptui v0.10.3
github.com/trzsz/ssh_config v1.3.3
github.com/trzsz/trzsz-go v1.1.6
github.com/trzsz/trzsz-go v1.1.7-0.20231028133930-ab9d3ad3d61d
golang.org/x/crypto v0.14.0
golang.org/x/sys v0.13.0
golang.org/x/term v0.13.0
Expand All @@ -31,7 +32,7 @@ require (
github.com/dchest/jsmin v0.0.0-20220218165748-59f39799265f // indirect
github.com/gorilla/websocket v1.5.0 // indirect
github.com/josephspurrier/goversioninfo v1.4.0 // indirect
github.com/klauspost/compress v1.17.1 // indirect
github.com/klauspost/compress v1.17.2 // indirect
github.com/ncruces/zenity v0.10.10 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/randall77/makefat v0.0.0-20210315173500-7ddd0e42c844 // indirect
Expand Down
10 changes: 6 additions & 4 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -32,10 +32,12 @@ github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWm
github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/josephspurrier/goversioninfo v1.4.0 h1:Puhl12NSHUSALHSuzYwPYQkqa2E1+7SrtAPJorKK0C8=
github.com/josephspurrier/goversioninfo v1.4.0/go.mod h1:JWzv5rKQr+MmW+LvM412ToT/IkYDZjaclF2pKDss8IY=
github.com/klauspost/compress v1.17.1 h1:NE3C767s2ak2bweCZo3+rdP4U/HoyVXLv/X9f2gPS5g=
github.com/klauspost/compress v1.17.1/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE=
github.com/klauspost/compress v1.17.2 h1:RlWWUY/Dr4fL8qk9YG7DTZ7PDgME2V4csBXA8L/ixi4=
github.com/klauspost/compress v1.17.2/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE=
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
github.com/ncruces/zenity v0.10.10 h1:V/rtAhr5QLdDThahOkm7EYlnw4RuEsf7oN+Xb6lz1j0=
github.com/ncruces/zenity v0.10.10/go.mod h1:k3k4hJ4Wt1MUbeV48y+Gbl7Fp9skfGszN/xtKmuvhZk=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
Expand All @@ -59,8 +61,8 @@ github.com/trzsz/promptui v0.10.3 h1:uhcLQsLZqMxEtGiYoeM2lR/Hd4pSxoYsd2eFctH8MCs
github.com/trzsz/promptui v0.10.3/go.mod h1:GMZtu6ZTzU73CBFkzGtmB4wnTROIAbv4GFA74fV8V8g=
github.com/trzsz/ssh_config v1.3.3 h1:FodC5NBpnBSI2CJqU62d0Ya3yTUHWDpbWKugKX0LXZs=
github.com/trzsz/ssh_config v1.3.3/go.mod h1:Dl1okTjVVfsrtTA8nqkJ1OnjiCrZY6DUEI2DGT2/YoQ=
github.com/trzsz/trzsz-go v1.1.6 h1:Bg3jnW4trxI/jJx94ahOeiHC/WpME42eF40zTuGrN5o=
github.com/trzsz/trzsz-go v1.1.6/go.mod h1:sOLnYeA77qyF2Aqj+leycDWxFpylV46BA7g6F3gWcfQ=
github.com/trzsz/trzsz-go v1.1.7-0.20231028133930-ab9d3ad3d61d h1:71sbYXSPC1lCae25jEwYjO2qhSF4KsojtbCwD5+91Jg=
github.com/trzsz/trzsz-go v1.1.7-0.20231028133930-ab9d3ad3d61d/go.mod h1:sOLnYeA77qyF2Aqj+leycDWxFpylV46BA7g6F3gWcfQ=
go.uber.org/goleak v1.2.1 h1:NBol2c7O1ZokfZ0LEU9K6Whx/KnwvepVetCUhtKja4A=
golang.org/x/crypto v0.14.0 h1:wBqGXzWJW6m1XrIKlAH0Hs1JJ7+9KBwnIO8v66Q9cHc=
golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4=
Expand Down
9 changes: 8 additions & 1 deletion tssh/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ import (
"strings"
"sync"

"github.com/mitchellh/go-homedir"
"github.com/trzsz/ssh_config"
)

Expand Down Expand Up @@ -151,7 +152,13 @@ func initUserConfig(configFile string) error {
var err error
userHomeDir, err = os.UserHomeDir()
if err != nil {
return fmt.Errorf("user home dir failed: %v", err)
debug("user home dir failed: %v", err)
if userHomeDir, err = homedir.Dir(); err != nil {
debug("obtain home dir failed: %v", err)
}
}
if userHomeDir == "" {
warning("Failed to obtain the home directory. Using the current directory as the home directory.")
}

if configFile != "" {
Expand Down
107 changes: 60 additions & 47 deletions tssh/login.go
Original file line number Diff line number Diff line change
Expand Up @@ -179,15 +179,6 @@ func getLoginParam(args *sshArgs) (*loginParam, error) {
return param, nil
}

func createKnownHosts(path string) error {
file, err := os.OpenFile(path, os.O_CREATE, 0600)
if err != nil {
return fmt.Errorf("create [%s] failed: %v", path, err)
}
defer file.Close()
return nil
}

func addHostKey(path, host string, remote net.Addr, key ssh.PublicKey) error {
fingerprint := ssh.FingerprintSHA256(key)
fmt.Fprintf(os.Stderr, "The authenticity of host '%s' can't be established.\r\n"+
Expand Down Expand Up @@ -237,46 +228,69 @@ func addHostKey(path, host string, remote net.Addr, key ssh.PublicKey) error {
return nil
}

var getHostKeyCallback = func() func() (ssh.HostKeyCallback, knownhosts.HostKeyCallback, error) {
var err error
var once sync.Once
var cb ssh.HostKeyCallback
var kh knownhosts.HostKeyCallback
return func() (ssh.HostKeyCallback, knownhosts.HostKeyCallback, error) {
once.Do(func() {
path := filepath.Join(userHomeDir, ".ssh", "known_hosts")
if err = createKnownHosts(path); err != nil {
return
func getHostKeyCallback(args *sshArgs) (ssh.HostKeyCallback, knownhosts.HostKeyCallback, error) {
primaryPath := ""
var files []string
userFile := getOptionConfig(args, "UserKnownHostsFile")
if userFile != "" && strings.ToLower(userFile) != "none" {
for _, path := range strings.Fields(userFile) {
path = resolveHomeDir(path)
if primaryPath == "" {
primaryPath = path
}
kh, err = knownhosts.New(path)
if err != nil {
err = fmt.Errorf("new knownhosts [%s] failed: %v", path, err)
return
if isFileExist(path) {
files = append(files, path)
debug("add UserKnownHostsFile: %s", path)
} else {
debug("UserKnownHostsFile [%s] does not exist", path)
}
cb = func(host string, remote net.Addr, key ssh.PublicKey) error {
err := kh(host, remote, key)
if knownhosts.IsHostKeyChanged(err) {
fmt.Fprintf(os.Stderr, "\033[0;31m@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@\r\n"+
"@ WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED! @\r\n"+
"@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@\r\n"+
"IT IS POSSIBLE THAT SOMEONE IS DOING SOMETHING NASTY!\r\n"+
"Someone could be eavesdropping on you right now (man-in-the-middle attack)!\033[0m\r\n"+
"It is also possible that a host key has just been changed.\r\n"+
"The fingerprint for the %s key sent by the remote host is\r\n"+
"%s\r\n"+
"Please contact your system administrator.\r\n"+
"Add correct host key in %s to get rid of this message.\r\n",
key.Type(), ssh.FingerprintSHA256(key), path)
return err
} else if knownhosts.IsHostUnknown(err) {
return addHostKey(path, host, remote, key)
}
return err
}
}
globalFile := getOptionConfig(args, "GlobalKnownHostsFile")
if globalFile != "" {
for _, path := range strings.Fields(globalFile) {
path = resolveHomeDir(path)
if isFileExist(path) {
files = append(files, path)
debug("add GlobalKnownHostsFile: %s", path)
} else {
debug("GlobalKnownHostsFile [%s] does not exist", path)
}
})
return cb, kh, err
}
}
}()

kh, err := knownhosts.New(files...)
if err != nil {
return nil, nil, fmt.Errorf("new knownhosts failed: %v", err)
}

cb := func(host string, remote net.Addr, key ssh.PublicKey) error {
err := kh(host, remote, key)
if knownhosts.IsHostKeyChanged(err) {
path := primaryPath
if path == "" {
path = "~/.ssh/known_hosts"
}
fmt.Fprintf(os.Stderr, "\033[0;31m@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@\r\n"+
"@ WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED! @\r\n"+
"@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@\r\n"+
"IT IS POSSIBLE THAT SOMEONE IS DOING SOMETHING NASTY!\r\n"+
"Someone could be eavesdropping on you right now (man-in-the-middle attack)!\033[0m\r\n"+
"It is also possible that a host key has just been changed.\r\n"+
"The fingerprint for the %s key sent by the remote host is\r\n"+
"%s\r\n"+
"Please contact your system administrator.\r\n"+
"Add correct host key in %s to get rid of this message.\r\n",
key.Type(), ssh.FingerprintSHA256(key), path)
return err
} else if knownhosts.IsHostUnknown(err) && primaryPath != "" {
return addHostKey(primaryPath, host, remote, key)
}
return err
}

return cb, kh, err
}

type sshSigner struct {
path string
Expand Down Expand Up @@ -710,7 +724,7 @@ func sshConnect(args *sshArgs, client *ssh.Client, proxy string) (*ssh.Client, b
}

authMethods := getAuthMethods(args, param.host, param.user)
cb, kh, err := getHostKeyCallback()
cb, kh, err := getHostKeyCallback(args)
if err != nil {
return nil, false, err
}
Expand Down Expand Up @@ -856,7 +870,6 @@ func sshLogin(args *sshArgs, tty bool) (client *ssh.Client, session *ssh.Session

cleanupAfterLogined = append(cleanupAfterLogined, func() {
getDefaultSigners = nil
getHostKeyCallback = nil
})

// ssh login
Expand Down

0 comments on commit a71ac3a

Please sign in to comment.