【golang】SSHサーバー(最低限の認証機能付き)をつくる

# なにこれ
先程、無事23歳を迎えました。記念に一発記事かきます。
今回は、SSHサーバーをGoで書いてみます。

機能としては、ユーザー名とサーバー側で用意したキー(opensslで生成)で
認証をします。

ある用途(SSOで認証し、SSH経由でコマンドを実行するもの)用に
書いたものです。ご紹介するコードは、最低限度の機能しかありません。

※まだ、pty(pseudo-terminal)とのパイプ部分がないので、できたら追記します
ここらへんを参考にやります。
https://gist.github.com/jpillora/b480fde82bff51a06238

# プログラム
- ユーザー名はadminを想定
- 同じフォルダのid_rsa.pubを認証に利用
- パスワード認証は実装していません
- SSHのポートは2222を利用

```go
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))

}
```

# 使ってみる

```bash
# キーの生成
$ 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サーバー側のログもハッておきます。

```bash

$ 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}
```