go語言實現ssh打隧道

顓頊發表於2019-01-21

如何打隧道

使用ssh打個隧道,是我一直想做的事情。出發點是:在公司內部經常有些機器訪問不到,只能通過公司提供的開發機,但是開發機我們可以在內網訪問到,這個

基礎知識

SSH是一種網路協議,用於計算機之間的加密登入,流程大致是:

  1. 遠端主機收到使用者的登入請求,把自己的公鑰發給使用者。
  2. 使用者使用這個公鑰,將登入密碼加密後,傳送回來。
  3. 遠端主機用自己的私鑰,解密登入密碼,如果密碼正確,就同意使用者登入。

上面這個過程中很容易受到攻擊的就是第一步,我們怎麼知道收到的公鑰是真正的主機的,體現在實際登陸中,就是會出現:

&
emsp;
$ ssh user@host&
emsp;
&
emsp;
The authenticity of host 'host (12.18.429.21)' can't be established.&
emsp;
&
emsp;
RSA key fingerprint is 98:2e:d7:e0:de:9f:ac:67:28:c2:42:2d:37:16:58:4d.&
emsp;
&
emsp;
Are you sure you want to continue connecting (yes/no)?
複製程式碼

此處RSA key fingerprint就是對128公鑰的MD5簽名,當輸入yes後,就會儲存到$HOME/.ssh/known_hosts,說明是對公鑰的認可。

解決了信任問題後,下一個不好的是每次都需要輸入密碼,於是就有了公鑰登陸,就是使用者將自己的公鑰儲存在遠端主機上。登入的時候,遠端主機會向使用者傳送一段隨機字串,使用者用自己的私鑰加密後,再發回來。遠端主機用事先儲存的公鑰進行解密,如果成功,就證明使用者是可信的,直接允許登入shell,不再要求密碼。

所以總結起來 ssh 認證的方式有兩種:

  1. 使用者名稱 + 證書
  2. 使用者名稱 + 密碼

下面我們來看go中相應的操作方法

golang 實現 ssh client

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的管理

eval `ssh-agent` 啟動agent代理ssh-add /path/to/key/key_name 新增指定私鑰ssh-agent -k 關閉 agentssh-add -l 檢視代理中私鑰ssh-add -L 檢視代理中私鑰對應的公鑰ssh-add -d /path/to/key/key_name 移除指定私鑰ssh-add -D 刪除管理的所有私鑰複製程式碼

從檔案讀取

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
}複製程式碼

client生成

當我們有了認證方式後,下面就是生成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

參考

how-to-create-an-ssh-tunnel-in-go

golang中ssh基礎操作

基礎知識

來源:https://juejin.im/post/5c452ccb5188252552516cfa

相關文章