Heim > Artikel > Backend-Entwicklung > In einem Artikel wird ausführlich erläutert, wie Golang SSH-bezogene Vorgänge implementiert
Dieser Artikel wurde in der Kolumne „Golang-Tutorial“ verfasst, um Ihnen vorzustellen, wie Golang SSH-Verbindungen und andere verwandte Vorgänge implementiert. Ich frage mich, wie viel Sie über SSH wissen. Lassen Sie mich ausführlich über die betrieblichen Probleme im Zusammenhang mit der SSH-Implementierung in Go sprechen. Ich hoffe, dass es Freunden, die es benötigen, hilfreich sein wird!
1.ssh
1.2 Go-Implementierung
go get golang.org/x/crypto/ssh
1.2.1 Zugehörige Parameter konfigurieren
für den Verbindungsaufbau konfigurieren. Die ClientConfig-Struktur unter dem SSH-Paket definiert einige Konfigurationselemente, die zum Herstellen einer SSH-Verbindung erforderlich sind. Einige Elemente stellen Standardparameter bereit, die wir bei ihrer Verwendung nicht deklarieren müssen. Im folgenden Codeausschnitt deklarieren wir zunächst den Benutzernamen und das Passwort, legen das Verbindungszeitlimit auf 10 Sekunden fest und die Variable addr definiert die IP-Adresse und den Port des Zielcomputers.
HostKeyCallback-Element, wir haben es auf Ignorieren eingestellt, weil das SSH-Protokoll zwei Sicherheitsüberprüfungsmethoden für den Client bereitstellt. Eine davon ist die passwortbasierte Sicherheitsüberprüfung, die wir häufig verwenden, und die andere ist „Verglichen“. Diese Form der Überprüfungsmethode ist die erste Art der schlüsselbasierten Sicherheitsüberprüfung und verbessert das Sicherheitsniveau erheblich. Der Nachteil besteht jedoch darin, dass sie relativ lange dauert.
Wenn wir diese Methode zur Überprüfung verwenden müssen, müssen wir zunächst ein Schlüsselpaar für uns selbst auf dem Server erstellen. Beim Zugriff als Client senden wir zunächst eine Sicherheitsüberprüfungsanfrage an den Server Anfrage: Zunächst wird der auf dem Computer gespeicherte öffentliche Schlüssel mit dem vom Client gesendeten öffentlichen Schlüssel verglichen. Wenn diese konsistent sind, antwortet der Server dem Client mit einer Verschlüsselungsaufforderung. Nach Erhalt der Herausforderung verwendet der Client den privaten Schlüssel zu entschlüsseln und dann das Ergebnis zu entschlüsseln, und der Server führt eine Überprüfung durch und gibt dann das Antwortergebnis zurück. An diesem Punkt ist ein Zeitraum der Schlüsselüberprüfung abgeschlossen.
//添加配置 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)
1.2.2 Herstellen einer Verbindung
func Dial(network, addr string, config *ClientConfig) (*Client, error)
//建立SSH连接 sshClient, err := ssh.Dial("tcp", addr, config) if err != nil { log.Fatal("unable to create ssh conn") }
1.2.3 Sitzung erstellen
//建立SSH会话 sshSession, err := sshClient.NewSession() if err != nil { log.Fatal("unable to create ssh session") }
1.2.4 Vorgänge ausführen
, Start(), Output(), CombineOutpt(), Shell(). ? Darunter sind
Output() und **CombineOutpt()** zwei Methoden, die die Run()-Methode in unterschiedlichem Maße kapseln und den Ausgabestream, den Fehlerstream und andere verwandte Inhalte überprüfen. Die // 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
}
Run()-Methode kapselt die Start()-Methode und fügt eine Wait-Methode hinzu, um den Exit-Befehl des Remote-Servers zu überprüfen. In der Wait()-Methode gibt es eine Variable vom Typ Pipeline
, die zum Speichern des von der Maschine nach jeder Befehlsausführung zurückgegebenen Exit-Status verwendet wird. Interessierte Freunde können sich den Code dieses Artikels ansehen, der Code wird jedoch hier nicht veröffentlicht. Hier besteht eine Gefahr, wenn wir ein Programm auf einem Remote-Computer ausführen, das niemals stoppt, und unser Programm nicht auf den vom Remote-Computer gesendeten Exit-Befehl wartet. Dies führt dazu, dass das Programm blockiert wird und nicht zurückkehren kann normalerweise. Die Lösung besteht darin, eine Coroutine zu verwenden, um diese Aufgabe separat auszuführen, oder einen Timer zu verwenden, um die Sitzung regelmäßig zu beenden, um zur Normalität zurückzukehren.
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 }
Das obige ist der detaillierte Inhalt vonIn einem Artikel wird ausführlich erläutert, wie Golang SSH-bezogene Vorgänge implementiert. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!