Compare commits

...

12 commits

Author SHA1 Message Date
Feng_Qi
3f32c3a325
Merge pull request #21 from TieWay59/fix-test-ssh
Fix:  not enough arguments in call to connect
2023-03-11 22:37:27 +08:00
TieWay59
cfc416503f doc: 增加 Test_SSH 函数描述 2023-03-11 20:43:51 +08:00
TieWay59
fa8ecd9a33 fix: 补齐 connect 函数缺失的参数数量
采用 nil 作为 `keyExchangeList` 从而提供默认的行为
2023-03-11 20:37:40 +08:00
Feng_Qi
53850621a5 README VERSION 0.4.0
更新 readme 和 example
2022-02-11 15:50:19 +08:00
Feng_Qi
770df7122e VERSION 0.4.0
支持在 sshHost 中配置 ciphers 和 keyExchanges 参数,以更好的适配不同的服务器环境
2022-02-11 15:26:40 +08:00
Feng_Qi
f369e796bf VERSION 0.3.0 2021-10-09 00:03:45 +08:00
Feng_Qi
8a6a859950
Merge pull request #12 from Dravening/master
fix:Adapt to some certain types of devices(switch).
2020-09-30 14:27:52 +08:00
Dravening
c7a73670a6
fix:Adapt to some certain types of devices(switch). 2020-08-11 16:29:23 +08:00
Feng_Qi
e704825aa9 0.2.3 2018-07-03 13:55:49 +08:00
Feng_Qi
de1b1f2b59 readme 2018-04-09 10:08:13 +08:00
Feng_Qi
82bd4c6577 version 0.2.2
add filelocate
2018-04-09 10:06:45 +08:00
Feng_Qi
360caeb82e README 2018-01-20 11:02:05 +08:00
11 changed files with 153 additions and 92 deletions

1
.gitignore vendored
View file

@ -2,3 +2,4 @@
*.json *.json
*.exe *.exe
*.key *.key
*.zip

View file

@ -187,7 +187,7 @@
same "printed page" as the copyright notice for easier same "printed page" as the copyright notice for easier
identification within third-party archives. identification within third-party archives.
Copyright [2017] [shanghai-edu] Copyright [2017] [shanghai-edu & ECNU]
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.

View file

@ -36,16 +36,22 @@ Usage of ./multissh:
cmdfile path cmdfile path
-cmds string -cmds string
cmds cmds
-f string
write file locate
-hostfile string -hostfile string
hostfile path hostfile path
-hosts string -hosts string
host address list host address list
-ipfile string -ipfile string
hostfile path ipfile path
-ips string
ip address list
-j print output in json format -j print output in json format
-k string -k string
ssh private key ssh private key
-l In linux mode,multi command combine with && ,such as date&&cd /opt&&ls (default true) -keyexchanges string
keyexchanges
-l In linux mode,multi command combine with && ,such as date&&cd /opt&&ls
-n int -n int
max execute number (default 20) max execute number (default 20)
-outTxt -outTxt
@ -78,13 +84,12 @@ show clock
**ssh.json 示例** **ssh.json 示例**
``` ```
{ {
"SshHosts": [ "SshHosts": [{
{
"Host": "192.168.31.51", "Host": "192.168.31.51",
"Port": 22, "Port": 22,
"Username": "admin", "Username": "admin",
"Password": "admin", "Password": "admin",
"cmds":"show clock;show clock" "cmds": "show clock;show clock"
}, },
{ {
"Host": "192.168.80.131", "Host": "192.168.80.131",
@ -95,7 +100,12 @@ show clock
"linuxMode": true, "linuxMode": true,
"CmdFile": "cmd2.txt.example" "CmdFile": "cmd2.txt.example"
} }
] ],
"Global": {
"Ciphers": "aes128-ctr,aes192-ctr,aes256-ctr,aes128-cbc,3des-cbc",
"KeyExchanges": "diffie-hellman-group1-sha1,curve25519-sha256@libssh.org,ecdh-sha2-nistp256,ecdh-sha2-nistp384,ecdh-sha2-nistp521,diffie-hellman-group-exchange-sha256,diffie-hellman-group14-sha1"
}
} }
``` ```

View file

@ -3,7 +3,7 @@ package funcs
import ( import (
"bytes" "bytes"
// "os" // "os"
//"strings" "strings"
"testing" "testing"
) )
@ -16,10 +16,13 @@ const (
key = "../server.key" key = "../server.key"
) )
/* // Tests the SSH functionality of the package.
//
// It requires manual input of the local SSH private key path into the key
// variable, and the remote address into the ip variable.
func Test_SSH(t *testing.T) { func Test_SSH(t *testing.T) {
var cipherList []string var cipherList []string
session, err := connect(username, password, ip, key, port, cipherList) session, err := connect(username, password, ip, key, port, cipherList, nil)
if err != nil { if err != nil {
t.Error(err) t.Error(err)
return return
@ -51,8 +54,8 @@ func Test_SSH(t *testing.T) {
t.Log((outbt.String() + errbt.String())) t.Log((outbt.String() + errbt.String()))
return return
} }
*/
/*
func Test_SSH_run(t *testing.T) { func Test_SSH_run(t *testing.T) {
var cipherList []string var cipherList []string
session, err := connect(username, password, ip, key, port, cipherList) session, err := connect(username, password, ip, key, port, cipherList)
@ -77,3 +80,4 @@ func Test_SSH_run(t *testing.T) {
return return
} }
*/

View file

@ -14,7 +14,7 @@ import (
"golang.org/x/crypto/ssh" "golang.org/x/crypto/ssh"
) )
func connect(user, password, host, key string, port int, cipherList []string) (*ssh.Session, error) { func connect(user, password, host, key string, port int, cipherList, keyExchangeList []string) (*ssh.Session, error) {
var ( var (
auth []ssh.AuthMethod auth []ssh.AuthMethod
addr string addr string
@ -45,15 +45,16 @@ func connect(user, password, host, key string, port int, cipherList []string) (*
} }
auth = append(auth, ssh.PublicKeys(signer)) auth = append(auth, ssh.PublicKeys(signer))
} }
if len(cipherList) == 0 { if len(cipherList) == 0 {
config = ssh.Config{ config.Ciphers = []string{"aes128-ctr", "aes192-ctr", "aes256-ctr", "aes128-gcm@openssh.com", "arcfour256", "arcfour128", "aes128-cbc", "3des-cbc", "aes192-cbc", "aes256-cbc"}
Ciphers: []string{"aes128-ctr", "aes192-ctr", "aes256-ctr", "aes128-gcm@openssh.com", "arcfour256", "arcfour128", "aes128-cbc", "3des-cbc", "aes192-cbc", "aes256-cbc"},
}
} else { } else {
config = ssh.Config{ config.Ciphers = cipherList
Ciphers: cipherList,
} }
if len(keyExchangeList) == 0 {
config.KeyExchanges = []string{"diffie-hellman-group-exchange-sha1", "diffie-hellman-group1-sha1", "diffie-hellman-group-exchange-sha256"}
} else {
config.KeyExchanges = keyExchangeList
} }
clientConfig = &ssh.ClientConfig{ clientConfig = &ssh.ClientConfig{
@ -91,12 +92,12 @@ func connect(user, password, host, key string, port int, cipherList []string) (*
return session, nil return session, nil
} }
func Dossh(username, password, host, key string, cmdlist []string, port, timeout int, cipherList []string, linuxMode bool, ch chan g.SSHResult) { func Dossh(username, password, host, key string, cmdlist []string, port, timeout int, cipherList, keyExchangeList []string, linuxMode bool, ch chan g.SSHResult) {
chSSH := make(chan g.SSHResult) chSSH := make(chan g.SSHResult)
if linuxMode { if linuxMode {
go dossh_run(username, password, host, key, cmdlist, port, cipherList, chSSH) go dossh_run(username, password, host, key, cmdlist, port, cipherList, keyExchangeList, chSSH)
} else { } else {
go dossh_session(username, password, host, key, cmdlist, port, cipherList, chSSH) go dossh_session(username, password, host, key, cmdlist, port, cipherList, keyExchangeList, chSSH)
} }
var res g.SSHResult var res g.SSHResult
@ -112,8 +113,8 @@ func Dossh(username, password, host, key string, cmdlist []string, port, timeout
return return
} }
func dossh_session(username, password, host, key string, cmdlist []string, port int, cipherList []string, ch chan g.SSHResult) { func dossh_session(username, password, host, key string, cmdlist []string, port int, cipherList, keyExchangeList []string, ch chan g.SSHResult) {
session, err := connect(username, password, host, key, port, cipherList) session, err := connect(username, password, host, key, port, cipherList, keyExchangeList)
var sshResult g.SSHResult var sshResult g.SSHResult
sshResult.Host = host sshResult.Host = host
@ -158,8 +159,8 @@ func dossh_session(username, password, host, key string, cmdlist []string, port
return return
} }
func dossh_run(username, password, host, key string, cmdlist []string, port int, cipherList []string, ch chan g.SSHResult) { func dossh_run(username, password, host, key string, cmdlist []string, port int, cipherList, keyExchangeList []string, ch chan g.SSHResult) {
session, err := connect(username, password, host, key, port, cipherList) session, err := connect(username, password, host, key, port, cipherList, keyExchangeList)
var sshResult g.SSHResult var sshResult g.SSHResult
sshResult.Host = host sshResult.Host = host

View file

@ -27,6 +27,12 @@ type SSHHost struct {
type HostJson struct { type HostJson struct {
SshHosts []SSHHost SshHosts []SSHHost
Global GlobalConfig
}
type GlobalConfig struct {
Ciphers string
KeyExchanges string
} }
type SSHResult struct { type SSHResult struct {
@ -36,6 +42,9 @@ type SSHResult struct {
} }
func SplitString(str string) (strList []string) { func SplitString(str string) (strList []string) {
if str == "" {
return
}
if strings.Contains(str, ",") { if strings.Contains(str, ",") {
strList = strings.Split(str, ",") strList = strings.Split(str, ",")
} else { } else {
@ -72,20 +81,22 @@ func Getfile(filePath string) ([]string, error) {
} }
//gu //gu
func GetJsonFile(filePath string) ([]SSHHost, error) { func GetJsonFile(filePath string) (HostJson, error) {
result := []SSHHost{} var result HostJson
b, err := ioutil.ReadFile(filePath) b, err := ioutil.ReadFile(filePath)
if err != nil { if err != nil {
log.Println("read file ", filePath, err) log.Println("read file ", filePath, err)
return result, err return result, err
} }
var m HostJson err = json.Unmarshal(b, &result)
json.Unmarshal(b, &m) if err != nil {
result = m.SshHosts log.Println("read file ", filePath, err)
return result, err
}
return result, nil return result, nil
} }
func WriteIntoTxt(sshResult SSHResult) error { func WriteIntoTxt(sshResult SSHResult, locate string) error {
outputFile, outputError := os.OpenFile(sshResult.Host+".txt", os.O_WRONLY|os.O_CREATE, 0666) outputFile, outputError := os.OpenFile(locate+sshResult.Host+".txt", os.O_WRONLY|os.O_CREATE, 0666)
if outputError != nil { if outputError != nil {
return outputError return outputError
} }

View file

@ -5,6 +5,9 @@ package g
// 0.1.2 fix ssh error on h3c switch // 0.1.2 fix ssh error on h3c switch
// 0.2 // 0.2
// 0.2.1 // 0.2.1
// add write locate file
// json Unmarshal with error
// 0.2.3
const ( const (
VERSION = "0.2.1" VERSION = "0.4.0"
) )

7
go.mod Normal file
View file

@ -0,0 +1,7 @@
module github.com/shanghai-edu/multissh
go 1.17
require golang.org/x/crypto v0.0.0-20210921155107-089bfa567519
require golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1 // indirect

10
go.sum Normal file
View file

@ -0,0 +1,10 @@
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519 h1:7I4JAnoQBe7ZtJcBaYHi5UtiO8tQHbUSXxL+pnGRANg=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1 h1:SrN+KX8Art/Sf4HNj6Zcz06G7VEz+7w9tdXTPOZ7+l4=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1 h1:v+OssWQX+hTHEmOBgwxdZxK4zHq3yOs8F9J7mk0PY8E=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=

22
main.go
View file

@ -23,19 +23,21 @@ func main() {
key := flag.String("k", "", "ssh private key") key := flag.String("k", "", "ssh private key")
port := flag.Int("port", 22, "ssh port") port := flag.Int("port", 22, "ssh port")
ciphers := flag.String("ciphers", "", "ciphers") ciphers := flag.String("ciphers", "", "ciphers")
keyExchanges := flag.String("keyexchanges", "", "keyexchanges")
cmdFile := flag.String("cmdfile", "", "cmdfile path") cmdFile := flag.String("cmdfile", "", "cmdfile path")
hostFile := flag.String("hostfile", "", "hostfile path") hostFile := flag.String("hostfile", "", "hostfile path")
ipFile := flag.String("ipfile", "", "ipfile path") ipFile := flag.String("ipfile", "", "ipfile path")
cfgFile := flag.String("c", "", "cfg File Path") cfgFile := flag.String("c", "", "cfg File Path")
jsonMode := flag.Bool("j", false, "print output in json format") jsonMode := flag.Bool("j", false, "print output in json format")
outTxt := flag.Bool("outTxt", false, "write result into txt") outTxt := flag.Bool("outTxt", false, "write result into txt")
fileLocate := flag.String("f", "", "write file locate")
linuxMode := flag.Bool("l", false, "In linux mode,multi command combine with && ,such as date&&cd /opt&&ls") linuxMode := flag.Bool("l", false, "In linux mode,multi command combine with && ,such as date&&cd /opt&&ls")
timeLimit := flag.Int("t", 30, "max timeout") timeLimit := flag.Int("t", 30, "max timeout")
numLimit := flag.Int("n", 20, "max execute number") numLimit := flag.Int("n", 20, "max execute number")
flag.Parse() flag.Parse()
var cmdList, hostList, cipherList []string var cmdList, hostList, cipherList, keyExchangeList []string
var err error var err error
sshHosts := []g.SSHHost{} sshHosts := []g.SSHHost{}
@ -87,6 +89,9 @@ func main() {
if *ciphers != "" { if *ciphers != "" {
cipherList = g.SplitString(*ciphers) cipherList = g.SplitString(*ciphers)
} }
if *keyExchanges != "" {
keyExchangeList = g.SplitString(*keyExchanges)
}
if *cfgFile == "" { if *cfgFile == "" {
for _, host := range hostList { for _, host := range hostList {
host_Struct.Host = host host_Struct.Host = host
@ -99,11 +104,14 @@ func main() {
sshHosts = append(sshHosts, host_Struct) sshHosts = append(sshHosts, host_Struct)
} }
} else { } else {
sshHosts, err = g.GetJsonFile(*cfgFile) sshHostConfig, err := g.GetJsonFile(*cfgFile)
if err != nil { if err != nil {
log.Println("load cfgFile error: ", err) log.Println("load cfgFile error: ", err)
return return
} }
cipherList = g.SplitString(sshHostConfig.Global.Ciphers)
keyExchangeList = g.SplitString(sshHostConfig.Global.KeyExchanges)
sshHosts = sshHostConfig.SshHosts
for i := 0; i < len(sshHosts); i++ { for i := 0; i < len(sshHosts); i++ {
if sshHosts[i].Cmds != "" { if sshHosts[i].Cmds != "" {
sshHosts[i].CmdList = g.SplitString(sshHosts[i].Cmds) sshHosts[i].CmdList = g.SplitString(sshHosts[i].Cmds)
@ -123,7 +131,7 @@ func main() {
startTime := time.Now() startTime := time.Now()
log.Println("Multissh start") log.Println("Multissh start")
limitFunc := func(chLimit chan bool, ch chan g.SSHResult, host g.SSHHost) { limitFunc := func(chLimit chan bool, ch chan g.SSHResult, host g.SSHHost) {
funcs.Dossh(host.Username, host.Password, host.Host, host.Key, host.CmdList, host.Port, *timeLimit, cipherList, host.LinuxMode, ch) funcs.Dossh(host.Username, host.Password, host.Host, host.Key, host.CmdList, host.Port, *timeLimit, cipherList, keyExchangeList, host.LinuxMode, ch)
<-chLimit <-chLimit
} }
for i, host := range sshHosts { for i, host := range sshHosts {
@ -143,12 +151,13 @@ func main() {
//gu //gu
if *outTxt { if *outTxt {
for _, sshResult := range sshResults { for _, sshResult := range sshResults {
err = g.WriteIntoTxt(sshResult) err = g.WriteIntoTxt(sshResult, *fileLocate)
if err != nil { if err != nil {
log.Println("write into txt error: ", err) log.Println("write into txt error: ", err)
return return
} }
} }
return
} }
if *jsonMode { if *jsonMode {
jsonResult, err := json.Marshal(sshResults) jsonResult, err := json.Marshal(sshResults)
@ -156,11 +165,12 @@ func main() {
log.Println("json Marshal error: ", err) log.Println("json Marshal error: ", err)
} }
fmt.Println(string(jsonResult)) fmt.Println(string(jsonResult))
} else { return
}
for _, sshResult := range sshResults { for _, sshResult := range sshResults {
fmt.Println("host: ", sshResult.Host) fmt.Println("host: ", sshResult.Host)
fmt.Println("========= Result =========") fmt.Println("========= Result =========")
fmt.Println(sshResult.Result) fmt.Println(sshResult.Result)
} }
}
} }

View file

@ -1,11 +1,10 @@
{ {
"SshHosts": [ "SshHosts": [{
{
"Host": "192.168.31.51", "Host": "192.168.31.51",
"Port": 22, "Port": 22,
"Username": "admin", "Username": "admin",
"Password": "admin", "Password": "admin",
"cmds":"show clock;show clock" "cmds": "show clock;show clock"
}, },
{ {
"Host": "192.168.80.131", "Host": "192.168.80.131",
@ -16,5 +15,10 @@
"linuxMode": true, "linuxMode": true,
"CmdFile": "cmd2.txt.example" "CmdFile": "cmd2.txt.example"
} }
] ],
"Global": {
"Ciphers": "aes128-ctr,aes192-ctr,aes256-ctr,aes128-cbc,3des-cbc",
"KeyExchanges": "diffie-hellman-group1-sha1,curve25519-sha256@libssh.org,ecdh-sha2-nistp256,ecdh-sha2-nistp384,ecdh-sha2-nistp521,diffie-hellman-group-exchange-sha256,diffie-hellman-group14-sha1"
}
} }