2019-12-07 22:01:40 +00:00
|
|
|
package sftp
|
|
|
|
|
|
|
|
import (
|
2020-05-29 05:07:53 +00:00
|
|
|
"github.com/apex/log"
|
2019-12-07 23:53:07 +00:00
|
|
|
"github.com/pkg/errors"
|
|
|
|
"github.com/pterodactyl/sftp-server"
|
|
|
|
"github.com/pterodactyl/wings/api"
|
2019-12-07 22:01:40 +00:00
|
|
|
"github.com/pterodactyl/wings/config"
|
2019-12-08 01:35:45 +00:00
|
|
|
"github.com/pterodactyl/wings/server"
|
2019-12-07 23:53:07 +00:00
|
|
|
"go.uber.org/zap"
|
2020-07-03 04:03:11 +00:00
|
|
|
"regexp"
|
2019-12-07 22:01:40 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
func Initialize(config *config.Configuration) error {
|
2019-12-07 23:53:07 +00:00
|
|
|
c := &sftp_server.Server{
|
|
|
|
User: sftp_server.SftpUser{
|
2019-12-07 22:01:40 +00:00
|
|
|
Uid: config.System.User.Uid,
|
|
|
|
Gid: config.System.User.Gid,
|
|
|
|
},
|
2019-12-07 23:53:07 +00:00
|
|
|
Settings: sftp_server.Settings{
|
2019-12-07 22:01:40 +00:00
|
|
|
BasePath: config.System.Data,
|
|
|
|
ReadOnly: config.System.Sftp.ReadOnly,
|
|
|
|
BindAddress: config.System.Sftp.Address,
|
|
|
|
BindPort: config.System.Sftp.Port,
|
|
|
|
},
|
2019-12-07 23:53:07 +00:00
|
|
|
CredentialValidator: validateCredentials,
|
2019-12-08 00:43:00 +00:00
|
|
|
PathValidator: validatePath,
|
|
|
|
DiskSpaceValidator: validateDiskSpace,
|
2019-12-07 22:01:40 +00:00
|
|
|
}
|
|
|
|
|
2019-12-07 23:53:07 +00:00
|
|
|
if err := sftp_server.New(c); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
c.ConfigureLogger(func() *zap.SugaredLogger {
|
|
|
|
return zap.S().Named("sftp")
|
|
|
|
})
|
|
|
|
|
|
|
|
// Initialize the SFTP server in a background thread since this is
|
|
|
|
// a long running operation.
|
|
|
|
go func(instance *sftp_server.Server) {
|
|
|
|
if err := c.Initalize(); err != nil {
|
2020-05-29 05:07:53 +00:00
|
|
|
log.WithField("subsystem", "sftp").WithField("error", errors.WithStack(err)).Error("failed to initialize SFTP subsystem")
|
2019-12-07 23:53:07 +00:00
|
|
|
}
|
|
|
|
}(c)
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2019-12-08 00:43:00 +00:00
|
|
|
func validatePath(fs sftp_server.FileSystem, p string) (string, error) {
|
2019-12-08 01:35:45 +00:00
|
|
|
s := server.GetServers().Find(func(server *server.Server) bool {
|
|
|
|
return server.Uuid == fs.UUID
|
|
|
|
})
|
|
|
|
|
|
|
|
if s == nil {
|
|
|
|
return "", errors.New("no server found with that UUID")
|
|
|
|
}
|
|
|
|
|
|
|
|
return s.Filesystem.SafePath(p)
|
2019-12-08 00:43:00 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func validateDiskSpace(fs sftp_server.FileSystem) bool {
|
2019-12-08 01:35:45 +00:00
|
|
|
s := server.GetServers().Find(func(server *server.Server) bool {
|
|
|
|
return server.Uuid == fs.UUID
|
|
|
|
})
|
|
|
|
|
|
|
|
if s == nil {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
|
|
|
return s.Filesystem.HasSpaceAvailable()
|
2019-12-08 00:43:00 +00:00
|
|
|
}
|
|
|
|
|
2020-07-03 04:03:11 +00:00
|
|
|
var validUsernameRegexp = regexp.MustCompile(`^(?i)(.+)\.([a-z0-9]{8})$`)
|
|
|
|
|
2019-12-07 23:53:07 +00:00
|
|
|
// Validates a set of credentials for a SFTP login aganist Pterodactyl Panel and returns
|
|
|
|
// the server's UUID if the credentials were valid.
|
|
|
|
func validateCredentials(c sftp_server.AuthenticationRequest) (*sftp_server.AuthenticationResponse, error) {
|
2020-05-29 05:07:53 +00:00
|
|
|
log.WithFields(log.Fields{"subsystem": "sftp", "username": c.User}).Debug("validating credentials for SFTP connection")
|
2020-07-03 04:03:11 +00:00
|
|
|
|
|
|
|
f := log.Fields{
|
|
|
|
"subsystem": "sftp",
|
|
|
|
"username": c.User,
|
|
|
|
"ip": c.IP,
|
|
|
|
}
|
|
|
|
|
|
|
|
// If the username doesn't meet the expected format that the Panel would even recognize just go ahead
|
|
|
|
// and bail out of the process here to avoid accidentially brute forcing the panel if a bot decides
|
|
|
|
// to connect to spam username attempts.
|
|
|
|
if !validUsernameRegexp.MatchString(c.User) {
|
|
|
|
log.WithFields(f).Warn("failed to validate user credentials (invalid format)")
|
|
|
|
|
|
|
|
return nil, new(sftp_server.InvalidCredentialsError)
|
|
|
|
}
|
|
|
|
|
|
|
|
resp, err := api.NewRequester().ValidateSftpCredentials(c)
|
2019-12-08 01:35:45 +00:00
|
|
|
if err != nil {
|
2020-07-03 04:03:11 +00:00
|
|
|
if sftp_server.IsInvalidCredentialsError(err) {
|
|
|
|
log.WithFields(f).Warn("failed to validate user credentials (invalid username or password)")
|
|
|
|
} else {
|
|
|
|
log.WithFields(f).Error("encountered an error while trying to validate user credentials")
|
|
|
|
}
|
|
|
|
|
2019-12-08 01:35:45 +00:00
|
|
|
return resp, err
|
|
|
|
}
|
|
|
|
|
|
|
|
s := server.GetServers().Find(func(server *server.Server) bool {
|
|
|
|
return server.Uuid == resp.Server
|
|
|
|
})
|
|
|
|
|
|
|
|
if s == nil {
|
2020-05-10 00:46:22 +00:00
|
|
|
return resp, errors.New("no matching server with UUID found")
|
2019-12-08 01:35:45 +00:00
|
|
|
}
|
|
|
|
|
2020-07-03 04:03:11 +00:00
|
|
|
s.Log().WithFields(f).Debug("credentials successfully validated and matched user to server instance")
|
2020-05-29 05:07:53 +00:00
|
|
|
|
2019-12-08 01:35:45 +00:00
|
|
|
return resp, err
|
2019-12-07 22:01:40 +00:00
|
|
|
}
|