Guide to Implement an SSH Client Using Golang

  sonic0002        2023-11-11 09:19:29       7,987        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

Password is used by another user