Goで軽量レベルのsshバッチ操作ツールを書く
13136 ワード
前言
これは車輪です.
Ansibleは非常に強力な自動化メンテナンスツールであり、非常に高いことはよく知られています.あまりにも大きくて、ローエンドでは運維が少し水土不服で、3点にあります. AnsibleはPythonベースで、Pythonの下のインストールは依存しています.の笑うな!Winを利用する多くのユーザーにとって、Pythonを入れるだけでpipを入れるだけでポットを飲むことができます. Ansibleのpaybookで使われているyaml構文はもちろん強力です.しかし、新人にとって、手に入れたばかりでは游ぶことができず、勉强する必要があります.Ansibleは他の自動化メンテナンスツールに比べて、学習曲線が非常に親しみやすいが、やはり学ばなければならないのではないだろうか. Ansible自動化メンテナンスLinuxサーバは、Linux上のpythonのデフォルトのサポートのおかげで、非常に強力な機能を備えています.しかし、スイッチを走ると、通常python環境がないため、機能が大幅に割引されます.基本的には一連のコマンドの組合せを実行します.私达のこのような大きい园区のネットの伝统的な単位があって、运维の大頭は正式に交换机~ です
この車輪を作る出発点は以下の考慮に基づいている.はプラットフォームをまたいで、木に依存して、箱を開けてすぐ使います.Goで1つやっつけるとこのニーズをよく満たすことができます.Open-Falconのagent、ELKのbeatsを見て、すべてGoで実現することを選んだのは、このためです. 簡単で頭がなく、勉強する必要はありません.スイッチを初期化するコマンドラインの組み合わせテンプレートのように、コマンドラインを直接積み上げてください.cliが遊べる限り、そのまま引っ越してくれればいいのに. は、同時実行をサポートします.これはGoの強みですから、言うまでもありません. 最後はもちろんGoを勉強しました.
ブラックAnsibleという意味は全くありません.私たちもAnsibleで自動化メンテナンスの仕事をしています.すべてのメンテナンスはAnsibleを勉強したほうがいいと思います.将来はいつも自動化の方向に行かなければなりません.このホイールの目的はAnsibleを学ぶ前に、まず簡単で頭のないツールで目の前のニーズを解決することです.
sshセッションの確立
Go自身はsshバッグを持っていません.彼のsshバッグは置いてあるhttps://godoc.org/golang.org/x/crypto/sshここです.import彼ならいい
まず、このようなsshセッションを確立する必要があります.
基本的に黙認すればいいです.ただし、
linuxでは通常問題はありませんが、多くのスイッチはデフォルトで
ここには2つの場所があります.
これは、デフォルト鍵が信頼されていない場合、GoのsshパケットがHostKeyCallbackで接続を乾かすからです(1.8以降に追加すべきです).しかし、私たちがユーザー名パスワードを使って接続する場合、これは普通ではないでしょうか.
構成されたパラメータはGoDocの例に従ってください.
コマンドの実行
セッションが確立されると、命令を実行するのは簡単で、
ターゲットはスイッチです.テストしてください.
それともあのスイッチか、テストしてみましょう.
両方のコマンドが実行され、exitが実行された後に接続が終了することがわかります.
少なくとも、上の
答えはいいですが、命令を
すぐに簡潔になりますよね?
ホイール
sshがコマンドを実行するのはそれほど悪くない.sshバッチ操作ツールになるには、同時実行、同時制限、タイムアウト制御、入力パラメータ解析、出力フォーマットなどを追加します.
ここでは展開されず、最終的にはこのように作られた車輪が生えています.https://github.com/shanghai-edu/multissh
コマンドとホストの区切り記号として
ホストグループとコマンドグループをテキストで格納し、改行で区切ることもできます.
特に、IP(-ipsまたは-ipfile)が入力されている場合、
ssh鍵認証の使用をサポートします.この場合passwordを入力するとkeyとしてのパスワードになります.
linuxについては、linuxModeモードをサポートする、つまり、コマンドの組み合わせを
ホストごとに異なる構成パラメータを定義し、json形式で構成をロードすることもできます.
出力はjson形式に打つことができて、プログラムの処理を便利にします.
また、構成バックアップなど、ホスト名で命名されたテキストに出力結果を保存することもできます.
リファレンスドキュメント
golang.org/x/crypto/ssh golang-ssh-how-to-run-multiple-commands-on-the-same-session golang-enter-ssh-sudo-password-on-prompt-or-exit
以上
転載許可
CC BY-SA
これは車輪です.
Ansibleは非常に強力な自動化メンテナンスツールであり、非常に高いことはよく知られています.あまりにも大きくて、ローエンドでは運維が少し水土不服で、3点にあります.
この車輪を作る出発点は以下の考慮に基づいている.
ブラックAnsibleという意味は全くありません.私たちもAnsibleで自動化メンテナンスの仕事をしています.すべてのメンテナンスはAnsibleを勉強したほうがいいと思います.将来はいつも自動化の方向に行かなければなりません.このホイールの目的はAnsibleを学ぶ前に、まず簡単で頭のないツールで目の前のニーズを解決することです.
sshセッションの確立
Go自身はsshバッグを持っていません.彼のsshバッグは置いてあるhttps://godoc.org/golang.org/x/crypto/sshここです.import彼ならいい
import "golang.org/x/crypto/ssh"
まず、このようなsshセッションを確立する必要があります.
func connect(user, password, host, key string, port int, cipherList []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 = ssh.Config{
Ciphers: []string{"aes128-ctr", "aes192-ctr", "aes256-ctr", "[email protected]", "arcfour256", "arcfour128", "aes128-cbc", "3des-cbc", "aes192-cbc", "aes256-cbc"},
}
} else {
config = ssh.Config{
Ciphers: cipherList,
}
}
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
}
ssh.AuthMethod
にはsshの認証方式が格納されている.パスワード認証を使うと、ssh.Password()
でパスワードをロードします.鍵認証を使用すると、ssh.ParsePrivateKey()
またはssh.ParsePrivateKeyWithPassphrase()
で鍵を読み出し、ssh.PublicKeys()
でロードします.ssh.config
このstructにはsshの構成パラメータが格納されています.彼は次のいくつかの構成オプションを持っています.以下はGoDocから参照されます.type Config struct {
// Rand provides the source of entropy for cryptographic
// primitives. If Rand is nil, the cryptographic random reader
// in package crypto/rand will be used.
// 。
Rand io.Reader
// The maximum number of bytes sent or received after which a
// new key is negotiated. It must be at least 256. If
// unspecified, a size suitable for the chosen cipher is used.
// ,
RekeyThreshold uint64
// The allowed key exchanges algorithms. If unspecified then a
// default set of algorithms is used.
//
KeyExchanges []string
// The allowed cipher algorithms. If unspecified then a sensible
// default is used.
//
Ciphers []string
// The allowed MAC algorithms. If unspecified then a sensible default
// is used.
// MAC (Message Authentication Code ) ,
MACs []string
}
基本的に黙認すればいいです.ただし、
Ciphers
は変更が必要であり、デフォルト構成ではGoのSSHパケットが提供するCiphersには以下の暗号化方式が含まれている.aes128-ctr aes192-ctr aes256-ctr [email protected] arcfour256 arcfour128
linuxでは通常問題はありませんが、多くのスイッチはデフォルトで
aes128-cbc 3des-cbc aes192-cbc aes256-cbc
しか提供されていません.だから私たちはもう少し加えたほうがいいです.ここには2つの場所があります.
clientConfig
にこんな一節がありますHostKeyCallback: func(hostname string, remote net.Addr, key ssh.PublicKey) error {
return nil
},
これは、デフォルト鍵が信頼されていない場合、GoのsshパケットがHostKeyCallbackで接続を乾かすからです(1.8以降に追加すべきです).しかし、私たちがユーザー名パスワードを使って接続する場合、これは普通ではないでしょうか.
return nil
にすればいいのです.NewSession()
の後、modes
およびRequestPty
を定義した.これは、後にsession.Shell()
を用いて端末をシミュレートする場合に確立される端末パラメータであるからである.不一致の場合、デフォルト値は一部の端末で実行に失敗する可能性があります.たとえば、H 3 Cのスイッチでは、接続が確立された後にデフォルトでプッシュされたCopyrightがssh接続異常を起こし、タイムアウトまたは直接切断する可能性があります.例えば、******************************************************************************
* 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. *
******************************************************************************
構成されたパラメータはGoDocの例に従ってください.
// Set up terminal modes
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
}
// Request pseudo terminal
if err := session.RequestPty("xterm", 40, 80, modes); err != nil {
log.Fatal("request for pseudo terminal failed: ", err)
}
コマンドの実行
セッションが確立されると、命令を実行するのは簡単で、
session.Run()
で私たちの命令を実行することができ、結果はsession.Studout
に戻ります.簡単なテストをします.const (
username = "admin"
password = "password"
ip = "192.168.15.101"
port = 22
cmd = "show clock"
)
func Test_SSH_run(t *testing.T) {
ciphers := []string{}
session, err := connect(username, password, ip, port, ciphers)
if err != nil {
t.Error(err)
return
}
defer session.Close()
var stdoutBuf bytes.Buffer
session.Stdout = &stdoutBuf
session.Run(cmd)
t.Log(session.Stdout)
return
}
ターゲットはスイッチです.テストしてください.
=== RUN Test_SSH_run
--- PASS: Test_SSH_run (0.69s)
ssh_test.go:30: 07:55:52.598 UTC Wed Jan 17 2018
PASS
show clock
のコマンドが正常に実行され、結果が返されたことがわかります.session.Run()
は単一のコマンドを実行することを限定し、いくつかのコマンドの組み合わせを実行するにはsession.Shell()
が必要です.意味ははっきりしていて、1つの端末をシミュレートして1つの実行命令を行って、そして結果を返します.私たちがShellを使うように、プロセス全体を印刷して出力すればいいです.session.StdinPipe()
からコマンドを1つずつ入力し、session.Stdout
およびsession.Stderr
からShell
の出力を取得する.同じようにテストをします.const (
username = "admin"
password = "password"
ip = "192.168.15.101"
port = 22
cmds = "show clock;show env power;exit"
)
func Test_SSH(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, ";")
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 + "
"
stdinBuf.Write([]byte(c))
}
session.Wait()
t.Log((outbt.String() + errbt.String()))
return
}
それともあのスイッチか、テストしてみましょう.
=== RUN Test_SSH
--- PASS: Test_SSH (0.69s)
ssh_test.go:51: sw-1#show clock
07:59:52.598 UTC Wed Jan 17 2018
sw-1#show env power
SW PID Serial# Status Sys Pwr PoE Pwr Watts
-- ------------------ ---------- --------------- ------- ------- -----
1 Built-in Good
sw-1#exit
PASS
両方のコマンドが実行され、exitが実行された後に接続が終了することがわかります.
session.Run()
との違いを比較すると、session.Shell()
モードでは、出力されたコンテンツにホスト名、入力されたコマンドなどが含まれていることがわかります.これはtty
実行の結果ですからね.命令を実行するだけなら構わないが、命令を実行した結果からいくつかの情報を読み取る必要がある場合は、これらの内容は少し肥大化しているように見えます.例えばubuntuで走ってみましょう=== RUN Test_SSH
--- PASS: Test_SSH (0.98s)
ssh_test.go:50: 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 Thu Jan 18 16:34:56 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/
16 。
16 。
New release '17.10' available.
Run 'do-release-upgrade' to upgrade to it.
You have new mail.
Last login: Thu Jan 18 16:31:41 2018 from 192.168.95.104
root@ubuntu-docker-node3:~# root@ubuntu-docker-node3:/opt# /opt
root@ubuntu-docker-node3:/opt#
少なくとも、上の
System information
の山は必要ありません.スイッチは仕方がありません.Linuxで命令を通すことができますか.つまり、方法を考えます.Run()はコマンドの組み合わせを実行しますか?答えはいいですが、命令を
&&
でつなげればいいのでしょうか.LinnuxのShellは私たちを分解してそれぞれ実行します.例えば、上のこのコマンドはcd /opt&&pwd&&exit
に統合できます.=== RUN Test_SSH_run
--- PASS: Test_SSH_run (0.91s)
ssh_test.go:76: /opt
すぐに簡潔になりますよね?
ホイール
sshがコマンドを実行するのはそれほど悪くない.sshバッチ操作ツールになるには、同時実行、同時制限、タイムアウト制御、入力パラメータ解析、出力フォーマットなどを追加します.
ここでは展開されず、最終的にはこのように作られた車輪が生えています.https://github.com/shanghai-edu/multissh
コマンドとホストの区切り記号として
;
番または,
番を使用して、直接コマンドラインで実行できます.# ./multissh -cmds "show clock" -hosts "192.168.31.21;192.168.15.102" -u admin -p password
ホストグループとコマンドグループをテキストで格納し、改行で区切ることもできます.
# ./multissh -cmdfile cmd1.txt.example -hostfile host.txt.example -u admin -p password
特に、IP(-ipsまたは-ipfile)が入力されている場合、
192.168.15.101-192.168.15.110
のようなIPアドレスセグメント方式の入力を許可する.# ./multissh -cmds "show clock" -ips "192.168.15.101-192.168.15.110" -u admin -p password
ssh鍵認証の使用をサポートします.この場合passwordを入力するとkeyとしてのパスワードになります.
# ./multissh -hosts "192.168.80.131" -cmds "date;cd /opt;ls" -u root -k "server.key"
linuxについては、linuxModeモードをサポートする、つまり、コマンドの組み合わせを
&&
で接続した後、se ssionを使用する.Run()運転.# ./multissh -hosts "192.168.80.131" -cmds "date;cd /opt;ls" -u root -k "server.key" -l
ホストごとに異なる構成パラメータを定義し、json形式で構成をロードすることもできます.
# ./multissh -c ssh.json.example
出力はjson形式に打つことができて、プログラムの処理を便利にします.
# ./multissh -c ssh.json.example -j
また、構成バックアップなど、ホスト名で命名されたテキストに出力結果を保存することもできます.
# ./multissh -c ssh.json.example -outTxt
リファレンスドキュメント
golang.org/x/crypto/ssh golang-ssh-how-to-run-multiple-commands-on-the-same-session golang-enter-ssh-sudo-password-on-prompt-or-exit
以上
転載許可
CC BY-SA