LoginSignup
17
11

More than 5 years have passed since last update.

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

Last updated at Posted at 2017-01-14

なにこれ

先程、無事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}
17
11
3

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
17
11