diff --git a/go.mod b/go.mod index 1211b0a..c303bc1 100644 --- a/go.mod +++ b/go.mod @@ -33,7 +33,6 @@ require ( github.com/gorilla/websocket v1.4.2 github.com/iancoleman/strcase v0.2.0 github.com/icza/dyno v0.0.0-20210726202311-f1bafe5d9996 - github.com/imdario/mergo v0.3.12 github.com/juju/ratelimit v1.0.1 github.com/karrick/godirwalk v1.16.1 github.com/klauspost/compress v1.13.2 // indirect diff --git a/go.sum b/go.sum index 7c16bc3..f361140 100644 --- a/go.sum +++ b/go.sum @@ -513,8 +513,6 @@ github.com/imdario/mergo v0.3.5/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJ github.com/imdario/mergo v0.3.8/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= github.com/imdario/mergo v0.3.10/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= github.com/imdario/mergo v0.3.11/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= -github.com/imdario/mergo v0.3.12 h1:b6R2BslTbIEToALKP7LxUvijTsNI9TAe80pLWN2g/HU= -github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= github.com/j-keck/arping v0.0.0-20160618110441-2cf9dc699c56/go.mod h1:ymszkNOg6tORTn+6F6j+Jc8TOr5osrynvN6ivFWZ2GA= diff --git a/sftp/server.go b/sftp/server.go index a2d03f0..df4229e 100644 --- a/sftp/server.go +++ b/sftp/server.go @@ -3,7 +3,6 @@ package sftp import ( "context" "crypto/rand" - "crypto/rsa" "crypto/x509" "encoding/pem" "io" @@ -18,6 +17,7 @@ import ( "emperror.dev/errors" "github.com/apex/log" "github.com/pkg/sftp" + "golang.org/x/crypto/ed25519" "golang.org/x/crypto/ssh" "github.com/pterodactyl/wings/config" @@ -48,18 +48,20 @@ func New(m *server.Manager) *SFTPServer { } } -// Starts the SFTP server and add a persistent listener to handle inbound SFTP connections. +// Run starts the SFTP server and add a persistent listener to handle inbound +// SFTP connections. This will automatically generate an ED25519 key if one does +// not already exist on the system for host key verification purposes. func (c *SFTPServer) Run() error { - if _, err := os.Stat(path.Join(c.BasePath, ".sftp/id_rsa")); os.IsNotExist(err) { - if err := c.generatePrivateKey(); err != nil { + if _, err := os.Stat(c.PrivateKeyPath()); os.IsNotExist(err) { + if err := c.generateED25519PrivateKey(); err != nil { return err } } else if err != nil { - return errors.Wrap(err, "sftp/server: could not stat private key file") + return errors.Wrap(err, "sftp: could not stat private key file") } - pb, err := ioutil.ReadFile(path.Join(c.BasePath, ".sftp/id_rsa")) + pb, err := ioutil.ReadFile(c.PrivateKeyPath()) if err != nil { - return errors.Wrap(err, "sftp/server: could not read private key file") + return errors.Wrap(err, "sftp: could not read private key file") } private, err := ssh.ParsePrivateKey(pb) if err != nil { @@ -78,7 +80,9 @@ func (c *SFTPServer) Run() error { return err } - log.WithField("listen", c.Listen).Info("sftp server listening for connections") + public := string(ssh.MarshalAuthorizedKey(private.PublicKey())) + log.WithField("listen", c.Listen).WithField("public_key", strings.Trim(public, "\n")).Info("sftp server listening for connections") + for { if conn, _ := listener.Accept(); conn != nil { go func(conn net.Conn) { @@ -148,26 +152,30 @@ func (c *SFTPServer) AcceptInbound(conn net.Conn, config *ssh.ServerConfig) { } } -// Generates a private key that will be used by the SFTP server. -func (c *SFTPServer) generatePrivateKey() error { - key, err := rsa.GenerateKey(rand.Reader, 2048) +// Generates a new ED25519 private key that is used for host authentication when +// a user connects to the SFTP server. +func (c *SFTPServer) generateED25519PrivateKey() error { + _, priv, err := ed25519.GenerateKey(rand.Reader) if err != nil { - return errors.WithStack(err) + return errors.Wrap(err, "sftp: failed to generate ED25519 private key") } - if err := os.MkdirAll(path.Join(c.BasePath, ".sftp"), 0755); err != nil { - return errors.Wrap(err, "sftp/server: could not create .sftp directory") + if err := os.MkdirAll(path.Dir(c.PrivateKeyPath()), 0755); err != nil { + return errors.Wrap(err, "sftp: could not create internal sftp data directory") } - o, err := os.OpenFile(path.Join(c.BasePath, ".sftp/id_rsa"), os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0600) + o, err := os.OpenFile(c.PrivateKeyPath(), os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0600) if err != nil { return errors.WithStack(err) } defer o.Close() - err = pem.Encode(o, &pem.Block{ - Type: "RSA PRIVATE KEY", - Bytes: x509.MarshalPKCS1PrivateKey(key), - }) - return errors.WithStack(err) + b, err := x509.MarshalPKCS8PrivateKey(priv) + if err != nil { + return errors.Wrap(err, "sftp: failed to marshal private key into bytes") + } + if err := pem.Encode(o, &pem.Block{Type: "PRIVATE KEY", Bytes: b}); err != nil { + return errors.Wrap(err, "sftp: failed to write ED25519 private key to disk") + } + return nil } // A function capable of validating user credentials with the Panel API. @@ -209,3 +217,8 @@ func (c *SFTPServer) passwordCallback(conn ssh.ConnMetadata, pass []byte) (*ssh. return sshPerm, nil } + +// PrivateKeyPath returns the path the host private key for this server instance. +func (c *SFTPServer) PrivateKeyPath() string { + return path.Join(c.BasePath, ".sftp/id_ed25519") +} \ No newline at end of file