From 7019b00643c3ecdcb0a8c802b8a919ca43356b02 Mon Sep 17 00:00:00 2001 From: braginini Date: Sat, 4 Jun 2022 21:16:41 +0200 Subject: [PATCH 1/3] Playing with ssh --- client/ssh/server.go | 123 +++++++++++++++++++++++++++++++++++++++++++ go.mod | 6 +++ go.sum | 10 ++++ 3 files changed, 139 insertions(+) create mode 100644 client/ssh/server.go diff --git a/client/ssh/server.go b/client/ssh/server.go new file mode 100644 index 000000000..834233f84 --- /dev/null +++ b/client/ssh/server.go @@ -0,0 +1,123 @@ +package main + +import ( + "crypto/rand" + "crypto/rsa" + "fmt" + "github.com/gliderlabs/ssh" + gossh "golang.org/x/crypto/ssh" + "io" + "net" + "sync" +) + +type Server struct { + listener net.Listener + allowedKeys []ssh.PublicKey + mu sync.Mutex +} + +func (srv *Server) UpdateKeys(newKeys []string) error { + srv.mu.Lock() + defer srv.mu.Unlock() + + srv.allowedKeys = make([]ssh.PublicKey, len(newKeys)) + for _, strKey := range newKeys { + parsedKey, _, _, _, err := ssh.ParseAuthorizedKey([]byte(strKey)) + if err != nil { + return err + } + srv.allowedKeys = append(srv.allowedKeys, parsedKey) + } + + return nil +} + +func NewSSHServer() (*Server, error) { + ln, err := net.Listen("tcp", ":2222") + if err != nil { + return nil, err + } + return &Server{listener: ln, mu: sync.Mutex{}}, nil +} + +// Stop stops SSH server. Blocking +func (srv *Server) Stop() error { + err := srv.listener.Close() + if err != nil { + return err + } + return nil +} + +// Start starts SSH server. Blocking +func (srv *Server) Start() error { + handler := func(s ssh.Session) { + authorizedKey := gossh.MarshalAuthorizedKey(s.PublicKey()) + io.WriteString(s, fmt.Sprintf("public key used by %s:\n", s.User())) + s.Write(authorizedKey) + } + + publicKeyOption := ssh.PublicKeyAuth(func(ctx ssh.Context, key ssh.PublicKey) bool { + srv.mu.Lock() + defer srv.mu.Unlock() + + for _, allowed := range srv.allowedKeys { + if ssh.KeysEqual(allowed, key) { + return true + } + } + + return false + }) + + err := ssh.Serve(srv.listener, handler, publicKeyOption) + if err != nil { + return err + } + + return nil +} + +func main() { + + server, err := NewSSHServer() + if err != nil { + return + } + + err = server.UpdateKeys([]string{"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIIwAoefixS03tYDfNuFfNRMO2syYfkw/C/76m8LS8xum"}) + if err != nil { + return + } + + err = server.Start() + if err != nil { + // will throw error when Stop has been called + } +} + +// generatePrivateKey creates RSA Private Key of specified byte size +func generatePrivateKey(bitSize int) (*rsa.PrivateKey, error) { + privateKey, err := rsa.GenerateKey(rand.Reader, bitSize) + if err != nil { + return nil, err + } + + err = privateKey.Validate() + if err != nil { + return nil, err + } + + return privateKey, nil +} + +// generatePublicKey takes a rsa.PublicKey and return bytes suitable for writing to .pub file +// returns the key in format format "ssh-rsa ..." +func generatePublicKey(privateKey *rsa.PublicKey) ([]byte, error) { + publicRsaKey, err := gossh.NewPublicKey(privateKey) + if err != nil { + return nil, err + } + return gossh.MarshalAuthorizedKey(publicRsaKey), nil +} diff --git a/go.mod b/go.mod index 3d362d70f..89759b9a7 100644 --- a/go.mod +++ b/go.mod @@ -31,7 +31,9 @@ require ( fyne.io/fyne/v2 v2.1.4 github.com/c-robinson/iplib v1.0.3 github.com/getlantern/systray v1.2.1 + github.com/gliderlabs/ssh v0.3.4 github.com/magiconair/properties v1.8.5 + github.com/prometheus/common v0.9.1 github.com/rs/xid v1.3.0 github.com/skratchdot/open-golang v0.0.0-20200116055534-eef842397966 github.com/stretchr/testify v1.7.0 @@ -39,6 +41,9 @@ require ( require ( github.com/BurntSushi/toml v0.4.1 // indirect + github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751 // indirect + github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4 // indirect + github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/fredbi/uri v0.0.0-20181227131451-3dcfdacbaaf3 // indirect github.com/fsnotify/fsnotify v1.5.1 // indirect @@ -83,6 +88,7 @@ require ( golang.zx2c4.com/go118/netip v0.0.0-20211111135330-a4a02eeacf9d // indirect golang.zx2c4.com/wintun v0.0.0-20211104114900-415007cec224 // indirect google.golang.org/genproto v0.0.0-20211208223120-3a66f561d7aa // indirect + gopkg.in/alecthomas/kingpin.v2 v2.2.6 // indirect gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b // indirect gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect diff --git a/go.sum b/go.sum index 8a52afa8d..a4d1f981a 100644 --- a/go.sum +++ b/go.sum @@ -57,9 +57,13 @@ github.com/Kodeworks/golang-image-ico v0.0.0-20141118225523-73f0f4cfade9/go.mod github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= github.com/akavel/rsrc v0.8.0/go.mod h1:uLoCtb9J+EyAqh+26kdrTgmzRBFPGOolLWKpdxkKq+c= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= +github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751 h1:JYp7IbQjafoB+tBA3gMyHYHrpOtNuDiK/uB5uXxq5wM= github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= +github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4 h1:Hs82Z41s6SdL1CELW+XaDYmOH4hkBN4/N9og/AsOv7E= github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= +github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be h1:9AeTilPcZAjCFIImctFaOjnTIavg87rW78vTPkQqLI8= +github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be/go.mod h1:ySMOLuWl6zY27l47sB3qLNK6tF2fkHG55UZxx8oIVo4= github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= @@ -142,6 +146,8 @@ github.com/getlantern/systray v1.2.1/go.mod h1:AecygODWIsBquJCJFop8MEQcJbWFfw/1y github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= github.com/gin-gonic/gin v1.5.0/go.mod h1:Nd6IXA8m5kNZdNEHMBd93KT+mdY3+bewLgRvmCsR2Do= +github.com/gliderlabs/ssh v0.3.4 h1:+AXBtim7MTKaLVPgvE+3mhewYRawNLTd+jEEz/wExZw= +github.com/gliderlabs/ssh v0.3.4/go.mod h1:ZSS+CUoKHDrqVakTfTWUlKSr9MtMFkC4UvtQKD7O914= github.com/go-gl/gl v0.0.0-20210813123233-e4099ee2221f h1:s0O46d8fPwk9kU4k1jj76wBquMVETx7uveQD9MCIQoU= github.com/go-gl/gl v0.0.0-20210813123233-e4099ee2221f/go.mod h1:wjpnOv6ONl2SuJSxqCPVaPZibGFdSci9HFocT9qtVYM= github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= @@ -426,6 +432,7 @@ github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1: github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= +github.com/prometheus/common v0.9.1 h1:KOMtN28tlbam3/7ZKEYKHhKoJZYYj3gMH4uc62x7X7U= github.com/prometheus/common v0.9.1/go.mod h1:yhUN8i9wzaXS3w1O07YhxHEBxD+W35wd8bs7vj7HSQ4= github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= @@ -516,6 +523,7 @@ golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20190923035154-9ee001bba392/go.mod h1:/lpIB1dKB+9EgE3H3cr1v9wB50oz8l4C4h62xy7jSTY= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20210817164053-32db794688a5/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20211202192323-5770296d904e/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= @@ -745,6 +753,7 @@ golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220204135822-1c1b9b1eba6a h1:ppl5mZgokTT8uPkmYOyEUmPTr3ypaKkg5eFOGrAmxxE= golang.org/x/sys v0.0.0-20220204135822-1c1b9b1eba6a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 h1:JGgROgKl9N8DuW20oFS5gxc+lE67/N3FcwmBPMe7ArY= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -981,6 +990,7 @@ google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp0 google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.27.1 h1:SnqbnDw1V7RiZcXPx5MEeqPv2s79L9i7BJUlG/+RurQ= google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +gopkg.in/alecthomas/kingpin.v2 v2.2.6 h1:jMFz6MfLP0/4fUyZle81rXUoxOBFi19VUFKVDOQfozc= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= From cd47599900f5b587fb4344a62a6f29997d3e2567 Mon Sep 17 00:00:00 2001 From: braginini Date: Sat, 4 Jun 2022 21:54:55 +0200 Subject: [PATCH 2/3] Do map lookup for allowed keys --- client/ssh/server.go | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/client/ssh/server.go b/client/ssh/server.go index 834233f84..ea89e7427 100644 --- a/client/ssh/server.go +++ b/client/ssh/server.go @@ -8,12 +8,13 @@ import ( gossh "golang.org/x/crypto/ssh" "io" "net" + "strings" "sync" ) type Server struct { listener net.Listener - allowedKeys []ssh.PublicKey + allowedKeys map[string]ssh.PublicKey mu sync.Mutex } @@ -21,13 +22,13 @@ func (srv *Server) UpdateKeys(newKeys []string) error { srv.mu.Lock() defer srv.mu.Unlock() - srv.allowedKeys = make([]ssh.PublicKey, len(newKeys)) + srv.allowedKeys = make(map[string]ssh.PublicKey, len(newKeys)) for _, strKey := range newKeys { parsedKey, _, _, _, err := ssh.ParseAuthorizedKey([]byte(strKey)) if err != nil { return err } - srv.allowedKeys = append(srv.allowedKeys, parsedKey) + srv.allowedKeys[strKey] = parsedKey } return nil @@ -62,7 +63,8 @@ func (srv *Server) Start() error { srv.mu.Lock() defer srv.mu.Unlock() - for _, allowed := range srv.allowedKeys { + k := strings.TrimSpace(string(gossh.MarshalAuthorizedKey(key))) + if allowed, ok := srv.allowedKeys[k]; ok { if ssh.KeysEqual(allowed, key) { return true } From 7934e62c93e2633e2340657bdd4ea3b126301356 Mon Sep 17 00:00:00 2001 From: braginini Date: Sat, 4 Jun 2022 22:09:25 +0200 Subject: [PATCH 3/3] Add hostkey to the ssh server --- client/ssh/server.go | 54 +++++++++++++------------------------------- client/ssh/ssh.go | 52 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 68 insertions(+), 38 deletions(-) create mode 100644 client/ssh/ssh.go diff --git a/client/ssh/server.go b/client/ssh/server.go index ea89e7427..7eb9c5000 100644 --- a/client/ssh/server.go +++ b/client/ssh/server.go @@ -1,8 +1,6 @@ -package main +package ssh import ( - "crypto/rand" - "crypto/rsa" "fmt" "github.com/gliderlabs/ssh" gossh "golang.org/x/crypto/ssh" @@ -16,6 +14,16 @@ type Server struct { listener net.Listener allowedKeys map[string]ssh.PublicKey mu sync.Mutex + hostKeyPEM []byte +} + +// NewSSHServer creates new server with provided host key +func NewSSHServer(hostKeyPEM []byte) (*Server, error) { + ln, err := net.Listen("tcp", ":2222") + if err != nil { + return nil, err + } + return &Server{listener: ln, mu: sync.Mutex{}, hostKeyPEM: hostKeyPEM}, nil } func (srv *Server) UpdateKeys(newKeys []string) error { @@ -34,14 +42,6 @@ func (srv *Server) UpdateKeys(newKeys []string) error { return nil } -func NewSSHServer() (*Server, error) { - ln, err := net.Listen("tcp", ":2222") - if err != nil { - return nil, err - } - return &Server{listener: ln, mu: sync.Mutex{}}, nil -} - // Stop stops SSH server. Blocking func (srv *Server) Stop() error { err := srv.listener.Close() @@ -73,7 +73,9 @@ func (srv *Server) Start() error { return false }) - err := ssh.Serve(srv.listener, handler, publicKeyOption) + hostKeyPEM := ssh.HostKeyPEM(srv.hostKeyPEM) + + err := ssh.Serve(srv.listener, handler, publicKeyOption, hostKeyPEM) if err != nil { return err } @@ -83,7 +85,8 @@ func (srv *Server) Start() error { func main() { - server, err := NewSSHServer() + strKey := "-----BEGIN RSA PRIVATE KEY-----\n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\n-----END RSA PRIVATE KEY-----\n" + server, err := NewSSHServer([]byte(strKey)) if err != nil { return } @@ -98,28 +101,3 @@ func main() { // will throw error when Stop has been called } } - -// generatePrivateKey creates RSA Private Key of specified byte size -func generatePrivateKey(bitSize int) (*rsa.PrivateKey, error) { - privateKey, err := rsa.GenerateKey(rand.Reader, bitSize) - if err != nil { - return nil, err - } - - err = privateKey.Validate() - if err != nil { - return nil, err - } - - return privateKey, nil -} - -// generatePublicKey takes a rsa.PublicKey and return bytes suitable for writing to .pub file -// returns the key in format format "ssh-rsa ..." -func generatePublicKey(privateKey *rsa.PublicKey) ([]byte, error) { - publicRsaKey, err := gossh.NewPublicKey(privateKey) - if err != nil { - return nil, err - } - return gossh.MarshalAuthorizedKey(publicRsaKey), nil -} diff --git a/client/ssh/ssh.go b/client/ssh/ssh.go new file mode 100644 index 000000000..4998f9119 --- /dev/null +++ b/client/ssh/ssh.go @@ -0,0 +1,52 @@ +package ssh + +import ( + "crypto/rand" + "crypto/rsa" + "crypto/x509" + "encoding/pem" + gossh "golang.org/x/crypto/ssh" +) + +// GeneratePrivateKey creates RSA Private Key of specified byte size +func GeneratePrivateKey(bitSize int) (*rsa.PrivateKey, error) { + privateKey, err := rsa.GenerateKey(rand.Reader, bitSize) + if err != nil { + return nil, err + } + + err = privateKey.Validate() + if err != nil { + return nil, err + } + + return privateKey, nil +} + +// GeneratePublicKey takes a rsa.PublicKey and return bytes suitable for writing to .pub file +// returns the key in format format "ssh-rsa ..." +func GeneratePublicKey(privateKey *rsa.PublicKey) ([]byte, error) { + publicRsaKey, err := gossh.NewPublicKey(privateKey) + if err != nil { + return nil, err + } + return gossh.MarshalAuthorizedKey(publicRsaKey), nil +} + +// EncodePrivateKeyToPEM encodes Private Key from RSA to PEM format +func EncodePrivateKeyToPEM(privateKey *rsa.PrivateKey) []byte { + // Get ASN.1 DER format + privDER := x509.MarshalPKCS1PrivateKey(privateKey) + + // pem.Block + privBlock := pem.Block{ + Type: "RSA PRIVATE KEY", + Headers: nil, + Bytes: privDER, + } + + // Private key in PEM format + privatePEM := pem.EncodeToMemory(&privBlock) + + return privatePEM +}