この記事は、golang チュートリアル というコラムで紹介されており、golang が SSH 接続やその他の関連操作を実装する方法を紹介しています。 Go の ssh 実装に関連する運用上の問題について詳しく説明します。必要としている友人の役に立てば幸いです。
日常的な開発シナリオでは、リモート サーバーと通信する必要があります。通信といくつかの関連コマンド操作の実行現時点では、SSH プロトコルを使用して目的を達成できます。 SSH プロトコルはアプリケーション層に構築されたセキュリティ プロトコルで、正式名は Secure Shell で、送信にはコネクション型の TCP プロトコルを使用するため、安全で信頼性が高くなります。ファイル転送は SSH プロトコルでは完了できず、後述する SFTP プロトコルで完了する必要があることに注意してください。
Go 公式は、golang.org/x/crypto にある SSH 接続を実装するためのパッケージを提供します。これは、プログラム内でパッケージを呼び出すことで提供されます。関連するメソッドを使用して、他のマシンと通信できます。使用する前に、 go get を使用して関連する依存関係パッケージをインポートする必要があります。
go get golang.org/x/crypto/ssh
通信する前に、接続を確立するためのいくつかの関連パラメータを設定する必要もあります。 ssh パッケージの下の ClientConfig 構造は、SSH 接続を確立するために必要ないくつかの構成項目を定義します。一部の項目は、使用時に宣言する必要のないデフォルトのパラメーターを提供します。
以下のコード スニペットでは、最初にユーザー名とパスワードを宣言し、接続タイムアウトを 10 秒に設定し、addr 変数でターゲット マシンの IP アドレスとポートを定義します。 HostKeyCallback 項目を無視するように設定しました。これは、SSH プロトコルがクライアントに 2 つのセキュリティ検証方法を提供しているためです。1 つはパスワード ベースのセキュリティ検証であり、これは私たちがよく使用するアカウント パスワード フォームです。 1 つはキーベースのセキュリティ検証で、前者に比べてセキュリティレベルが大幅に向上しますが、比較的時間がかかるのがデメリットです。 この方法を検証に使用する必要がある場合は、まずサーバー上に自分用のキーのペアを作成する必要があります。クライアントとしてアクセスする場合は、まずサーバーにセキュリティ検証リクエストを送信し、サーバーはリクエストを受信すると、まずマシンに保存されている公開キーとクライアントから送信された公開キーを比較し、それらが一致する場合、サーバーは暗号化されたチャレンジでクライアントに応答します。クライアントは秘密鍵を使用して復号し、復号結果をサーバーに送信し、サーバーは検証を行って応答結果を返します。この時点で、鍵の検証期間が完了します。//添加配置 config := &ssh.ClientConfig{ User: "root", Auth: []ssh.AuthMethod{ssh.Password("Password")}, HostKeyCallback: ssh.InsecureIgnoreHostKey(), Timeout: 10 * time.Second, } } addr := fmt.Sprintf("%v:%v", IP, Port)
//建立SSH连接 sshClient, err := ssh.Dial("tcp", addr, config) if err != nil { log.Fatal("unable to create ssh conn") }
//建立SSH会话 sshSession, err := sshClient.NewSession() if err != nil { log.Fatal("unable to create ssh session") }
Run()、Start()、Output()、CombineOutpt()# を提供しています。 ##、シェル()。 ? このうち、
Output() と **CombineOutpt()** は、Run() メソッドをさまざまな程度にカプセル化し、出力ストリームとエラー ストリームを検証する 2 つのメソッドです。その他の関連コンテンツ。 // Output runs cmd on the remote host and returns its standard output.
func (s *Session) Output(cmd string) ([]byte, error) { if s.Stdout != nil { return nil, errors.New("ssh: Stdout already set")
} var b bytes.Buffer
s.Stdout = &b
err := s.Run(cmd) return b.Bytes(), err
}
// CombinedOutput runs cmd on the remote host and returns its combined
// standard output and standard error.
func (s *Session) CombinedOutput(cmd string) ([]byte, error) { if s.Stdout != nil { return nil, errors.New("ssh: Stdout already set")
} if s.Stderr != nil { return nil, errors.New("ssh: Stderr already set")
} var b singleWriter
s.Stdout = &b
s.Stderr = &b
err := s.Run(cmd) return b.b.Bytes(), err
}
があり、各コマンドの実行後にマシンから返される終了ステータスを保存するために使用されます。興味のある友人はこの作品のコードを見てみることができますが、コードはここには掲載されません。 ここには落とし穴があります。決して停止しないプログラムをリモート マシン上で実行し、そのプログラムがリモート マシンから送信される終了コマンドを待機している場合、プログラムはブロックされてしまいます。 . 正常に戻ることができません。解決策は、コルーチンを使用してこのタスクを個別に実行するか、タイマーを使用してセッションを定期的に終了して通常に戻ることです。
Start()方法与Shell方法一致,都是返回一个error类型,在底层都是调用了start()方法和SendRequest方法,关于这两个方法的内容这里就不做详细介绍了,有兴趣的朋友可以自行去阅读。唯一的区别是Start()方法有一个string类型的参数,用于接收用户输入的参数,而Shell()方法是无参数的。
使用Shell()方法配合RequestPty()等方法可以在本地建立一个伪终端,可以直接通过输入命令的形式操作目标机器。下面都会做一个示例。
//Run func (s *Session) Run(cmd string) error { err := s.Start(cmd) if err != nil { fmt.Println(err) return err } return s.Wait() } // Start runs cmd on the remote host. Typically, the remote // server passes cmd to the shell for interpretation. // A Session only accepts one call to Run, Start or Shell. func (s *Session) Start(cmd string) error { if s.started { return errors.New("ssh: session already started") } req := execMsg{ Command: cmd, } ok, err := s.ch.SendRequest("exec", true, Marshal(&req)) if err == nil && !ok { err = fmt.Errorf("ssh: command %v failed", cmd) } if err != nil { return err } return s.start() }
这里我们使用Run()方法来演示一下如果去执行命令,其他方法类型就不做演示了。这里我们使用一个标准输出流、错误流来保存执行结果。
这里演示了一个简单的执行过程,使用了cd命令到/home/min目录下,在给helloworld程序添加可执行权限,最后运行程序。
var stdoutBuf, stderrBuf bytes.Buffer session.Stdout = &stdoutBuf session.Stderr = &stderrBuf // cd /home/min // chmod +x helloworld // ./helloworld cmd := fmt.Sprintf("cd %v ; chmod +x %v ; %v &", "/home/min", "helloworld", ./helloworld) err := session.Run(cmd) if err != nil { log.Fatal("[ERROR]: ", session.Stderr, err) }
// 设置Terminal Mode modes := ssh.TerminalModes{ ssh.ECHO: 0, // 关闭回显 ssh.TTY_OP_ISPEED: 14400, // 设置传输速率 ssh.TTY_OP_OSPEED: 14400, } // 请求伪终端 err = session.RequestPty("linux", 32, 160, modes) if err != nil { log.Println(err) return } // 设置输入输出 session.Stdout = os.Stdout session.Stdin = os.Stdin session.Stderr = os.Stderr session.Shell() // 启动shell session.Wait() // 等待退出
//机器平台信息type Machine struct { IP string Port string Username string Password string}//建立SSH连接func CreateSSHConn(m *model.Machine) error { //初始化连接信息 config := &ssh.ClientConfig{ User: m.Username, Auth: []ssh.AuthMethod{ssh.Password(m.Password)}, HostKeyCallback: ssh.InsecureIgnoreHostKey(), Timeout: 10 * time.Second, } addr := fmt.Sprintf("%v:%v", m.IP, m.Port) //建立ssh连接 sshClient, err := ssh.Dial("tcp", addr, config) if err != nil { fmt.Println("unable create ssh conn", err) return err } defer sshClient.Close() //建立ssh会话 session, err := sshClient.NewSession() if err != nil { fmt.Println("unable create ssh conn", err) return err } defer session.Close() //执行命令 var stdoutBuf, stderrBuf bytes.Buffer session.Stdout = &stdoutBuf session.Stderr = &stderrBuf cmd := fmt.Sprintf("cd %v ; chmod +x %v ; %v &", "/home/min", "helloworld", ./helloworld) if err := session.Run(cmd); err != nil { log.Fatal("[ERROR]: ", session.Stderr, err) } //创建伪终端 // 设置Terminal Mode modes := ssh.TerminalModes{ ssh.ECHO: 0, // 关闭回显 ssh.TTY_OP_ISPEED: 14400, // 设置传输速率 ssh.TTY_OP_OSPEED: 14400, } // 请求伪终端 err = session.RequestPty("linux", 32, 160, modes) if err != nil { log.Fatal(err) } // 设置输入输出 session.Stdout = os.Stdout session.Stdin = os.Stdin session.Stderr = os.Stderr session.Shell() // 启动shell session.Wait() // 等待退出 return err }
以上がgolang が ssh 関連の操作を実装する方法について詳しく説明した記事の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。