Skip to content

Commit 017579f

Browse files
committed
add beginings of ssh support
1 parent 85e60cf commit 017579f

13 files changed

+288
-24
lines changed

Gopkg.lock

+13-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Gopkg.toml

+8-23
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,3 @@
1-
2-
# Gopkg.toml example
3-
#
4-
# Refer to https://github.com/golang/dep/blob/master/docs/Gopkg.toml.md
5-
# for detailed Gopkg.toml documentation.
6-
#
7-
# required = ["github.com/user/thing/cmd/thing"]
8-
# ignored = ["github.com/user/project/pkgX", "bitbucket.org/user/project/pkgA/pkgY"]
9-
#
10-
# [[constraint]]
11-
# name = "github.com/user/project"
12-
# version = "1.0.0"
13-
#
14-
# [[constraint]]
15-
# name = "github.com/user/project2"
16-
# branch = "dev"
17-
# source = "github.com/myfork/project2"
18-
#
19-
# [[override]]
20-
# name = "github.com/x/y"
21-
# version = "2.4.0"
22-
23-
241
[[constraint]]
252
branch = "master"
263
name = "github.com/c2stack/c2g"
@@ -36,3 +13,11 @@
3613
[[constraint]]
3714
branch = "master"
3815
name = "github.com/mattn/anko"
16+
17+
[[constraint]]
18+
branch = "master"
19+
name = "golang.org/x/crypto"
20+
21+
[[constraint]]
22+
branch = "master"
23+
name = "golang.org/x/sys"

ssh/mgmt.go

+19
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
package ssh
2+
3+
import (
4+
"github.com/c2stack/c2g/node"
5+
"github.com/c2stack/c2g/nodes"
6+
)
7+
8+
func Manage(s *Service) node.Node {
9+
o := s.Options()
10+
return &nodes.Extend{
11+
Base: nodes.ReflectChild(&o),
12+
OnEndEdit: func(p node.Node, r node.NodeRequest) error {
13+
if err := p.EndEdit(r); err != nil {
14+
return err
15+
}
16+
return s.Apply(o)
17+
},
18+
}
19+
}

ssh/mgmt_test.go

+25
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
package ssh
2+
3+
import (
4+
"testing"
5+
6+
"github.com/c2stack/c2g/nodes"
7+
8+
"github.com/c2stack/c2g/meta"
9+
"github.com/c2stack/c2g/meta/yang"
10+
"github.com/c2stack/c2g/node"
11+
)
12+
13+
func TestManage(t *testing.T) {
14+
s := NewService()
15+
model := yang.RequireModule(&meta.FileStreamSource{Root: "../yang"}, "ssh")
16+
b := node.NewBrowser(model, Manage(s))
17+
config := `{
18+
"address" : "127.0.0.1:2202",
19+
"hostKeyFiles" : ["testdata/server_key_rsa"],
20+
"authorizedKeysFile" : "testdata/authorized_keys"
21+
}`
22+
if err := b.Root().InsertFrom(nodes.ReadJSON(config)).LastErr; err != nil {
23+
t.Fatal(err)
24+
}
25+
}

ssh/service.go

+129
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
package ssh
2+
3+
import (
4+
"fmt"
5+
"io/ioutil"
6+
"log"
7+
"net"
8+
9+
"github.com/c2stack/c2g/c2"
10+
11+
gssh "golang.org/x/crypto/ssh"
12+
"golang.org/x/crypto/ssh/terminal"
13+
)
14+
15+
type Service struct {
16+
config *gssh.ServerConfig
17+
authorizedKeys map[string]struct{}
18+
stop func()
19+
options Options
20+
}
21+
22+
type Options struct {
23+
Address string
24+
HostKeyFiles []string
25+
AuthorizedKeysFile string
26+
}
27+
28+
func NewService() *Service {
29+
s := &Service{}
30+
return s
31+
}
32+
33+
func (self *Service) Options() Options {
34+
return self.options
35+
}
36+
37+
func (self *Service) Apply(options Options) error {
38+
if self.stop != nil {
39+
self.stop()
40+
}
41+
self.config = &gssh.ServerConfig{
42+
PasswordCallback: self.passwordAuth,
43+
PublicKeyCallback: self.keyAuth,
44+
}
45+
for _, k := range options.HostKeyFiles {
46+
privateBytes, err := ioutil.ReadFile(k)
47+
if err != nil {
48+
return c2.NewErr(fmt.Sprintf("Failed to load private key %s : %v", k, err))
49+
}
50+
private, err := gssh.ParsePrivateKey(privateBytes)
51+
if err != nil {
52+
return c2.NewErr(fmt.Sprintf("Failed to parse private key %s: %v", k, err))
53+
}
54+
self.config.AddHostKey(private)
55+
}
56+
57+
authBytes, err := ioutil.ReadFile(options.AuthorizedKeysFile)
58+
if err != nil {
59+
return c2.NewErr(fmt.Sprintf("Failed to read authorized file %s : %v", options.AuthorizedKeysFile, err))
60+
}
61+
self.authorizedKeys = make(map[string]struct{})
62+
for len(authBytes) > 0 {
63+
pubKey, _, _, rest, err := gssh.ParseAuthorizedKey(authBytes)
64+
if err != nil {
65+
log.Fatal(err)
66+
}
67+
self.authorizedKeys[string(pubKey.Marshal())] = struct{}{}
68+
authBytes = rest
69+
}
70+
71+
self.options = options
72+
go self.start()
73+
return nil
74+
}
75+
76+
func (self *Service) passwordAuth(c gssh.ConnMetadata, pass []byte) (*gssh.Permissions, error) {
77+
return nil, nil
78+
}
79+
80+
func (self *Service) keyAuth(c gssh.ConnMetadata, pubKey gssh.PublicKey) (*gssh.Permissions, error) {
81+
return nil, nil
82+
}
83+
84+
func (self *Service) start() error {
85+
l, err := net.Listen("tcp", self.options.Address)
86+
if err != nil {
87+
return err
88+
}
89+
90+
c, err := l.Accept()
91+
if err != nil {
92+
return err
93+
}
94+
95+
_, chans, reqs, err := gssh.NewServerConn(c, self.config)
96+
if err != nil {
97+
return err
98+
}
99+
100+
go gssh.DiscardRequests(reqs)
101+
102+
for newChannel := range chans {
103+
fmt.Println("new channel")
104+
if newChannel.ChannelType() != "session" {
105+
newChannel.Reject(gssh.UnknownChannelType, "unknown channel type")
106+
}
107+
ch, req, err := newChannel.Accept()
108+
if err != nil {
109+
log.Fatalf("Could not accept channel: %v", err)
110+
}
111+
go func(in <-chan *gssh.Request) {
112+
for req := range in {
113+
req.Reply(req.Type == "shell", nil)
114+
}
115+
}(req)
116+
term := terminal.NewTerminal(ch, "> ")
117+
go func() {
118+
defer ch.Close()
119+
for {
120+
line, err := term.ReadLine()
121+
if err != nil {
122+
break
123+
}
124+
fmt.Println(line)
125+
}
126+
}()
127+
}
128+
return nil
129+
}

ssh/service_test.go

+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
package ssh
2+
3+
import "testing"
4+
5+
func TestService(t *testing.T) {
6+
s := NewService()
7+
o := s.Options()
8+
o.Address = "0.0.0.0:2202"
9+
o.HostKeyFiles = []string{
10+
"testdata/server_key_rsa",
11+
}
12+
o.AuthorizedKeysFile = "testdata/authorized_keys"
13+
if err := s.Apply(o); err != nil {
14+
t.Fatal(err)
15+
}
16+
}

ssh/testdata/ans/hosts

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
localhost:2202

ssh/testdata/authorized_keys

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDgP5udRUU8wLJjn92qBj6Vrja/GeolvyikOxQhCfBMwcnmua3Hc0CFatguEguFjdTlh/vJSLS6/gcg+GrDfXLealQhn0K+m3NugTVa8RlUUHx5gySO7vG8lcn/rkgWUZ1N/HVl1G1KknvZm+aRj83rQPjvCFzIvPrQ3uZM0spf+/g2FPt+KI7S1AdmSrPEI0/rj4wu5YrjnMUtStwLYB8iYA+QNT/UuuYIhW3Wl2kGt2K0T0HaYsi0hGVCe1+qrdoCHyDuSHFuRs9b68DOzMGPyREoyu5yCaxI+e2WN3DCeoZ5w3fMe/dGn/KhL45x2258TGnO2ITDNRTbV865DqUx [email protected]

ssh/testdata/client_rsa

+27
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
-----BEGIN RSA PRIVATE KEY-----
2+
MIIEpAIBAAKCAQEA4D+bnUVFPMCyY5/dqgY+la42vxnqJb8opDsUIQnwTMHJ5rmt
3+
x3NAhWrYLhILhY3U5Yf7yUi0uv4HIPhqw31y3mpUIZ9CvptzboE1WvEZVFB8eYMk
4+
ju7xvJXJ/65IFlGdTfx1ZdRtSpJ72ZvmkY/N60D47whcyLz60N7mTNLKX/v4NhT7
5+
fiiO0tQHZkqzxCNP64+MLuWK45zFLUrcC2AfImAPkDU/1LrmCIVt1pdpBrditE9B
6+
2mLItIRlQntfqq3aAh8g7khxbkbPW+vAzszBj8kRKMrucgmsSPntljdwwnqGecN3
7+
zHv3Rp/yoS+OcdtufExpztiEwzUU21fOuQ6lMQIDAQABAoIBAG5RvIuNVXeC0P/D
8+
2PfZJJbcYuB2rkMtnJ/W1JtAWXBZcatJM13IrDg2jO11QSfN06urz0mBtC+94uHs
9+
dBGCOK2En6j+wYYl0Y7Oj+ISdESPZ/0bcDPFBzWgdwSKx7n7IqkIBvU2oSGrmLRA
10+
RxjbJxGSICTv9z1mAvoYfjHTDW5UCkRLwvWrjcxHfmCSUpiJKbFJFMbaNftzoo67
11+
+UwX6XfmM/xO41fNRkMzySbdukgItzZ7lrhtZrrzhealECduuMLn+mIDkXfysApw
12+
1pbYIm//DzGx6+tQ3PTWOTdzzd450nixw2djsrBIyLM72QkksUf8qsT0CTZPEyyR
13+
fGXgoOUCgYEA9Sncd6OEEMor6Ni8K1SU3eLLgVI/gKGVlppxiNwb5Tj8TtIr0SXT
14+
eWrIbUr5dtZhu5fLgyT1it45H47fkHf97a47Sp82E0ot3KU42akeRuvvFMLaY18N
15+
8F/3ogO63vtc0CTffCionrYLMw9Y/WbSzEPtIS4WwYYkVwCd9AFhj/cCgYEA6ikV
16+
SlDiSYTGjo6RS3jXJuygpP9jFvDsBLfIUyDmqZF6dnk8a02bmlDwtr51g2zGgUA9
17+
OfbcZzHdyTjEHYxHCBTnBtz+ypODWtUeY6DUDMM0Eu0vI5BWRiK1f0X/nKnf8JvL
18+
cGQ1N1vm6eQL8D8FN0zNgOk+mN/00e4YR40JehcCgYAU7wogPRodxOWS8E7A9Gvx
19+
tlfiJv+9IA8B2RYwtXq8S+1fXZrYNP6ls3SBwJEkkJGvzvpVrGY7AVanVy/Hyjco
20+
gGQXrxiS9RaNbJ46F7K6I5M6lpjHO2qZR9iBQQhH2fbG3x8mHuKnCqxSI9FZcdo2
21+
DubEmobe53Fa7HaQSz3laQKBgQDa809VDgyJcf23jteNGpETdG9B2QtuiBgo0TAf
22+
isQkCkPYQ8SbERZOVSC/v5diLHPwp4CYkpvEYnxfixTWDDTe+ayMXlhgU06fwGeK
23+
APhGdsBaci2Bs5T4P7w2Jd29P5qGASdZEFoySVzBltsS6dqWutntkCtYBxGEun05
24+
akdSQQKBgQCj6VKIVE9GgQhZBjOu5bPdk8tns5dcymr6eD3cJ5C70S8cTi4n0BCC
25+
f+4IIeVY/j+EzvvlvZT2LYgWBEDn2Ajt+SndrthWYJBxKxB5Mnt0P7ixAOmj4+kJ
26+
EXf0mCWstCVA+LOoMEAjEmIgvoFl1gqz9RGhOh+tb+PqlYvT3hwt6A==
27+
-----END RSA PRIVATE KEY-----

ssh/testdata/client_rsa.pub

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDgP5udRUU8wLJjn92qBj6Vrja/GeolvyikOxQhCfBMwcnmua3Hc0CFatguEguFjdTlh/vJSLS6/gcg+GrDfXLealQhn0K+m3NugTVa8RlUUHx5gySO7vG8lcn/rkgWUZ1N/HVl1G1KknvZm+aRj83rQPjvCFzIvPrQ3uZM0spf+/g2FPt+KI7S1AdmSrPEI0/rj4wu5YrjnMUtStwLYB8iYA+QNT/UuuYIhW3Wl2kGt2K0T0HaYsi0hGVCe1+qrdoCHyDuSHFuRs9b68DOzMGPyREoyu5yCaxI+e2WN3DCeoZ5w3fMe/dGn/KhL45x2258TGnO2ITDNRTbV865DqUx [email protected]

ssh/testdata/server_key_rsa

+27
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
-----BEGIN RSA PRIVATE KEY-----
2+
MIIEowIBAAKCAQEAxNwQ6LH6Skt3wO4OoT7dyqmw/77FE3OdplF+etmgRMIJRoZF
3+
e7LKZ71e2mjJMLnv9qDG2dPJj/gzcwhxhrzu08/xeJNewJXpxgyUJ0+n+zOloRt2
4+
hz5WO6T4QIntLJaOpYcO7jpiegDj8nl1tIFidUUvELwU8w8tpKoHUsomq4LjSVkH
5+
Zm7dejh+mhfeJVxZcBKWI21gX/TvgH+j3i2X+Tr/am3G2zoSzTbkd/G6LhpmuAAT
6+
8KHIV99ovDghcH99vbbkh1DZsZXLWHVVGaY4M68r5fL9b0yEiocxjppk7ex8GvWz
7+
8Rp1Noo+PxZpTAnYV8NGz9tlUxxoPyb2siI/1wIDAQABAoIBADSX6riQXCMfOHZF
8+
44i2yACOB2i8KeDBgbveh+EAZW4ZPOsnkkazBJpkoIPPfPjUpESKvbWSfIZHNE+m
9+
UNZDgbb7FjM1hBoFszgjQi2ifWmCady5/pexUs6Ki3yKnN/NtXyJsbZ5yLd5p2yv
10+
gH/iFVDpU+KvrRUm1/XnKx+2PFCTpnjFfTkVs5qC3RotzxtQ4LBTS8t6gGlgKhDo
11+
ONfU/ArypEqd0OoaVkDOGx+QnsWtpUUsOGsWYQudDsAVw0dEECoDjQLRj3CiqR9G
12+
IoZwjSyWNLVM/Kzk2Yfw8vvvbwkS3/XsJQMav8iDwvJ4ltnH09WbL2KwIgBQb7H3
13+
rAKUmbECgYEA45PDU5o8EphgwxiGS+o1FSbIu6gNiLyzZvVZar+VO1NqlAgoCJjr
14+
Ul+/saBefSc863rA3Tfg+V/4vBuJsxCZ7CtF2bNEaPf3fjQ72+3JUCNYQGAk9JEn
15+
ft7tftxpY+PuU6rswD6lql1IjsznV85Jy6HbpAmiy3vuQm2LjyZ/0YkCgYEA3XIu
16+
qvkHFIK9lL6+khVBfzEISnfF9RYYjwjE6Lex2SWfEeY2E4e5hiVg6stbDzM4JbVw
17+
fiBcY57mtn6qUTwC/pmf6sJKUBaInzIgGBgBo+YBixqjEE/Fkdhr5+7ZGkv6msM6
18+
KtgUZb1/c9jjj31ypsQo+jmD3+mnpgZeepfODl8CgYEA3sAb2LHHiN/zHiLcGGQ2
19+
7uwWYG9+R8+Dvgv6KJ07IWVllV1iaf/bnbYweFkjA1crrsVpD16Jay1/1gcSQd3G
20+
+z3j0dFA7YmAitvvlzXo7PKbh+9TXLrTMMBdL+XnlilcdjKAJy9qkhVaVsPC0NQO
21+
sS1NQgNYf9le/VAh4MzrIRkCgYAG5J7/Q3Es+kTcdHOKKI85XlVbvA61alKpYSR/
22+
+ffXQoyJChl00iy8OD2kEWBsQLPmuJcf1fIKR8/2rkqu7KdLNYz8yb92br0h93V4
23+
SocCiw5RPisFPvZva8UwGwc3AlZyqtEMtF0uLY/iE0ZCGS2Qc1qzL0cjmSpWyKv4
24+
NPF9EQKBgEF0GfnsKBOgFllpDN1eSpki2mzE/g4IkqRZMcGepCvWzI+Vu/WWSFcm
25+
T1KzTto/BPsvjl4S/yNvhKw0GGRUYH29KkO97ffBUV564IrSJ2WLs8nJpUxdSAMG
26+
tm/DNg1bfOshhnqhoVtutZJI45FlsTU0kFVph3+E92Ru4lTIMQtN
27+
-----END RSA PRIVATE KEY-----

ssh/testdata/server_key_rsa.pub

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDE3BDosfpKS3fA7g6hPt3KqbD/vsUTc52mUX562aBEwglGhkV7sspnvV7aaMkwue/2oMbZ08mP+DNzCHGGvO7Tz/F4k17AlenGDJQnT6f7M6WhG3aHPlY7pPhAie0slo6lhw7uOmJ6AOPyeXW0gWJ1RS8QvBTzDy2kqgdSyiarguNJWQdmbt16OH6aF94lXFlwEpYjbWBf9O+Af6PeLZf5Ov9qbcbbOhLNNuR38bouGma4ABPwochX32i8OCFwf329tuSHUNmxlctYdVUZpjgzryvl8v1vTISKhzGOmmTt7Hwa9bPxGnU2ij4/FmlMCdhXw0bP22VTHGg/JvayIj/X [email protected]

yang/ssh.yang

+20
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
module console {
2+
prefix "";
3+
namespace "";
4+
revision 0;
5+
6+
leaf address {
7+
description "bind IP and port to use for server. e.g. 0.0.0.0:2202";
8+
type string;
9+
}
10+
11+
leaf-list hostKeyFiles {
12+
description "PEM encoded private keys to use for server";
13+
type string;
14+
}
15+
16+
leaf authorizedKeysFile {
17+
description "plain test file listing public key of each allowed client. Same format as ~/.ssh/authorized_keys";
18+
type string;
19+
}
20+
}

0 commit comments

Comments
 (0)