preface

this is a wheel.

everyone knows that Ansible is a super powerful automated operation and maintenance tool, very tall. Too tall and so low at the low end operation and maintenance is a bit of acclimatized. It lies in the three point:

  1. Ansible is based on Python, and the installation under Python is a bunch of dependence. Don't laugh! For a lot of users who use Win, the light is Python, and pip is enough to drink a pot. The yaml syntax used by the paybook of
  2. Ansible is of course very powerful. However, for newcomers, it is hard to play and need to learn. Although the Ansible compared with the operation and maintenance of automation tools, learning curve it has been very amiable and easy of approach, supported by default, but he wanted to learn about the right
  3. Ansible Linux server operation and maintenance of automation due to the Linux python, very powerful. However, if you run a switch, there is a lot of discounts on the switch because there is no Python environment on the switch. Basically, it is a series of command combinations. And our traditional unit with large campus network is the switch ~

. So the starting point of building this wheel is based on the following considerations:

  1. has to cross platform, and wood relies on it and opens the box. Use Go to roll a can well meet the demand. You look at the agent of Open-Falcon, the beats of ELK, and this is the reason that you choose to use Go.
  2. is simple and no brain, no need to learn. Directly stacking the command line is like a command line combination template that we initialize the switch. As long as cli will play, just copy it directly.
  3. needs to support concurrency. This is the strength of Go, no more words.
  4. , of course, is to learn Go.

doesn't mean black Ansible at all. We also have the work of automating operation and maintenance with Ansible. I think all operations and best practices are learning Ansible, and always go towards automation in the future. The purpose of this wheel is to learn Ansible. Before that, there is a tool that is simple and without brain to solve the immediate needs. ~

establishes SSH session

Go itself without SSH package. His SSH package is placed here in the https://godoc.org/golang.org/x/crypto/ssh. Import he is good for

 import "golang.org/x/crypto/ssh" 

and first we need to build a SSH session, such as this.

 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 auth method auth / / get) = make ([]ssh.AuthMethod, 0) if = = {auth = "key" 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 = ssh.ParsePrivateKeyWithPassphrase (err, pemBytes, []byte (password) err = nil if)}! {return nil}, err auth = append (auth, ssh.PublicKeys (signer) if len (cipherList)}) = = 0 {config = ssh.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 = 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})}, to SSH = addr / / connet fmt.Sprintf (%s:%d, host, port) if client, err = ssh.Dial (TCP, addr, clientConfig) ; err! = nil {return} / / create session err nil, if session, err = client.NewSession (ERR);! = nil {return nil err} modes: ssh.TerminalModes{= ssh.ECHO: 0, echoing ssh.TTY_OP_ISPEED: / / disable / / input speed = 14.4kbaud 14400, ssh.TTY_OP_OSPEED: 14400, output speed = 14.4kbaud / if}: = err session.RequestPty ("xterm", 80, 40, modes); err! = nil {return} return nil, err session, nil} 

ssh.AuthMethod to store SSH authentication. With password authentication, the password is loaded with ssh.Password (). If you use the key authentication, read the key with ssh.ParsePrivateKey () or ssh.ParsePrivateKeyWithPassphrase (), then load it through ssh.PublicKeys ().

ssh.config struct saves SSH configuration parameters. He has the following configuration options, which are referenced from GoDoc >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. / / encryption with seed. The default Rand io.Reader maximum number of bytes / / The sent or received after which a new key is negotiated. It must / be at least 256. If a size suitable for / unspecified the chosen cipher is maximum transmission byte used. / / key agreement after the default 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 is used. / / default / / connection allows encryption algorithm Ciphers []string The allowed MAC algorithms. If unspecified / then a sensible / / is / / default used. MAC (Message Authentication connection allows the Code message digest algorithm, default) The good MACs []string} 

is basically the default. But Ciphers need to modify the default configuration, the Go SSH package provides Ciphers encryption

include " external nofollow ">aes128-gcm@openssh.com arcfour256 arcfour128

Linux is no problem, but in fact many switches default only provide aes128-cbc 3des-cbc aes192-cbc aes256-cbc. So it's better for us to add a little bit more.

there are two places to mention

1, so a

 HostKeyCallback: func in clientConfig (hostname string, remote net.Addr, key ssh.PublicKey) error nil 

{return}, this is because the default key is not trusted, Go will connect the SSH package in the HostKeyCallback (kill 1.8) should be added later. But when we use the username and password to connect, this is too normal, so let him return nil.

2, after NewSession (), we define modes and RequestPty. This is due to the terminal parameters established for the later use of the session.Shell () analog terminal. If not matched, the default may result in failure on certain terminals. For example, some H3C switches, the default Copyright that is pushed out after the connection is set up may lead to the exception of the SSH connection, and then out of time or directly. Such as:

 ****************************************************************************** * Copyright (c) 2004-2016 Hangzhou H3C Tech. Co. Ltd. All rights reserved. Without the owner's prior written * * * * no decompiling consent, or reverse-engineering shall be allowed. * ******************************************************************************

configuration parameter copy GoDoc the sample is good:

 up terminal modes modes: / / Set = ssh.TerminalModes{ssh.ECHO: 0, echoing ssh.TTY_OP_ISPEED: / / disable / / input speed = 14.4kbaud 14400, ssh.TTY_OP_OSPEED: 14400, speed = 14.4kbaud / / output / / Request pseudo terminal} err: if = session.RequestPty ("xterm", 40 , 80, modes); err! = nil {log.Fatal ("request for pseudo terminal failed:, ERR) 

}

to execute a command to establish session, execute the command is very simple, with session.Run () can carry out our order results back to session.Studout. We run a simple test.

 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 = nil {t.Error err! (ERR) return defer session.Close (VaR)} stdoutBuf bytes.Buffer session.Stdout = & stdoutBuf session.Run (CMD) t.Log (session.Stdout) return 

} goal is a switch, test

 RUN Test_SSH_run - PASS: Test_SSH_run = (0.69s) ssh_test.go:30: 07:55:52.598 UTC Wed Jan 172018 PASS 

show clock can see the command has been the successful implementation of the results, and returns the.

session.Run () only limits the execution of a single command, and a number of command combinations need to be used in session.Shell (). The meaning is very clear, which is to simulate a terminal to carry out a single execution command and return the result. Just as we do with Shell, we print out the whole process and output it. Input commands from session.StdinPipe () one by one to get the output on Shell from session.Stdout and session.Stderr. Do a test as well.

 const (username = "admin" password = "password" IP = "192.168.15.101" port CMDS = "show clock = 22; 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} return (ERR) var outbt, errbt bytes.Buffer session.Stdout outbt = & session.Stderr = & errbt = err session.Shell (if) err! = nil {t.Error} return (ERR) for _, C: range = cmdlist {C = C + n stdinBuf.Write ([]byte (c))} (session.Wait) T.Log (((outbt.String () + errbt.String ())) return}

or that exchange


This concludes the body part

This paper fixed link:http://www.script-home.com/a-method-of-writing-a-lightweight-ssh-batch-operation-tool-with-go.html | Script Home | +Copy Link

Article reprint please specify:A method of writing a lightweight SSH batch operation tool with Go | Script Home

You may also be interested in these articles!