Skip to content

Instantly share code, notes, and snippets.

@dreampuf
Last active December 28, 2023 06:46
Show Gist options
  • Save dreampuf/429374c448fb9db3815d7ee0d0e467e2 to your computer and use it in GitHub Desktop.
Save dreampuf/429374c448fb9db3815d7ee0d0e467e2 to your computer and use it in GitHub Desktop.
Golang SSHAgent Usage Example
package main
/*
Golang SSHAgent Usage
https://orebibou.com/2019/03/golang%E3%81%A7ssh-agent%E3%81%8B%E3%82%89%E9%8D%B5%E3%82%92%E5%8F%96%E5%BE%97%E3%81%97%E3%81%A6ssh%E6%8E%A5%E7%B6%9A%E3%81%99%E3%82%8B/
*/
import (
"fmt"
"golang.org/x/crypto/ssh"
"golang.org/x/crypto/ssh/agent"
"golang.org/x/crypto/ssh/terminal"
"io/ioutil"
"net"
"os"
"os/signal"
"syscall"
)
var (
host = "target.host"
port = "22"
user = "user"
ssh_agent_key = "/path/to/.ssh/id_rsa" // SshAgentに新たに追加する鍵のPATH(フルパス)
)
func main() {
// SSH_AUTH_SOCKのSocketへ接続し、SshAgentとしてセッションを開始する
sock, err := net.Dial("unix", os.Getenv("SSH_AUTH_SOCK"))
if err != nil {
panic(err)
}
agentsock := agent.NewClient(sock)
// ssh agentに読み込ませる鍵ファイルを読み込む
buf, err := ioutil.ReadFile(ssh_agent_key)
if err != nil {
panic(err)
}
// ssh agentに読み込ませる鍵ファイルをパースする(interface形式)
key, err := ssh.ParseRawPrivateKey(buf)
if err != nil {
panic(err)
}
// SshAgentに鍵を追加する前のリストを出力
fmt.Println(agentsock.List())
// 鍵を追加する
err = agentsock.Add(agent.AddedKey{
PrivateKey: key,
ConfirmBeforeUse: true,
LifetimeSecs: 300, // SshAgentに追加する鍵の利用可能時間(秒)
})
if err != nil {
fmt.Println("add keyring error: %s", err)
}
// SshAgentに鍵追加後のリストを出力
fmt.Println(agentsock.List())
// Create sshClientConfig
signers, err := agentsock.Signers()
if err != nil {
fmt.Println("create signers error: %s", err)
}
sshConfig := &ssh.ClientConfig{
User: user,
Auth: []ssh.AuthMethod{
ssh.PublicKeys(signers...),
},
HostKeyCallback: ssh.InsecureIgnoreHostKey(),
}
// SSH connect.
client, err := ssh.Dial("tcp", host+":"+port, sshConfig)
// Create Session
session, err := client.NewSession()
defer session.Close()
// ssh agentに鍵を転送する
agent.ForwardToAgent(client, agentsock)
agent.RequestAgentForwarding(session)
// キー入力を接続先が認識できる形式に変換する(ここがキモ)
fd := int(os.Stdin.Fd())
state, err := terminal.MakeRaw(fd)
if err != nil {
fmt.Println(err)
}
defer terminal.Restore(fd, state)
// ターミナルサイズの取得
w, h, err := terminal.GetSize(fd)
if err != nil {
fmt.Println(err)
}
modes := ssh.TerminalModes{
ssh.ECHO: 1,
ssh.TTY_OP_ISPEED: 14400,
ssh.TTY_OP_OSPEED: 14400,
}
err = session.RequestPty("xterm", h, w, modes)
if err != nil {
fmt.Println(err)
}
session.Stdout = os.Stdout
session.Stderr = os.Stderr
session.Stdin = os.Stdin
err = session.Shell()
if err != nil {
fmt.Println(err)
}
// ターミナルサイズの変更検知・処理
signal_chan := make(chan os.Signal, 1)
signal.Notify(signal_chan, syscall.SIGWINCH)
go func() {
for {
s := <-signal_chan
switch s {
case syscall.SIGWINCH:
fd := int(os.Stdout.Fd())
w, h, _ = terminal.GetSize(fd)
session.WindowChange(h, w)
}
}
}()
err = session.Wait()
if err != nil {
fmt.Println(err)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment