なにこれ
先程、無事23歳を迎えました。記念に一発記事かきます。
今回は、SSHサーバーをGoで書いてみます。
機能としては、ユーザー名とサーバー側で用意したキー(opensslで生成)で
認証をします。
ある用途(SSOで認証し、SSH経由でコマンドを実行するもの)用に
書いたものです。ご紹介するコードは、最低限度の機能しかありません。
※まだ、pty(pseudo-terminal)とのパイプ部分がないので、できたら追記します
ここらへんを参考にやります。
https://gist.github.com/jpillora/b480fde82bff51a06238
プログラム
- ユーザー名はadminを想定
- 同じフォルダのid_rsa.pubを認証に利用
- パスワード認証は実装していません
- SSHのポートは2222を利用
package main
import (
"io"
"io/ioutil"
"log"
b64 "encoding/base64"
"github.com/gliderlabs/ssh"
gossh "golang.org/x/crypto/ssh"
)
func main() {
ssh.Handle(func(s ssh.Session) {
user := s.User()
keyType := s.PublicKey().Type()
addr := s.RemoteAddr()
publicKeyString := keyType + " " + b64.StdEncoding.EncodeToString(s.PublicKey().Marshal())
io.WriteString(s, "<Session Information>\n")
io.WriteString(s, "Hello "+user+" "+addr.String()+" \n")
io.WriteString(s, "your publicKey:\n")
io.WriteString(s, publicKeyString+"\n")
io.WriteString(s, "<Environments>\n")
for _, s2 := range(s.Environ()) {
io.WriteString(s, s2 + "\n")
}
io.WriteString(s, "<Sent Command>\n")
for _, s2 := range(s.Command()) {
io.WriteString(s, s2 + "\n")
}
})
publicKeyHandler := ssh.PublicKeyAuth(func(user string, key ssh.PublicKey) bool {
data, err := ioutil.ReadFile("id_rsa.pub")
if err != nil {
log.Fatal("Error: ioutil.ReadFile")
}
allowed, _, _, _, err := ssh.ParseAuthorizedKey(data)
if err != nil {
log.Fatal("Error: ssh.ParseAuthorizedKey")
}
log.Println("user", user)
log.Println("key", gossh.FingerprintLegacyMD5(key))
log.Println("allowed", allowed)
return user == "admin" && ssh.KeysEqual(key, allowed)
})
log.Fatal(ssh.ListenAndServe(":2222", nil, publicKeyHandler))
}
使ってみる
# キーの生成
$ ssh-keygen -t rsa
# SSHサーバー起動
$ go run main.go
# SSHサーバーに接続
$ ssh -p 2222 admin@localhost -i ./id_rsa
<Session Information>
Hello admin 127.0.0.1:34790
your publicKey:
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC47K6l2/ugU3cqvpo/xoXKyEyRRcCn7hifngMa4VI59Bef0pv/stQaE+ioGvHqJeOOxzhB6V2m3FTNsZWiOlIa8VgomxYx2VMEheerNRaF2cBrKKIMc7hLzqAA8YblcnD14WTOBPl7ZP61x6FCh9ppEgCMuLz271kw1iMM4BNvGbPLBYddTPf+B0mdsPsYxSOkHtMiUC672aKti/JLA1oI8isspXQ92aDGXS9lF5IfPD0kPC1C7HoJHjMe7AufOXcrb+wjTwkBCvsg2Nzc4UqioSmoWz+JEw1U9nRFmWipS8oslS4Y9lP/3N2FhHQe5bNS3LkgdKLMPVWEtcm7/ejV
<Environments>
LANG=en_US.UTF-8
LC_CTYPE=ja_JP.UTF-8
<Sent Command>
Connection to localhost closed.
# 想定ユーザー(admin以外)でログインしてみる
$ ssh -p 2222 wea@localhost -i ./id_rsa
Permission denied (publickey).
# 異なる鍵でログインしてみる
$ ssh -p 2222 admin@localhost -i ./xxxxx
Permission denied (publickey).
一応、SSHサーバー側のログもハッておきます。
$ go run main.go
2017/01/14 19:05:28 user user
2017/01/14 19:05:28 key dc:5f:f5:67:4e:e8:c8:76:79:a7:ca:92:62:3b:76:ac
2017/01/14 19:05:28 allowed &{23344560465533452981709943898161005919731392830590105606963309150551630551302414730708970948200481816141922749907637272225626490155159783569434797845519793423325995710694707976227032104697777390896339738452235226668962416200276176275276550953440317310944541408238121689915748449794216812036291612634533745336567014294425345858468318042821685112433734625161260430358579385185049925298115208981611271949350328216520828666256777214457602526303073117380005636162594316795144700687451939116284229189559669338016574786225894545539656338668059662112144588948659920332018782076376316484352527749295572657988311248141136292053 65537}