SSH Server Util#
PublicKeyCallback#
在 golang/ssh-server 中的 ssh 伺服器是沒有驗證使用者身份的,因為在 ssh.ServerConfig 中設定了 NoClientAuth: true。用下面的 ssh.SererConfig 可以設定用只有指定的使用者可以進入,不過這個版本中沒有處理時序攻擊(Timming attack),也就是用比對公鑰的時間不同獲取資訊的攻擊手段。
var allowedUser = map[string][]string{
	"simbafs": {
		"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIHcLVJDmYggMFXJ3CqMOSMnBkkDX1982cdd3rmRqfpMC simba@simba-nb",
	},
}
func CompareKey(key ssh.PublicKey, pubKeyStr string) bool {
	// compare two keys
	pubKey, _, _, _, err := ssh.ParseAuthorizedKey([]byte(pubKeyStr))
	if err != nil {
		return false
	}
	return ssh.FingerprintSHA256(key) == ssh.FingerprintSHA256(pubKey)
}
sshConf := &ssh.ServerConfig{
	NoClientAuth: false,
	PublicKeyCallback: func(conn ssh.ConnMetadata, key ssh.PublicKey) (*ssh.Permissions, error) {
		// find if the public key is in the allowed list
		for user, keys := range allowedUser {
			for _, pubKey := range keys {
				if CompareKey(key, pubKey) {
					log.Printf("User %q authenticated with key %s\n", user, ssh.FingerprintSHA256(key))
					return &ssh.Permissions{
						Extensions: map[string]string{
							"user":  user,
							"pk-fp": ssh.FingerprintSHA256(key),
						},
					}, nil
				}
			}
		}
		return nil, fmt.Errorf("unknown public key for %q", conn.User())
	},
}goParsePayload#
payload 格式是四個 byte 代表長度,接著是一串資料,如果有第二段就重複以上,例如 [0 0 0 3 65 66 67] 的 [0 0 0 3] 代表接下來資料長度是 3,然後是資料 [65 66 67],也就是 ABC
func ParsePayload(payload []byte) []string {
	result := []string{}
	var index uint32 = 0
	min := func(a uint32, b uint32) uint32 {
		if a < b {
			return a
		}
		return b
	}
	for index < uint32(len(payload)) {
		var length uint32
		length = min(
			uint32(payload[index])<<24|uint32(payload[index+1])<<16|uint32(payload[index+2])<<8|uint32(payload[index+3]),
			uint32(len(payload))-index-4,
		)
		result = append(result, string(payload[index+4:index+4+length]))
		index += 4 + length
	}
	return result
}go