Goでターミナル接続sshクライアントを作成する(x11 forwarding対応)
Pocket

Goでsshのクライアントコマンドを作ってるのだが、それにx11 forwarding機能を実装したかったのでいろいろと調べてみた。
で、以下のような通信の流れになっているので、それを踏まえて実装してみた。

  1.  sshクライアント=>sshサーバ: sshクライアント側からsshサーバ側に、「SSH_MSG_CHANNEL_REQUEST」でx11-reqを送る(RFC4254)
  2.  sshサーバ: x11の初期化処理が行われる(sshサーバ側のDISPLAY環境変数にlocalhost:6000+x11ディスプレイ番号が入り、ポートが開く)
  3.  sshサーバ => sshクライアント: sshサーバ側でx11を使うアプリケーションを動作させると、sshサーバ側のDISPLAY環境変数にxプロトコルのデータを送る(RFC4254)
    (sshクライアント側では、そのデータの受け皿を用意しておく必要がある)
  4. sshクライアント: sshサーバ側から送られてきたxプロトコルのデータを受け付け、sshクライアント側のDISPLAY環境変数宛にSocket通信でデータ転送をする
    (sshクライアント側で、そのSocketへの転送処理をする必要がある)
Sponsored Links

ざっくり書くとこんな感じ。
で、以下のようなコードで実装ができる(動作するコードはこちらに全文を上げてる)。

// sshサーバ側から受け付けたx11プロトコルの通信をそのままSocketに転送する関数
func forwardX11Socket(channel ssh.Channel) {
	conn, err := net.Dial("unix", os.Getenv("DISPLAY"))
	if err != nil {
		return
	}

	var wg sync.WaitGroup
	wg.Add(2)
	go func() {
		io.Copy(conn, channel)
		conn.(*net.UnixConn).CloseWrite()
		wg.Done()
	}()
	go func() {
		io.Copy(channel, conn)
		channel.CloseWrite()
		wg.Done()
	}()

	wg.Wait()
	conn.Close()
	channel.Close()
}

// x11-req Requestで送信するリクエストの中身を生成するためのStruct
type x11request struct {
    SingleConnection bool
    AuthProtocol     string
    AuthCookie       string
    ScreenNumber     uint32
}

func main() {
    (snip)

    // x11-req Requestで送信するデータを作成(AuthCookieはランダムな値)
    payload := x11request{
        SingleConnection: false,
        AuthProtocol:     string("MIT-MAGIC-COOKIE-1"),
        AuthCookie:       string(NewSHA1Hash()),
        ScreenNumber:     uint32(0),
    }

    // x11-req Requestを送信
    ok, err := session.SendRequest("x11-req", true, ssh.Marshal(payload))
    if err == nil && !ok {
        fmt.Println(errors.New("ssh: x11-req failed"))
    } else {
        // sshサーバ側から送られるx11プロトコルのデータを受け付ける処理
        x11channels := client.HandleChannelOpen("x11")

        go func() {
            for ch := range x11channels {
                channel, _, err := ch.Accept()
                if err != nil {
                    continue
                }

                go forwardX11Socket(channel)
            }
        }()
    }

}

 

こっちにある動作するコードを使って接続するとx11 forwardingを使ってログインシェルに接続されるので、xeyesとか実行すると動作するのが確認できる。
実装にあたりPythonのParamikoのコードとかも参考にしてみたのだけど、これだとどうも動かないのが気になる。2012年とかのコードなので、ライブラリとかも変わってしまって動かないのかな(´・ω・`)。

実は実装するの(+理解するの)に結構苦労したのだけど、出来てみるとそんなにコードも多いわけでもなかった(自分がアホなだけ)。

 

【参考】

 

Pocket

Written by blacknon

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

Leave a Comment

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

*