Golangでターミナル接続sshクライアントを作成する(証明書認証方式)
Pocket

Golangで、自前のsshのクライアントを作ってるのだけど、それに証明書認証の機能を追加したかったので調べてた。
で、以下のようなコードでできるようになったので備忘で残しておく(一応こちらにもソースを置いておく)。

Sponsored Links

● ssh_term_cert.go

package main
import (
"fmt"
"io"
"io/ioutil"
"os"
"os/signal"
"os/user"
"strings"
"syscall"
"golang.org/x/crypto/ssh"
"golang.org/x/crypto/ssh/terminal"
)
func main() {
Host := "172.20.100.122"
Port := "22"
User := "blacknon"
Cert := "/Users/blacknon/.ssh/user.key-cert.pub"
Certkey := "/Users/blacknon/.ssh/user.key"
// PATHをフルパスへ変換する
usr, _ := user.Current()
cert := strings.Replace(Cert, "~", usr.HomeDir, 1)
certkey := strings.Replace(Certkey, "~", usr.HomeDir, 1)
// 秘密鍵のSignerを作成する
keyData, err := ioutil.ReadFile(certkey)
if err != nil {
fmt.Println(err)
os.Exit(1)
}
keySigner, _ := ssh.ParsePrivateKey(keyData)
// 証明書を読み込む
certData, err := ioutil.ReadFile(cert)
if err != nil {
fmt.Println(err)
os.Exit(1)
}
// 証明書から公開鍵を取得する
pubkey, _, _, _, err := ssh.ParseAuthorizedKey(certData)
if err != nil {
fmt.Println(err)
os.Exit(1)
}
// 証明書をデータとして取得
certificate, ok := pubkey.(*ssh.Certificate)
if !ok {
fmt.Println("ng")
os.Exit(1)
}
// 証明書からsignerを作成する
signer, err := ssh.NewCertSigner(certificate, keySigner)
// authを作成する
var auth []ssh.AuthMethod
// ssh.PublicKeys(signers)
auth = append(auth, ssh.PublicKeys(signer))
// Create sshClientConfig
sshConfig := &ssh.ClientConfig{
User:            User,
Auth:            auth,
HostKeyCallback: ssh.InsecureIgnoreHostKey(),
}
// SSH connect.
client, err := ssh.Dial("tcp", Host+":"+Port, sshConfig)
// Create Session
session, err := client.NewSession()
defer session.Close()
// キー入力を接続先が認識できる形式に変換する(ここがキモ)
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)
}
// log := new(bytes.Buffer)
logFile, _ := os.OpenFile("./ssh_term_with_log.log", os.O_RDWR|os.O_CREATE|os.O_APPEND, 0600)
session.Stdout = io.MultiWriter(os.Stdout, logFile)
session.Stderr = io.MultiWriter(os.Stderr, logFile)
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)
}
}

 

Pocket

Written by blacknon

インフラエンジニア(…のつもり)。 仕事で使うならクライアントはWindowsよりはUNIXの方が好き。 大体いつも眠い。

This article has 1 comments

  1. Pingback: sshで証明書認証で接続ができるようにする | 俺的備忘録 〜なんかいろいろ〜

Leave a Comment

メールアドレスが公開されることはありません。

*