Last active
January 30, 2023 01:58
-
-
Save blacknon/8f7e1b06a9a6e0b6f43a10bf29850e3a to your computer and use it in GitHub Desktop.
goでsocks5 proxy経由でsshでシェルに接続する検証・サンプルコード
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package main | |
import ( | |
"fmt" | |
"net" | |
"os" | |
"os/signal" | |
"syscall" | |
"golang.org/x/crypto/ssh" | |
"golang.org/x/crypto/ssh/terminal" | |
"golang.org/x/net/proxy" | |
) | |
// @note: | |
// proxy1 ... プロキシサーバ | |
// target ... プロキシサーバ経由でログインするサーバ | |
func main() { | |
// proxy1の情報 | |
proxy1Host := "proxy1.server.local" | |
proxy1Port := "54321" | |
// proxy1User := "user" // 認証が必要な場合の情報 | |
// proxy1Pass := "password" // 認証が必要な場合の情報 | |
// targetの情報 | |
targetHost := "target.server.local" | |
targetPort := "22" | |
targetUser := "user" | |
targetPass := "password" | |
// sshClientConfigの作成(target) | |
targetSshConfig := &ssh.ClientConfig{ | |
User: targetUser, | |
Auth: []ssh.AuthMethod{ssh.Password(targetPass)}, | |
HostKeyCallback: ssh.InsecureIgnoreHostKey(), | |
} | |
// proxy接続に認証が必要な場合 | |
// auth = &proxy.Auth{ | |
// User: proxy1User, | |
// Password: proxy1Pass, | |
// } | |
// proxy1のDialerを生成 | |
// proxy1Dialer, err := proxy.SOCKS5("tcp", net.JoinHostPort(proxy1Host, proxy1Port), auth, proxy.Direct) // proxy接続に認証が必要な場合 | |
proxy1Dialer, err := proxy.SOCKS5("tcp", net.JoinHostPort(proxy1Host, proxy1Port), nil, proxy.Direct) | |
if err != nil { | |
fmt.Println(err) | |
} | |
// proxy1の*net.Connを生成 | |
proxy1Conn, err := proxy1Dialer.Dial("tcp", net.JoinHostPort(targetHost, targetPort)) | |
if err != nil { | |
fmt.Println(err) | |
} | |
// TargetへのsshClientを作成 | |
pConnect, pChans, pReqs, err := ssh.NewClientConn(proxy1Conn, net.JoinHostPort(targetHost, targetPort), targetSshConfig) | |
if err != nil { | |
fmt.Println(err) | |
} | |
client := ssh.NewClient(pConnect, pChans, pReqs) | |
// ----------------------------------- | |
// ↓以降はプロキシの処理とは無関係なコード | |
// ----------------------------------- | |
// 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) | |
} | |
// sessionの標準入出力の処理 | |
session.Stdout = os.Stdout | |
session.Stderr = os.Stderr | |
session.Stdin = os.Stdin | |
// ssh接続先でShellの起動 | |
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