Guide to Implement an SSH Client Using Golang

  sonic0002        2023-11-11 09:19:29       9,562        0          English  简体中文  繁体中文  ภาษาไทย  Tiếng Việt 

SSH,全名為 Secure Shell,是一種網路協定,用於安全地遠端登入網路上的其他電腦。我相信大多數後端開發人員都熟悉 SSH。用於登入伺服器的常見 shell 工具,例如 Xshell、SecureCRT 和 iTerm2,都是基於 SSH 協定。在 Golang 中,crypto/ssh 套件提供了實作 SSH 用戶端的功能。在本文中,我們將詳細說明如何使用 Golang 實作 SSH 用戶端。

建立 SSH 用戶端組態

首先,我們需要設定 SSH 用戶端連線到伺服器的參數。基本組態包括 usernameauthentication methodhost key callback。以下是一個程式碼片段範例:

package main

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

func main() {
	config := &ssh.ClientConfig{
		User: "username",
		Auth: []ssh.AuthMethod{
			ssh.Password("password"),
		},
		HostKeyCallback: ssh.InsecureIgnoreHostKey(),
	}
}

在此組態中,使用者名稱設定為 "username",密碼設定為 "password"。ssh.Password 函數用於建立密碼驗證方法。每次連線到新主機時,都會呼叫 HostKeyCallback 函數,它用於驗證伺服器的主機金鑰。在此範例中,使用 ssh.InsecureIgnoreHostKey,這表示接受任何主機金鑰。不建議在生產環境中使用此方法,因為不驗證主機金鑰會造成安全風險。

連線到 SSH 伺服器

ssh.Dial 函數用於連線到遠端 SSH 伺服器。它需要三個參數:網路類型(通常為 "tcp")、伺服器的位址和連接埠,以及先前建立的組態物件。以下是一個程式碼片段範例:

package main

import (
	"golang.org/x/crypto/ssh"
	"log"
)

func main() {
	config := &ssh.ClientConfig{
		User: "username",
		Auth: []ssh.AuthMethod{
			ssh.Password("password"),
		},
		HostKeyCallback: ssh.InsecureIgnoreHostKey(),
	}

	client, err := ssh.Dial("tcp", "192.168.3.111:22", config)
	if err != nil {
		log.Fatal("Failed to dial: ", err)
	}
}

使用 IP 位址 192.168.3.111 連線到伺服器,連接埠為 22(SSH 協定的預設連接埠)。如果連線失敗,將會傳回錯誤,您可以使用 log.Fatal 列印錯誤並結束程式。

建立 SSH 工作階段

一旦建立 SSH 連線,您就可以建立 SSH 工作階段以與伺服器通訊。以下是一個程式碼片段範例:

package main

import (
	"golang.org/x/crypto/ssh"
	"log"
)

func main() {
	config := &ssh.ClientConfig{
		User: "username",
		Auth: []ssh.AuthMethod{
			ssh.Password("password"),
		},
		HostKeyCallback: ssh.InsecureIgnoreHostKey(),
	}

	client, err := ssh.Dial("tcp", "192.168.3.111:22", config)
	if err != nil {
		log.Fatal("Failed to dial: ", err)
	}

	session, err := client.NewSession()
	if err != nil {
		log.Fatal("Failed to create session: ", err)
	}
	defer session.Close()
}

使用 client.NewSession 函數建立新的工作階段,如果失敗,將會傳回錯誤。使用 defer 關鍵字確保在操作完成後關閉工作階段。

建立虛擬終端機

SSH 協定允許建立虛擬終端機,它會模擬真實終端機的行為,並允許互動式命令,例如 shell 或文字編輯器。以下是一個程式碼片段範例:

package main

import (
	"golang.org/x/crypto/ssh"
	"log"
)

func main() {
	config := &ssh.ClientConfig{
		User: "username",
		Auth: []ssh.AuthMethod{
			ssh.Password("password"),
		},
		HostKeyCallback: ssh.InsecureIgnoreHostKey(),
	}

	client, err := ssh.Dial("tcp", "192.168.3.111:22", config)
	if err != nil {
		log.Fatal("Failed to dial: ", err)
	}

	session, err := client.NewSession()
	if err != nil {
		log.Fatal("Failed to create session: ", err)
	}
	defer session.Close()

	modes := ssh.TerminalModes{
		ssh.ECHO:          0,     // 停用回顯
		ssh.TTY_OP_ISPEED: 14400, // 輸入速度 = 14.4kbaud
		ssh.TTY_OP_OSPEED: 14400, // 輸出速度 = 14.4kbaud
	}
	if err := session.RequestPty("linux", 80, 40, modes); err != nil {
		log.Fatal("request for pseudo terminal failed: ", err)
	}
}

使用 session.RequestPty 函數請求虛擬終端機。它需要四個參數:終端機類型(在此範例中為 "xterm")、終端機的寬度和高度,以及用於設定終端機模式的 map。在此 map 中,ssh.ECHO 設定為 0,這會停用回顯,而輸入和輸出速度設定為 14.4 kbaud。

設定輸入和輸出

指定遠端 shell 的標準輸入和輸出。以下是一個程式碼片段範例:

package main

import (
	"golang.org/x/crypto/ssh"
	"log"
	"os"
)

func main() {
	config := &ssh.ClientConfig{
		User: "username",
		Auth: []ssh.AuthMethod{
			ssh.Password("password"),
		},
		HostKeyCallback: ssh.InsecureIgnoreHostKey(),
	}

	client, err := ssh.Dial("tcp", "192.168.3.111:22", config)
	if err != nil {
		log.Fatal("Failed to dial: ", err)
	}

	session, err := client.NewSession()
	if err != nil {
		log.Fatal("Failed to create session: ", err)
	}
	defer session.Close()

	modes := ssh.TerminalModes{
		ssh.ECHO:          0,     // 停用回顯
		ssh.TTY_OP_ISPEED: 14400, // 輸入速度 = 14.4kbaud
		ssh.TTY_OP_OSPEED: 14400, // 輸出速度 = 14.4kbaud
	}
	if err := session.RequestPty("linux", 80, 40, modes); err != nil {
		log.Fatal("request for pseudo terminal failed: ", err)
	}

	//設定輸入和輸出
	session.Stdout = os.Stdout
	session.Stdin = os.Stdin
	session.Stderr = os.Stderr
}

啟動遠端 Shell

遠端 shell 現在已準備就緒,您可以使用 session.Shell 函數啟動預設 shell,或使用 session.Run 函數執行特定命令。以下是一個程式碼片段範例:

package main

import (
	"golang.org/x/crypto/ssh"
	"log"
)

func main() {
	config := &ssh.ClientConfig{
		User: "username",
		Auth: []ssh.AuthMethod{
			ssh.Password("password"),
		},
		HostKeyCallback: ssh.InsecureIgnoreHostKey(),
	}

	client, err := ssh.Dial("tcp", "192.168.3.111:22", config)
	if err != nil {
		log.Fatal("Failed to dial: ", err)
	}

	session, err := client.NewSession()
	if err != nil {
		log.Fatal("Failed to create session: ", err)
	}
	defer session.Close()

	modes := ssh.TerminalModes{
		ssh.ECHO:          0,     // 停用回顯
		ssh.TTY_OP_ISPEED: 14400, // 輸入速度 = 14.4kbaud
		ssh.TTY_OP_OSPEED: 14400, // 輸出速度 = 14.4kbaud
	}
	if err := session.RequestPty("linux", 80, 40, modes); err != nil {
		log.Fatal("request for pseudo terminal failed: ", err)
	}
  
    // 設定輸入和輸出
	session.Stdout = os.Stdout
	session.Stdin = os.Stdin
	session.Stderr = os.Stderr

	if err := session.Shell(); err != nil {
		log.Fatal("failed to start shell: ", err)
	}
}

啟動預設 shell,如果啟動失敗,將會傳回錯誤。

等待工作階段結束

使用 session.Wait 函數封鎖直到工作階段結束。以下是一個程式碼片段範例:

package main

import (
	"golang.org/x/crypto/ssh"
	"log"
	"os"
)

func main() {
	config := &ssh.ClientConfig{
		User: "username",
		Auth: []ssh.AuthMethod{
			ssh.Password("password"),
		},
		HostKeyCallback: ssh.InsecureIgnoreHostKey(),
	}

	client, err := ssh.Dial("tcp", "192.168.3.111:22", config)
	if err != nil {
		log.Fatal("Failed to dial: ", err)
	}

	session, err := client.NewSession()
	if err != nil {
		log.Fatal("Failed to create session: ", err)
	}
	defer session.Close()

	modes := ssh.TerminalModes{
		ssh.ECHO:          0,     // 停用回顯
		ssh.TTY_OP_ISPEED: 14400, // 輸入速度 = 14.4kbaud
		ssh.TTY_OP_OSPEED: 14400, // 輸出速度 = 14.4kbaud
	}
	if err := session.RequestPty("linux", 80, 40, modes); err != nil {
		log.Fatal("request for pseudo terminal failed: ", err)
	}

	//設定輸入和輸出
	session.Stdout = os.Stdout
	session.Stdin = os.Stdin
	session.Stderr = os.Stderr

	if err := session.Shell(); err != nil {
		log.Fatal("failed to start shell: ", err)
	}

	err = session.Wait()
	if err != nil {
		log.Fatal("Failed to run: " + err.Error())
	}
}

這樣一來,您就完成了基本 SSH 用戶端的實作。此用戶端可以連線到 SSH 伺服器、啟動遠端 shell,並等待工作階段結束。

總結

本文中實作的 SSH 用戶端僅提供最基本的功能。若要建立功能豐富且使用者友善的 SSH 用戶端,您需要注意許多細節,例如錯誤處理、重新連線、逾時、訊號處理等等。

SSH  GUIDE  GOLANG  SSH CLIENT 

           

  RELATED


  0 COMMENT


No comment for this article.



  RANDOM FUN

What is procedure programming?

After viewing this picture, you should understand what procedure programming is. Want to install air conditioner at higher levels? Give them more ladders.