mirror of
https://github.com/shanghai-edu/multissh.git
synced 2025-12-16 13:27:44 +00:00
Compare commits
14 commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3f32c3a325 | ||
|
|
cfc416503f | ||
|
|
fa8ecd9a33 | ||
|
|
53850621a5 | ||
|
|
770df7122e | ||
|
|
f369e796bf | ||
|
|
8a6a859950 | ||
|
|
c7a73670a6 | ||
|
|
e704825aa9 | ||
|
|
de1b1f2b59 | ||
|
|
82bd4c6577 | ||
|
|
360caeb82e | ||
|
|
378c12a285 | ||
|
|
87e207932f |
16 changed files with 744 additions and 337 deletions
2
.gitignore
vendored
2
.gitignore
vendored
|
|
@ -1,3 +1,5 @@
|
|||
*.txt
|
||||
*.json
|
||||
*.exe
|
||||
*.key
|
||||
*.zip
|
||||
2
LICENSE
2
LICENSE
|
|
@ -187,7 +187,7 @@
|
|||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [2017] [shanghai-edu]
|
||||
Copyright [2017] [shanghai-edu & ECNU]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
|
|
|
|||
326
README.MD
326
README.MD
|
|
@ -2,6 +2,15 @@
|
|||
|
||||
一个简单的并行 SSH 工具,可以批量的对主机通过 SSH 执行命令组合。
|
||||
|
||||
支持:
|
||||
- 并发执行
|
||||
- 单次执行多条命令
|
||||
- ip 地址段自动匹配主机(192.168.0.1-192.168.0.100)
|
||||
- ssh 用户名/密码认证
|
||||
- ssh key 认证
|
||||
- json 格式输出
|
||||
- 输出到文本,文件名为 host.txt
|
||||
|
||||
#### 编译
|
||||
```
|
||||
go get ./...
|
||||
|
|
@ -17,33 +26,45 @@ https://github.com/shanghai-edu/multissh/releases/
|
|||
|
||||
#### 命令体系
|
||||
```
|
||||
./multissh -h
|
||||
-cmd string
|
||||
cmds // 需要执行的命令组合,多条命令以 ; 分割
|
||||
# ./multissh -h
|
||||
Usage of ./multissh:
|
||||
-c string
|
||||
cfg File Path
|
||||
-ciphers string
|
||||
ciphers
|
||||
-cmdfile string
|
||||
cmdfile path //需要执行的命令组合文件,文件内命令按行分割
|
||||
cmdfile path
|
||||
-cmds string
|
||||
cmds
|
||||
-f string
|
||||
write file locate
|
||||
-hostfile string
|
||||
hostfile path // 需要执行的主机列表文件,主机列表在文件内按行分割
|
||||
hostfile path
|
||||
-hosts string
|
||||
host address list //需要执行的主机列表,多个主机以 ; 分割
|
||||
host address list
|
||||
-ipfile string
|
||||
hostfile path //需要执行的主机(IP)列表文件,IP可以以地址段的方式逐行写在文本内
|
||||
-p string
|
||||
password // 主机的 SSH 密码
|
||||
-port int
|
||||
ssh port (default 22) //主机的 SSH 端口,默认 22
|
||||
-u string
|
||||
username //主机的 SSH 用户名
|
||||
-j string
|
||||
jsonFile //保存大量主机,包括主机地址,SSH用户名,SSH密码,SSH端口,所需执行的cmd指令文件地址
|
||||
-outTxt bool
|
||||
outTxt (default false) //是否允许把结果保存到文件中,文件名为 ssh 连接的主机名(host 或 ip),true为允许 false为默认值
|
||||
-t int
|
||||
timeLimit (default 30) //单个 ssh 会话的最大时间,超过时间命令未执行完则超时 默认为30s
|
||||
ipfile path
|
||||
-ips string
|
||||
ip address list
|
||||
-j print output in json format
|
||||
-k string
|
||||
ssh private key
|
||||
-keyexchanges string
|
||||
keyexchanges
|
||||
-l In linux mode,multi command combine with && ,such as date&&cd /opt&&ls
|
||||
-n int
|
||||
numLimit (default 20) //最大并发访问量 默认为20
|
||||
-v show version
|
||||
|
||||
max execute number (default 20)
|
||||
-outTxt
|
||||
write result into txt
|
||||
-p string
|
||||
password
|
||||
-port int
|
||||
ssh port (default 22)
|
||||
-t int
|
||||
max timeout (default 30)
|
||||
-u string
|
||||
username
|
||||
-v show version
|
||||
```
|
||||
**cmdfile 示例**
|
||||
```
|
||||
|
|
@ -51,111 +72,246 @@ show clock
|
|||
```
|
||||
**hostfile 示例**
|
||||
```
|
||||
192.168.15.101
|
||||
192.168.31.21
|
||||
192.168.15.102
|
||||
```
|
||||
**ipfile 示例**
|
||||
```
|
||||
192.168.15.101-192.168.15.102
|
||||
192.168.15.101-192.168.15.103
|
||||
192.168.31.21-192.168.31.22
|
||||
```
|
||||
|
||||
**ssh.json 示例**
|
||||
```
|
||||
{
|
||||
"SshHosts": [
|
||||
{
|
||||
"Host": "192.168.15.101",
|
||||
"Port": 22,
|
||||
"Username": "admin",
|
||||
"Password": "admin",
|
||||
"CmdFile": "cmd1.txt.example"
|
||||
},
|
||||
{
|
||||
"Host": "192.168.83.40",
|
||||
"Port": 22,
|
||||
"Username": "root",
|
||||
"Password": "root",
|
||||
"CmdFile": "cmd2.txt.example"
|
||||
}
|
||||
]
|
||||
"SshHosts": [{
|
||||
"Host": "192.168.31.51",
|
||||
"Port": 22,
|
||||
"Username": "admin",
|
||||
"Password": "admin",
|
||||
"cmds": "show clock;show clock"
|
||||
},
|
||||
{
|
||||
"Host": "192.168.80.131",
|
||||
"Port": 22,
|
||||
"Username": "root",
|
||||
"Password": "",
|
||||
"key": "./server.key",
|
||||
"linuxMode": true,
|
||||
"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"
|
||||
}
|
||||
|
||||
}
|
||||
```
|
||||
|
||||
## 用法
|
||||
#### cmd string & host string
|
||||
```
|
||||
./multissh -cmd "show clock" -hosts "192.168.15.101;192.168.15.102" -u admin -p admin
|
||||
# ./multissh -cmds "show clock" -hosts "192.168.31.21;192.168.15.102" -u admin -p password
|
||||
2018/01/17 14:01:28 Multissh start
|
||||
2018/01/17 14:01:31 Multissh finished. Process time 2.867808673s. Number of active ip is 2
|
||||
host: 192.168.31.21
|
||||
========= Result =========
|
||||
|
||||
192.168.15.101 ssh start
|
||||
sw-1#show clock
|
||||
05:26:40.649 UTC Tue Jun 6 2017
|
||||
sw-1#exit
|
||||
******************************************************************************
|
||||
* Copyright (c) 2004-2016 Hangzhou H3C Tech. Co., Ltd. All rights reserved. *
|
||||
* Without the owner's prior written consent, *
|
||||
* no decompiling or reverse-engineering shall be allowed. *
|
||||
******************************************************************************
|
||||
|
||||
192.168.15.101 ssh end
|
||||
<sw-h3c>show clock
|
||||
14:01:31 CN Wed 01/17/2018
|
||||
Time Zone : CN add 08:00:00
|
||||
<sw-h3c>exit
|
||||
|
||||
192.168.15.102 ssh start
|
||||
sw-2#show clock
|
||||
05:24:38.708 UTC Tue Jun 6 2017
|
||||
sw-2#exit
|
||||
host: 192.168.15.102
|
||||
========= Result =========
|
||||
|
||||
sw-cisco#show clock
|
||||
05:50:24.935 UTC Wed Jan 17 2018
|
||||
sw-cisco#exit
|
||||
|
||||
192.168.15.102 ssh end
|
||||
```
|
||||
|
||||
#### cmdfile & hostfile
|
||||
```
|
||||
./multissh -cmdfile cmd.txt -hostfile host.txt -u admin -p admin
|
||||
# ./multissh -cmdfile cmd1.txt.example -hostfile host.txt.example -u admin -p password
|
||||
2018/01/17 14:01:28 Multissh start
|
||||
2018/01/17 14:01:31 Multissh finished. Process time 2.867808673s. Number of active ip is 2
|
||||
host: 192.168.31.21
|
||||
========= Result =========
|
||||
|
||||
192.168.15.101 ssh start
|
||||
sw-1#show clock
|
||||
05:29:43.269 UTC Tue Jun 6 2017
|
||||
sw-1#exit
|
||||
******************************************************************************
|
||||
* Copyright (c) 2004-2016 Hangzhou H3C Tech. Co., Ltd. All rights reserved. *
|
||||
* Without the owner's prior written consent, *
|
||||
* no decompiling or reverse-engineering shall be allowed. *
|
||||
******************************************************************************
|
||||
|
||||
192.168.15.101 ssh end
|
||||
<sw-h3c>show clock
|
||||
14:01:31 CN Wed 01/17/2018
|
||||
Time Zone : CN add 08:00:00
|
||||
<sw-h3c>exit
|
||||
|
||||
192.168.15.102 ssh start
|
||||
sw-2#show clock
|
||||
05:27:41.332 UTC Tue Jun 6 2017
|
||||
sw-2#exit
|
||||
host: 192.168.15.102
|
||||
========= Result =========
|
||||
|
||||
sw-cisco#show clock
|
||||
05:50:24.935 UTC Wed Jan 17 2018
|
||||
sw-cisco#exit
|
||||
|
||||
192.168.15.102 ssh end
|
||||
```
|
||||
|
||||
#### ipfile
|
||||
```
|
||||
./multissh -cmdfile cmd.txt -ipfile ip.txt -u admin -p admin
|
||||
# ./multissh -cmdfile cmd1.txt.example -ipfile ip.txt.example -u admin -p password
|
||||
2018/01/17 14:25:26 Multissh start
|
||||
2018/01/17 14:25:29 Multissh finished. Process time 2.847347642s. Number of active ip is 5
|
||||
host: 192.168.15.101
|
||||
========= Result =========
|
||||
|
||||
192.168.15.101 ssh start
|
||||
sw-1#show clock
|
||||
05:29:43.269 UTC Tue Jun 6 2017
|
||||
sw-1#exit
|
||||
sw-cisco-1#show clock
|
||||
06:17:49.422 UTC Wed Jan 17 2018
|
||||
sw-cisco-1#exit
|
||||
|
||||
192.168.15.101 ssh end
|
||||
host: 192.168.15.102
|
||||
========= Result =========
|
||||
sw-cisco-2#show clock
|
||||
06:14:22.445 UTC Wed Jan 17 2018
|
||||
sw-cisco-2#exit
|
||||
|
||||
192.168.15.102 ssh start
|
||||
sw-2#show clock
|
||||
05:27:41.332 UTC Tue Jun 6 2017
|
||||
sw-2#exit
|
||||
host: 192.168.15.103
|
||||
========= Result =========
|
||||
sw-cisco-3#show clock
|
||||
06:19:14.487 UTC Wed Jan 17 2018
|
||||
sw-cisco-3#exit
|
||||
|
||||
host: 192.168.31.21
|
||||
========= Result =========
|
||||
|
||||
******************************************************************************
|
||||
* Copyright (c) 2004-2016 Hangzhou H3C Tech. Co., Ltd. All rights reserved. *
|
||||
* Without the owner's prior written consent, *
|
||||
* no decompiling or reverse-engineering shall be allowed. *
|
||||
******************************************************************************
|
||||
|
||||
<sw-h3c>show clock
|
||||
14:25:29 CN Wed 01/17/2018
|
||||
Time Zone : CN add 08:00:00
|
||||
<sw-h3c>exit
|
||||
|
||||
host: 192.168.31.22
|
||||
========= Result =========
|
||||
|
||||
sw-cisco-4#show clock
|
||||
14:25:27.639 beijing Wed Jan 17 2018
|
||||
sw-cisco-4#exit
|
||||
```
|
||||
#### ssh key-based Auth and linuxMode
|
||||
```
|
||||
# ./multissh -hosts "192.168.80.131" -cmds "date;cd /opt;ls" -u root -k "server.key"
|
||||
2018/01/17 14:33:55 Multissh start
|
||||
2018/01/17 14:33:56 Multissh finished. Process time 960.367764ms. Number of active ip is 1
|
||||
host: 192.168.80.131
|
||||
========= Result =========
|
||||
Welcome to Ubuntu 16.04.3 LTS (GNU/Linux 4.4.0-98-generic x86_64)
|
||||
|
||||
* Documentation: https://help.ubuntu.com
|
||||
* Management: https://landscape.canonical.com
|
||||
* Support: https://ubuntu.com/advantage
|
||||
|
||||
System information as of Wed Jan 17 14:33:55 CST 2018
|
||||
|
||||
System load: 0.0 Processes: 335
|
||||
Usage of /: 10.0% of 90.18GB Users logged in: 0
|
||||
Memory usage: 2% IP address for eth0: 192.168.80.131
|
||||
Swap usage: 0% IP address for docker0: 172.17.0.1
|
||||
|
||||
Graph this data and manage this system at:
|
||||
https://landscape.canonical.com/
|
||||
|
||||
0 个可升级软件包。
|
||||
0 个安全更新。
|
||||
|
||||
New release '17.10' available.
|
||||
Run 'do-release-upgrade' to upgrade to it.
|
||||
|
||||
You have new mail.
|
||||
Last login: Wed Jan 17 14:29:39 2018 from 202.120.80.201
|
||||
root@ubuntu-docker-node3:~# 201817:33:56 CST
|
||||
root@ubuntu-docker-node3:~# root@ubuntu-docker-node3:/opt# cisco
|
||||
composer.json
|
||||
composer.phar
|
||||
example-oauth2-server
|
||||
getting-started-with-mmdb
|
||||
gitlab
|
||||
gitlab-ce_8.0.4-ce.1_amd64.deb
|
||||
oauth2-demo-php
|
||||
oauth2-server-php
|
||||
python_test
|
||||
rsyslog-maxminddb
|
||||
root@ubuntu-docker-node3:/opt# 注销
|
||||
|
||||
# ./multissh -hosts "192.168.80.131" -cmds "date;cd /opt;ls" -u root -k "server.key" -l
|
||||
2018/01/17 14:34:02 Multissh start
|
||||
2018/01/17 14:34:02 Multissh finished. Process time 842.465643ms. Number of active ip is 1
|
||||
host: 192.168.80.131
|
||||
========= Result =========
|
||||
201817:34:02 CST
|
||||
cisco
|
||||
composer.json
|
||||
composer.phar
|
||||
example-oauth2-server
|
||||
getting-started-with-mmdb
|
||||
gitlab
|
||||
gitlab-ce_8.0.4-ce.1_amd64.deb
|
||||
oauth2-demo-php
|
||||
oauth2-server-php
|
||||
python_test
|
||||
rsyslog-maxminddb
|
||||
|
||||
192.168.15.102 ssh end
|
||||
```
|
||||
|
||||
#### ssh.json
|
||||
```
|
||||
./multissh -j ssh.json -t 30 -n 20 -outTxt
|
||||
192.168.15.101 ssh start
|
||||
sw-1#show clock
|
||||
05:29:43.269 UTC Tue Jun 6 2017
|
||||
sw-1#exit
|
||||
./multissh -c ssh.json.example
|
||||
2018/01/17 14:29:38 Multissh start
|
||||
2018/01/17 14:29:41 Multissh finished. Process time 2.922928532s. Number of active ip is 2
|
||||
host: 192.168.31.51
|
||||
========= Result =========
|
||||
|
||||
192.168.15.101 ssh end
|
||||
******************************************************************************
|
||||
* Copyright (c) 2004-2016 Hangzhou H3C Tech. Co., Ltd. All rights reserved. *
|
||||
* Without the owner's prior written consent, *
|
||||
* no decompiling or reverse-engineering shall be allowed. *
|
||||
******************************************************************************
|
||||
|
||||
192.168.83.40 ssh start
|
||||
2017年 06月 09日 星期五 09:33:11 CST
|
||||
2017年 06月 09日 星期五 09:33:14 CST
|
||||
192.168.83.40 ssh end
|
||||
<sw-h3c>show clock
|
||||
14:29:41 CN Wed 01/17/2018
|
||||
Time Zone : CN add 08:00:00
|
||||
<WenKe-5F-Stack-2>show clock
|
||||
14:29:41 CN Wed 01/17/2018
|
||||
Time Zone : CN add 08:00:00
|
||||
<WenKe-5F-Stack-2>exit
|
||||
|
||||
host: 192.168.80.131
|
||||
========= Result =========
|
||||
cisco
|
||||
composer.json
|
||||
composer.phar
|
||||
example-oauth2-server
|
||||
getting-started-with-mmdb
|
||||
gitlab
|
||||
gitlab-ce_8.0.4-ce.1_amd64.deb
|
||||
oauth2-demo-php
|
||||
oauth2-server-php
|
||||
python_test
|
||||
rsyslog-maxminddb
|
||||
```
|
||||
#### TODO
|
||||
增加使用证书认证的支持
|
||||
|
||||
#### LICENSE
|
||||
Apache License 2.0
|
||||
|
|
@ -1,3 +1,3 @@
|
|||
date
|
||||
sleep 3
|
||||
date
|
||||
cd /opt
|
||||
sleep 2
|
||||
ls
|
||||
83
funcs/ssh_test.go
Normal file
83
funcs/ssh_test.go
Normal file
|
|
@ -0,0 +1,83 @@
|
|||
package funcs
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
// "os"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
const (
|
||||
username = "root"
|
||||
password = ""
|
||||
ip = "192.168.80.131"
|
||||
port = 22
|
||||
cmd = "cd /opt;pwd;exit"
|
||||
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) {
|
||||
var cipherList []string
|
||||
session, err := connect(username, password, ip, key, port, cipherList, nil)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
defer session.Close()
|
||||
|
||||
cmdlist := strings.Split(cmd, ";")
|
||||
stdinBuf, err := session.StdinPipe()
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
|
||||
var outbt, errbt bytes.Buffer
|
||||
session.Stdout = &outbt
|
||||
|
||||
session.Stderr = &errbt
|
||||
err = session.Shell()
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
for _, c := range cmdlist {
|
||||
c = c + "\n"
|
||||
stdinBuf.Write([]byte(c))
|
||||
|
||||
}
|
||||
session.Wait()
|
||||
t.Log((outbt.String() + errbt.String()))
|
||||
return
|
||||
}
|
||||
|
||||
/*
|
||||
func Test_SSH_run(t *testing.T) {
|
||||
var cipherList []string
|
||||
session, err := connect(username, password, ip, key, port, cipherList)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
defer session.Close()
|
||||
|
||||
//cmdlist := strings.Split(cmd, ";")
|
||||
//newcmd := strings.Join(cmdlist, "&&")
|
||||
var outbt, errbt bytes.Buffer
|
||||
session.Stdout = &outbt
|
||||
|
||||
session.Stderr = &errbt
|
||||
err = session.Run(cmd)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
t.Log((outbt.String() + errbt.String()))
|
||||
|
||||
return
|
||||
}
|
||||
*/
|
||||
201
funcs/sshconnect.go
Normal file
201
funcs/sshconnect.go
Normal file
|
|
@ -0,0 +1,201 @@
|
|||
package funcs
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
|
||||
"net"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/shanghai-edu/multissh/g"
|
||||
"golang.org/x/crypto/ssh"
|
||||
)
|
||||
|
||||
func connect(user, password, host, key string, port int, cipherList, keyExchangeList []string) (*ssh.Session, error) {
|
||||
var (
|
||||
auth []ssh.AuthMethod
|
||||
addr string
|
||||
clientConfig *ssh.ClientConfig
|
||||
client *ssh.Client
|
||||
config ssh.Config
|
||||
session *ssh.Session
|
||||
err error
|
||||
)
|
||||
// get auth method
|
||||
auth = make([]ssh.AuthMethod, 0)
|
||||
if key == "" {
|
||||
auth = append(auth, ssh.Password(password))
|
||||
} else {
|
||||
pemBytes, err := ioutil.ReadFile(key)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var signer ssh.Signer
|
||||
if password == "" {
|
||||
signer, err = ssh.ParsePrivateKey(pemBytes)
|
||||
} else {
|
||||
signer, err = ssh.ParsePrivateKeyWithPassphrase(pemBytes, []byte(password))
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
auth = append(auth, ssh.PublicKeys(signer))
|
||||
}
|
||||
if len(cipherList) == 0 {
|
||||
config.Ciphers = []string{"aes128-ctr", "aes192-ctr", "aes256-ctr", "aes128-gcm@openssh.com", "arcfour256", "arcfour128", "aes128-cbc", "3des-cbc", "aes192-cbc", "aes256-cbc"}
|
||||
} else {
|
||||
config.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{
|
||||
User: user,
|
||||
Auth: auth,
|
||||
Timeout: 30 * time.Second,
|
||||
Config: config,
|
||||
HostKeyCallback: func(hostname string, remote net.Addr, key ssh.PublicKey) error {
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
// connet to ssh
|
||||
addr = fmt.Sprintf("%s:%d", host, port)
|
||||
|
||||
if client, err = ssh.Dial("tcp", addr, clientConfig); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// create session
|
||||
if session, err = client.NewSession(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
modes := ssh.TerminalModes{
|
||||
ssh.ECHO: 0, // disable echoing
|
||||
ssh.TTY_OP_ISPEED: 14400, // input speed = 14.4kbaud
|
||||
ssh.TTY_OP_OSPEED: 14400, // output speed = 14.4kbaud
|
||||
}
|
||||
|
||||
if err := session.RequestPty("xterm", 80, 40, modes); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return session, nil
|
||||
}
|
||||
|
||||
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)
|
||||
if linuxMode {
|
||||
go dossh_run(username, password, host, key, cmdlist, port, cipherList, keyExchangeList, chSSH)
|
||||
} else {
|
||||
go dossh_session(username, password, host, key, cmdlist, port, cipherList, keyExchangeList, chSSH)
|
||||
}
|
||||
var res g.SSHResult
|
||||
|
||||
select {
|
||||
case <-time.After(time.Duration(timeout) * time.Second):
|
||||
res.Host = host
|
||||
res.Success = false
|
||||
res.Result = ("SSH run timeout:" + strconv.Itoa(timeout) + " second.")
|
||||
ch <- res
|
||||
case res = <-chSSH:
|
||||
ch <- res
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
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, keyExchangeList)
|
||||
var sshResult g.SSHResult
|
||||
sshResult.Host = host
|
||||
|
||||
if err != nil {
|
||||
sshResult.Success = false
|
||||
sshResult.Result = fmt.Sprintf("<%s>", err.Error())
|
||||
ch <- sshResult
|
||||
return
|
||||
}
|
||||
defer session.Close()
|
||||
|
||||
cmdlist = append(cmdlist, "exit")
|
||||
|
||||
stdinBuf, _ := session.StdinPipe()
|
||||
|
||||
var outbt, errbt bytes.Buffer
|
||||
session.Stdout = &outbt
|
||||
|
||||
session.Stderr = &errbt
|
||||
err = session.Shell()
|
||||
if err != nil {
|
||||
sshResult.Success = false
|
||||
sshResult.Result = fmt.Sprintf("<%s>", err.Error())
|
||||
ch <- sshResult
|
||||
return
|
||||
}
|
||||
for _, c := range cmdlist {
|
||||
c = c + "\n"
|
||||
stdinBuf.Write([]byte(c))
|
||||
}
|
||||
session.Wait()
|
||||
if errbt.String() != "" {
|
||||
sshResult.Success = false
|
||||
sshResult.Result = errbt.String()
|
||||
ch <- sshResult
|
||||
} else {
|
||||
sshResult.Success = true
|
||||
sshResult.Result = outbt.String()
|
||||
ch <- sshResult
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
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, keyExchangeList)
|
||||
var sshResult g.SSHResult
|
||||
sshResult.Host = host
|
||||
|
||||
if err != nil {
|
||||
sshResult.Success = false
|
||||
sshResult.Result = fmt.Sprintf("<%s>", err.Error())
|
||||
ch <- sshResult
|
||||
return
|
||||
}
|
||||
defer session.Close()
|
||||
|
||||
cmdlist = append(cmdlist, "exit")
|
||||
newcmd := strings.Join(cmdlist, "&&")
|
||||
|
||||
var outbt, errbt bytes.Buffer
|
||||
session.Stdout = &outbt
|
||||
|
||||
session.Stderr = &errbt
|
||||
err = session.Run(newcmd)
|
||||
if err != nil {
|
||||
sshResult.Success = false
|
||||
sshResult.Result = fmt.Sprintf("<%s>", err.Error())
|
||||
ch <- sshResult
|
||||
return
|
||||
}
|
||||
|
||||
if errbt.String() != "" {
|
||||
sshResult.Success = false
|
||||
sshResult.Result = errbt.String()
|
||||
ch <- sshResult
|
||||
} else {
|
||||
sshResult.Success = true
|
||||
sshResult.Result = outbt.String()
|
||||
ch <- sshResult
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package main
|
||||
package g
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
|
|
@ -12,6 +12,47 @@ import (
|
|||
"strings"
|
||||
)
|
||||
|
||||
type SSHHost struct {
|
||||
Host string
|
||||
Port int
|
||||
Username string
|
||||
Password string
|
||||
CmdFile string
|
||||
Cmds string
|
||||
CmdList []string
|
||||
Key string
|
||||
LinuxMode bool
|
||||
Result SSHResult
|
||||
}
|
||||
|
||||
type HostJson struct {
|
||||
SshHosts []SSHHost
|
||||
Global GlobalConfig
|
||||
}
|
||||
|
||||
type GlobalConfig struct {
|
||||
Ciphers string
|
||||
KeyExchanges string
|
||||
}
|
||||
|
||||
type SSHResult struct {
|
||||
Host string
|
||||
Success bool
|
||||
Result string
|
||||
}
|
||||
|
||||
func SplitString(str string) (strList []string) {
|
||||
if str == "" {
|
||||
return
|
||||
}
|
||||
if strings.Contains(str, ",") {
|
||||
strList = strings.Split(str, ",")
|
||||
} else {
|
||||
strList = strings.Split(str, ";")
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func GetfileAll(filePath string) ([]byte, error) {
|
||||
result, err := ioutil.ReadFile(filePath)
|
||||
if err != nil {
|
||||
|
|
@ -40,20 +81,22 @@ func Getfile(filePath string) ([]string, error) {
|
|||
}
|
||||
|
||||
//gu
|
||||
func GetJsonFile(filePath string) ([]SSHHost, error) {
|
||||
result := []SSHHost{}
|
||||
func GetJsonFile(filePath string) (HostJson, error) {
|
||||
var result HostJson
|
||||
b, err := ioutil.ReadFile(filePath)
|
||||
if err != nil {
|
||||
log.Println("read file ", filePath, err)
|
||||
return result, err
|
||||
}
|
||||
var m HostJson
|
||||
json.Unmarshal(b, &m)
|
||||
result = m.SshHosts
|
||||
err = json.Unmarshal(b, &result)
|
||||
if err != nil {
|
||||
log.Println("read file ", filePath, err)
|
||||
return result, err
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
func WriteIntoTxt(sshHost SSHHost) error {
|
||||
outputFile, outputError := os.OpenFile(sshHost.Host+".txt", os.O_WRONLY|os.O_CREATE, 0666)
|
||||
func WriteIntoTxt(sshResult SSHResult, locate string) error {
|
||||
outputFile, outputError := os.OpenFile(locate+sshResult.Host+".txt", os.O_WRONLY|os.O_CREATE, 0666)
|
||||
if outputError != nil {
|
||||
return outputError
|
||||
}
|
||||
|
|
@ -62,13 +105,27 @@ func WriteIntoTxt(sshHost SSHHost) error {
|
|||
outputWriter := bufio.NewWriter(outputFile)
|
||||
//var outputString string
|
||||
|
||||
outputString := sshHost.Result
|
||||
outputString := sshResult.Result
|
||||
outputWriter.WriteString(outputString)
|
||||
outputWriter.Flush()
|
||||
return nil
|
||||
}
|
||||
|
||||
func GetIpList(filePath string) ([]string, error) {
|
||||
func GetIpList(ipString string) ([]string, error) {
|
||||
res := SplitString(ipString)
|
||||
var allIp []string
|
||||
if len(res) > 0 {
|
||||
for _, sip := range res {
|
||||
aip := ParseIp(sip)
|
||||
for _, ip := range aip {
|
||||
allIp = append(allIp, ip)
|
||||
}
|
||||
}
|
||||
}
|
||||
return allIp, nil
|
||||
}
|
||||
|
||||
func GetIpListFromFile(filePath string) ([]string, error) {
|
||||
res, err := Getfile(filePath)
|
||||
if err != nil {
|
||||
return nil, nil
|
||||
13
g/const.go
Normal file
13
g/const.go
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
package g
|
||||
|
||||
// changelog:
|
||||
// 0.1 fisrt version
|
||||
// 0.1.2 fix ssh error on h3c switch
|
||||
// 0.2
|
||||
// 0.2.1
|
||||
// add write locate file
|
||||
// json Unmarshal with error
|
||||
// 0.2.3
|
||||
const (
|
||||
VERSION = "0.4.0"
|
||||
)
|
||||
7
go.mod
Normal file
7
go.mod
Normal 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
10
go.sum
Normal 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=
|
||||
|
|
@ -1,2 +1,2 @@
|
|||
192.168.15.101
|
||||
192.168.15.110
|
||||
192.168.31.21
|
||||
192.168.15.102
|
||||
|
|
@ -1 +1,2 @@
|
|||
192.168.15.101-192.168.15.110
|
||||
192.168.31.21-192.168.31.22
|
||||
157
main.go
157
main.go
|
|
@ -1,155 +1,176 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"flag"
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"time"
|
||||
// "github.com/bitly/go-simplejson"
|
||||
)
|
||||
|
||||
const (
|
||||
VERSION = "0.1.2"
|
||||
"github.com/shanghai-edu/multissh/funcs"
|
||||
"github.com/shanghai-edu/multissh/g"
|
||||
)
|
||||
|
||||
type SSHHost struct {
|
||||
Host string
|
||||
Port int
|
||||
Username string
|
||||
Password string
|
||||
CmdFile string
|
||||
Cmd []string
|
||||
Result string
|
||||
}
|
||||
type HostJson struct {
|
||||
SshHosts []SSHHost
|
||||
}
|
||||
|
||||
func main() {
|
||||
version := flag.Bool("v", false, "show version")
|
||||
hosts := flag.String("hosts", "", "host address list")
|
||||
cmd := flag.String("cmd", "", "cmds")
|
||||
ips := flag.String("ips", "", "ip address list")
|
||||
cmds := flag.String("cmds", "", "cmds")
|
||||
username := flag.String("u", "", "username")
|
||||
password := flag.String("p", "", "password")
|
||||
key := flag.String("k", "", "ssh private key")
|
||||
port := flag.Int("port", 22, "ssh port")
|
||||
ciphers := flag.String("ciphers", "", "ciphers")
|
||||
keyExchanges := flag.String("keyexchanges", "", "keyexchanges")
|
||||
cmdFile := flag.String("cmdfile", "", "cmdfile path")
|
||||
hostFile := flag.String("hostfile", "", "hostfile path")
|
||||
ipFile := flag.String("ipfile", "", "hostfile path")
|
||||
//gu
|
||||
jsonFile := flag.String("j", "", "Json File Path")
|
||||
ipFile := flag.String("ipfile", "", "ipfile path")
|
||||
cfgFile := flag.String("c", "", "cfg File Path")
|
||||
jsonMode := flag.Bool("j", false, "print output in json format")
|
||||
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")
|
||||
timeLimit := flag.Int("t", 30, "max timeout")
|
||||
numLimit := flag.Int("n", 20, "max execute number")
|
||||
|
||||
flag.Parse()
|
||||
var cmdList []string
|
||||
var hostList []string
|
||||
|
||||
var cmdList, hostList, cipherList, keyExchangeList []string
|
||||
var err error
|
||||
|
||||
sshHosts := []SSHHost{}
|
||||
var host_Struct SSHHost
|
||||
timeout := time.Duration(*timeLimit) * time.Second
|
||||
sshHosts := []g.SSHHost{}
|
||||
var host_Struct g.SSHHost
|
||||
|
||||
if *version {
|
||||
fmt.Println(VERSION)
|
||||
fmt.Println(g.VERSION)
|
||||
os.Exit(0)
|
||||
}
|
||||
|
||||
if *ipFile != "" {
|
||||
hostList, err = GetIpList(*ipFile)
|
||||
hostList, err = g.GetIpListFromFile(*ipFile)
|
||||
if err != nil {
|
||||
log.Println("load hostlist error: ", err)
|
||||
log.Println("load iplist error: ", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if *hostFile != "" {
|
||||
hostList, err = Getfile(*hostFile)
|
||||
hostList, err = g.Getfile(*hostFile)
|
||||
if err != nil {
|
||||
log.Println("load hostfile error: ", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
if *ips != "" {
|
||||
hostList, err = g.GetIpList(*ips)
|
||||
if err != nil {
|
||||
log.Println("load iplist error: ", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if *hosts != "" {
|
||||
hostList = strings.Split(*hosts, ";")
|
||||
hostList = g.SplitString(*hosts)
|
||||
}
|
||||
|
||||
if *cmdFile != "" {
|
||||
cmdList, err = Getfile(*cmdFile)
|
||||
cmdList, err = g.Getfile(*cmdFile)
|
||||
if err != nil {
|
||||
log.Println("load cmdfile error: ", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
if *cmd != "" {
|
||||
cmdList = strings.Split(*cmd, ";")
|
||||
if *cmds != "" {
|
||||
cmdList = g.SplitString(*cmds)
|
||||
|
||||
}
|
||||
if *jsonFile == "" {
|
||||
if *ciphers != "" {
|
||||
cipherList = g.SplitString(*ciphers)
|
||||
}
|
||||
if *keyExchanges != "" {
|
||||
keyExchangeList = g.SplitString(*keyExchanges)
|
||||
}
|
||||
if *cfgFile == "" {
|
||||
for _, host := range hostList {
|
||||
host_Struct.Host = host
|
||||
host_Struct.Username = *username
|
||||
host_Struct.Password = *password
|
||||
host_Struct.Port = *port
|
||||
host_Struct.Cmd = cmdList
|
||||
host_Struct.CmdList = cmdList
|
||||
host_Struct.Key = *key
|
||||
host_Struct.LinuxMode = *linuxMode
|
||||
sshHosts = append(sshHosts, host_Struct)
|
||||
}
|
||||
}
|
||||
//gu
|
||||
if *jsonFile != "" {
|
||||
sshHosts, err = GetJsonFile(*jsonFile)
|
||||
} else {
|
||||
sshHostConfig, err := g.GetJsonFile(*cfgFile)
|
||||
if err != nil {
|
||||
log.Println("load jsonFile error: ", err)
|
||||
log.Println("load cfgFile error: ", err)
|
||||
return
|
||||
}
|
||||
cipherList = g.SplitString(sshHostConfig.Global.Ciphers)
|
||||
keyExchangeList = g.SplitString(sshHostConfig.Global.KeyExchanges)
|
||||
sshHosts = sshHostConfig.SshHosts
|
||||
for i := 0; i < len(sshHosts); i++ {
|
||||
cmdList, err = Getfile(sshHosts[i].CmdFile)
|
||||
if err != nil {
|
||||
log.Println("load cmdFile error: ", err)
|
||||
return
|
||||
if sshHosts[i].Cmds != "" {
|
||||
sshHosts[i].CmdList = g.SplitString(sshHosts[i].Cmds)
|
||||
} else {
|
||||
cmdList, err = g.Getfile(sshHosts[i].CmdFile)
|
||||
if err != nil {
|
||||
log.Println("load cmdFile error: ", err)
|
||||
return
|
||||
}
|
||||
sshHosts[i].CmdList = cmdList
|
||||
}
|
||||
sshHosts[i].Cmd = cmdList
|
||||
}
|
||||
}
|
||||
|
||||
chLimit := make(chan bool, *numLimit) //控制并发访问量
|
||||
chs := make([]chan string, len(sshHosts))
|
||||
limitFunc := func(chLimit chan bool, ch chan string, host SSHHost) {
|
||||
dossh(host.Username, host.Password, host.Host, host.Cmd, host.Port, ch)
|
||||
chs := make([]chan g.SSHResult, len(sshHosts))
|
||||
startTime := time.Now()
|
||||
log.Println("Multissh start")
|
||||
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, keyExchangeList, host.LinuxMode, ch)
|
||||
<-chLimit
|
||||
}
|
||||
for i, host := range sshHosts {
|
||||
chs[i] = make(chan string, 1)
|
||||
chs[i] = make(chan g.SSHResult, 1)
|
||||
chLimit <- true
|
||||
go limitFunc(chLimit, chs[i], host)
|
||||
}
|
||||
for i, ch := range chs {
|
||||
fmt.Println(sshHosts[i].Host, " ssh start")
|
||||
select {
|
||||
case res := <-ch:
|
||||
if res != "" {
|
||||
fmt.Println(res)
|
||||
sshHosts[i].Result += res
|
||||
}
|
||||
case <-time.After(timeout):
|
||||
log.Println("SSH run timeout")
|
||||
sshHosts[i].Result += ("SSH run timeout:" + strconv.Itoa(int(*timeLimit)) + "second.")
|
||||
sshResults := []g.SSHResult{}
|
||||
for _, ch := range chs {
|
||||
res := <-ch
|
||||
if res.Result != "" {
|
||||
sshResults = append(sshResults, res)
|
||||
}
|
||||
|
||||
fmt.Println(sshHosts[i].Host, " ssh end")
|
||||
}
|
||||
|
||||
endTime := time.Now()
|
||||
log.Printf("Multissh finished. Process time %s. Number of active ip is %d", endTime.Sub(startTime), len(sshHosts))
|
||||
//gu
|
||||
if *outTxt {
|
||||
for i := 0; i < len(sshHosts); i++ {
|
||||
err = WriteIntoTxt(sshHosts[i])
|
||||
for _, sshResult := range sshResults {
|
||||
err = g.WriteIntoTxt(sshResult, *fileLocate)
|
||||
if err != nil {
|
||||
log.Println("write into txt error: ", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
if *jsonMode {
|
||||
jsonResult, err := json.Marshal(sshResults)
|
||||
if err != nil {
|
||||
log.Println("json Marshal error: ", err)
|
||||
}
|
||||
fmt.Println(string(jsonResult))
|
||||
return
|
||||
}
|
||||
for _, sshResult := range sshResults {
|
||||
fmt.Println("host: ", sshResult.Host)
|
||||
fmt.Println("========= Result =========")
|
||||
fmt.Println(sshResult.Result)
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,18 +1,24 @@
|
|||
{
|
||||
"SshHosts": [
|
||||
{
|
||||
"Host": "192.168.15.101",
|
||||
"Port": 22,
|
||||
"Username": "admin",
|
||||
"Password": "admin",
|
||||
"CmdFile": "cmd1.txt.example"
|
||||
},
|
||||
{
|
||||
"Host": "192.168.83.40",
|
||||
"Port": 22,
|
||||
"Username": "root",
|
||||
"Password": "root",
|
||||
"CmdFile": "cmd2.txt.example"
|
||||
}
|
||||
]
|
||||
"SshHosts": [{
|
||||
"Host": "192.168.31.51",
|
||||
"Port": 22,
|
||||
"Username": "admin",
|
||||
"Password": "admin",
|
||||
"cmds": "show clock;show clock"
|
||||
},
|
||||
{
|
||||
"Host": "192.168.80.131",
|
||||
"Port": 22,
|
||||
"Username": "root",
|
||||
"Password": "",
|
||||
"key": "./server.key",
|
||||
"linuxMode": true,
|
||||
"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"
|
||||
}
|
||||
|
||||
}
|
||||
53
ssh_test.go
53
ssh_test.go
|
|
@ -1,53 +0,0 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"testing"
|
||||
)
|
||||
|
||||
const (
|
||||
username = "admin"
|
||||
password = "admin"
|
||||
ip = "192.168.31.21"
|
||||
port = 22
|
||||
cmd = "show clock;exit"
|
||||
)
|
||||
|
||||
func Test_SSH(t *testing.T) {
|
||||
session, err := connect(username, password, ip, port)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
defer session.Close()
|
||||
|
||||
cmdlist := strings.Split(cmd, ";")
|
||||
|
||||
stdinBuf, err := session.StdinPipe()
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
// var bt bytes.Buffer
|
||||
// session.Stdout = &bt
|
||||
t.Log(session.Stdout)
|
||||
t.Log(session.Stderr)
|
||||
session.Stdout = os.Stdout
|
||||
session.Stderr = os.Stderr
|
||||
session.Stdin = os.Stdin
|
||||
err = session.Shell()
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
for _, c := range cmdlist {
|
||||
c = c + "\n"
|
||||
stdinBuf.Write([]byte(c))
|
||||
}
|
||||
session.Wait()
|
||||
t.Error(err)
|
||||
// t.Log(bt.String())
|
||||
return
|
||||
}
|
||||
|
|
@ -1,97 +0,0 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"net"
|
||||
//"os"
|
||||
|
||||
"time"
|
||||
|
||||
"golang.org/x/crypto/ssh"
|
||||
)
|
||||
|
||||
func connect(user, password, host string, port int) (*ssh.Session, error) {
|
||||
var (
|
||||
auth []ssh.AuthMethod
|
||||
addr string
|
||||
clientConfig *ssh.ClientConfig
|
||||
client *ssh.Client
|
||||
config ssh.Config
|
||||
session *ssh.Session
|
||||
err error
|
||||
)
|
||||
// get auth method
|
||||
auth = make([]ssh.AuthMethod, 0)
|
||||
auth = append(auth, ssh.Password(password))
|
||||
|
||||
config = ssh.Config{
|
||||
Ciphers: []string{"aes128-ctr", "aes192-ctr", "aes256-ctr", "arcfour256", "arcfour128", "aes128-cbc", "3des-cbc", "blowfish-cbc", "cast128-cbc", "aes192-cbc", "aes256-cbc", "arcfour"},
|
||||
}
|
||||
|
||||
clientConfig = &ssh.ClientConfig{
|
||||
User: user,
|
||||
Auth: auth,
|
||||
Timeout: 30 * time.Second,
|
||||
Config: config,
|
||||
HostKeyCallback: func(hostname string, remote net.Addr, key ssh.PublicKey) error {
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
// connet to ssh
|
||||
addr = fmt.Sprintf("%s:%d", host, port)
|
||||
|
||||
if client, err = ssh.Dial("tcp", addr, clientConfig); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// create session
|
||||
if session, err = client.NewSession(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
modes := ssh.TerminalModes{
|
||||
ssh.ECHO: 0, // disable echoing
|
||||
ssh.TTY_OP_ISPEED: 14400, // input speed = 14.4kbaud
|
||||
ssh.TTY_OP_OSPEED: 14400, // output speed = 14.4kbaud
|
||||
}
|
||||
|
||||
if err := session.RequestPty("xterm", 80, 40, modes); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return session, nil
|
||||
}
|
||||
|
||||
func dossh(username, password, ip string, cmdlist []string, port int, ch chan string) {
|
||||
session, err := connect(username, password, ip, port)
|
||||
|
||||
if err != nil {
|
||||
ch <- fmt.Sprintf("<%s>", err.Error())
|
||||
//<-chLimit
|
||||
return
|
||||
|
||||
}
|
||||
defer session.Close()
|
||||
|
||||
cmdlist = append(cmdlist, "exit")
|
||||
// cmd := "ls;date;exit"
|
||||
stdinBuf, _ := session.StdinPipe()
|
||||
//fmt.Fprintf(os.Stdout, "%s", stdinBuf)
|
||||
var outbt, errbt bytes.Buffer
|
||||
session.Stdout = &outbt
|
||||
|
||||
session.Stderr = &errbt
|
||||
err = session.Shell()
|
||||
for _, c := range cmdlist {
|
||||
c = c + "\n"
|
||||
stdinBuf.Write([]byte(c))
|
||||
|
||||
}
|
||||
session.Wait()
|
||||
|
||||
ch <- (outbt.String() + errbt.String())
|
||||
//<-chLimit
|
||||
return
|
||||
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue