使用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