-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathmain.go
128 lines (103 loc) · 2.65 KB
/
main.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
// SPDX-FileCopyrightText: 2024 Tillitis AB <tillitis.se>
// SPDX-License-Identifier: BSD-2-Clause
package main
import (
"bufio"
"flag"
"fmt"
"log"
"os"
"strings"
"github.com/gliderlabs/ssh"
gossh "golang.org/x/crypto/ssh"
)
func sessionHandler(s ssh.Session) {
// Generate a cert that allows the user to login as the user
// they used here.
cert, err := genCert(s.PublicKey(), s.User(), s.User())
if err != nil {
log.Printf("cert: couldn't generate cert: %v", err)
cert = []byte("server error")
}
if _, err := s.Write(cert); err != nil {
log.Printf("couldn't send: %v", err)
}
}
func authHandler(userPubKeys map[string]UserPubKey) ssh.PublicKeyHandler {
return ssh.PublicKeyHandler(func(ctx ssh.Context, key ssh.PublicKey) bool {
mKey := string(gossh.MarshalAuthorizedKey(key))
// Drop the newline
mKey = mKey[:len(mKey)-1]
// If the presented pubkey is allowed to get a cert,
// let them continue with a challenge/response. If
// not, just stop here.
if _, ok := userPubKeys[mKey]; ok {
return true
}
return false
})
}
type UserPubKey struct {
PubKey string
KeyID string
}
type Config struct {
HostKey []byte
UserPubKeys map[string]UserPubKey
}
func loadConfig(authFileName string) (Config, error) {
var err error
conf := Config{
UserPubKeys: map[string]UserPubKey{},
}
// Host private key
conf.HostKey, err = os.ReadFile("host_ed25519")
if err != nil {
return conf, fmt.Errorf("private key file: %w", err)
}
// Get and parse list of authorized keys
file, err := os.Open(authFileName)
if err != nil {
return conf, fmt.Errorf("%w", err)
}
defer file.Close()
sc := bufio.NewScanner(file)
for sc.Scan() {
words := strings.Fields(sc.Text())
// Check length, should be 3.
if len(words) != 3 {
return conf, fmt.Errorf("parse error")
}
k := words[0] + " " + words[1]
uPubKey := UserPubKey{
PubKey: k,
KeyID: words[2],
}
conf.UserPubKeys[k] = uPubKey
}
if err := sc.Err(); err != nil {
return conf, fmt.Errorf("%w", err)
}
return conf, nil
}
func main() {
var confFileFlag = flag.String("c", "./authorized_keys", "")
var listenFlag = flag.String("l", "127.0.0.1:2222", "")
flag.Parse()
conf, err := loadConfig(*confFileFlag)
if err != nil {
log.Fatalf("couldn't load config file: %v", err)
}
hostKeySigner, err := gossh.ParsePrivateKey(conf.HostKey)
if err != nil {
log.Fatalf("unable to parse private key: %v", err)
}
s := &ssh.Server{
Addr: *listenFlag,
Handler: sessionHandler,
PublicKeyHandler: authHandler(conf.UserPubKeys),
}
s.AddHostKey(hostKeySigner)
log.Printf("starting ssh server on %v", *listenFlag)
log.Fatal(s.ListenAndServe())
}