用Go写一个轻量级的ssh批量操作工具的方法

2020-01-28 12:54:35丽君

前言

这是一个轮子。

大家都知道 Ansible 是功能超级强大的自动化运维工具,十分的高大上。太高大上了以至于在低端运维有点水土不服,在于三点:

    Ansible 是基于 Python 的,而 Python 下的安装是有一堆依赖的。。。不要笑!对于很多使用 Win 的用户而言,光是装 Python, 装 pip 就够喝一壶的了。 Ansible 的 paybook 所使用的 yaml 语法当然非常强大了。然而对于新人而言,刚入手是玩不转的,需要学习。虽然 Ansible 相比其他的自动化运维工具,它的学习曲线已经非常平易近人了,但毕竟还是要学一下的不是么 Ansible 自动化运维 Linux 服务器得益于 Linux 上 python 的默认支持,功能非常强大。然而如果拿来跑交换机的话,因为交换机上通常没有 python 环境,功能就要打很多折扣了。基本上也就是执行一系列的命令组合。而我们这种有大片园区网的传统单位,运维的大头正式是交换机~

所以造这个轮子的出发点是基于以下考虑的:

    要跨平台,木有依赖,开箱即用。用 Go 来撸一个就能很好的满足这个需求。你看 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 他就好


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", "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
    },
  }

  // 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
}