使用ssh打个隧道,是我一直想作的事情。出发点是:在公司内部常常有些机器访问不到,只能经过公司提供的开发机,可是开发机咱们能够在内网访问到,这个html
SSH是一种网络协议,用于计算机之间的加密登陆,流程大体是:linux
上面这个过程当中很容易受到攻击的就是第一步,咱们怎么知道收到的公钥是真正的主机的,体如今实际登录中,就是会出现:git
 $ ssh user@host
  The authenticity of host 'host (12.18.429.21)' can't be established.   RSA key fingerprint is 98:2e:d7:e0:de:9f:ac:67:28:c2:42:2d:37:16:58:4d.   Are you sure you want to continue connecting (yes/no)? 复制代码
此处RSA key fingerprint
就是对128公钥的MD5签名,当输入yes后,就会保存到$HOME/.ssh/known_hosts,说明是对公钥的承认。github
解决了信任问题后,下一个很差的是每次都须要输入密码,因而就有了公钥登录,就是用户将本身的公钥储存在远程主机上。登陆的时候,远程主机会向用户发送一段随机字符串,用户用本身的私钥加密后,再发回来。远程主机用事先储存的公钥进行解密,若是成功,就证实用户是可信的,直接容许登陆shell,再也不要求密码。golang
因此总结起来 ssh 认证的方式有两种:shell
下面咱们来看go中相应的操做方法bash
go get -u golang.org/x/crypto/...
复制代码
先来讲2种认证方式,显示密码,经过ssh.Password
来传入密码网络
sshConfig := &ssh.ClientConfig{
User: "your_user_name",
Auth: []ssh.AuthMethod{
ssh.Password("your_password")
},
}
复制代码
第二种是证书的方式,这又细分为两种,一种是咱们直接读取本身的私钥,另外一种是从ssh-agent读取,ssh-agent是用来帮助咱们管理私钥的程序,它的出现主要是为了解决当咱们访问不一样的主机使用不一样的私钥-公钥对,同时解决若是设置了私钥的密码,须要每次都手动设置的问题。 ps:ssh-agent的管理session
eval `ssh-agent` 启动agent代理
ssh-add /path/to/key/key_name 添加指定私钥
ssh-agent -k 关闭 agent
ssh-add -l 查看代理中私钥
ssh-add -L 查看代理中私钥对应的公钥
ssh-add -d /path/to/key/key_name 移除指定私钥
ssh-add -D 删除管理的全部私钥
复制代码
从文件读取ssh
func PublicKeyFile(file string) ssh.AuthMethod {
buffer, err := ioutil.ReadFile(file)
if err != nil {
return nil
}
key, err := ssh.ParsePrivateKey(buffer)
if err != nil {
return nil
}
return ssh.PublicKeys(key)
}
复制代码
从 ssh-agent 读取
func SSHAgent() ssh.AuthMethod {
if sshAgent, err := net.Dial("unix", os.Getenv("SSH_AUTH_SOCK")); err == nil {
return ssh.PublicKeysCallback(agent.NewClient(sshAgent).Signers)
}
return nil
}
复制代码
当咱们有了认证方式后,下面就是生成ssh-client
sshClient, err := ssh.Dial("tcp", "host:port", sshConfig)
if err != nil {
return nil, fmt.Errorf("Failed to dial: %s", err)
}
复制代码
创建链接后,咱们要建立一个命令执行的session,一个session就是一次命令执行,在开始执行命令以前,咱们还要创建一个伪终端,方便输入输出
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 {
session.Close()
return nil, fmt.Errorf("request for pseudo terminal failed: %s", err)
}
复制代码
再而后咱们就能够开始执行代码,完整代码见ssh_client