diff --git a/archive_auth.go b/archive_auth.go deleted file mode 100644 index e6d22d3..0000000 --- a/archive_auth.go +++ /dev/null @@ -1,34 +0,0 @@ -package main - -import ( - "github.com/gbrlsnchs/jwt/v3" - "github.com/pterodactyl/wings/config" - "time" -) - -var alg *jwt.HMACSHA - -// ArchiveTokenPayload represents an Archive Token Payload. -type ArchiveTokenPayload struct { - jwt.Payload -} - -func ParseArchiveJWT(token []byte) (*ArchiveTokenPayload, error) { - var payload ArchiveTokenPayload - if alg == nil { - alg = jwt.NewHS256([]byte(config.Get().AuthenticationToken)) - } - - now := time.Now() - verifyOptions := jwt.ValidatePayload( - &payload.Payload, - jwt.ExpirationTimeValidator(now), - ) - - _, err := jwt.Verify(token, alg, &payload, verifyOptions) - if err != nil { - return nil, err - } - - return &payload, nil -} diff --git a/download_tokens.go b/download_tokens.go deleted file mode 100644 index b7b0d02..0000000 --- a/download_tokens.go +++ /dev/null @@ -1,47 +0,0 @@ -package main - -import ( - "github.com/gbrlsnchs/jwt/v3" - cache2 "github.com/patrickmn/go-cache" - "sync" - "time" -) - -type JWTokens struct { - cache *cache2.Cache - mutex *sync.Mutex -} - -var _tokens *JWTokens - -type DownloadBackupPayload struct { - jwt.Payload - ServerUuid string `json:"server_uuid"` - BackupUuid string `json:"backup_uuid"` - UniqueId string `json:"unique_id"` -} - -func getTokenStore() *JWTokens { - if _tokens == nil { - _tokens = &JWTokens{ - cache: cache2.New(time.Minute*60, time.Minute*5), - mutex: &sync.Mutex{}, - } - } - - return _tokens -} - -// Determines if a given JWT unique token is valid. -func (tokens *JWTokens) IsValidToken(token string) bool { - tokens.mutex.Lock() - defer tokens.mutex.Unlock() - - _, exists := tokens.cache.Get(token) - - if !exists { - _tokens.cache.Add(token, "", time.Minute*60) - } - - return !exists -} diff --git a/router/router.go b/router/router.go index a68f519..8e3ad0c 100644 --- a/router/router.go +++ b/router/router.go @@ -1,20 +1,31 @@ package router -import "github.com/gin-gonic/gin" +import ( + "github.com/gin-gonic/gin" +) // Configures the routing infrastructure for this daemon instance. func Configure() *gin.Engine { router := gin.Default() router.Use(SetAccessControlHeaders) + router.OPTIONS("/api/system", func(c *gin.Context) { + c.Status(200) + }) + // These routes use signed URLs to validate access to the resource being requested. router.GET("/download/backup", getDownloadBackup) - // This route is special is sits above all of the other requests because we are - // using a JWT to authorize access to it, therefore it needs to be publically + // This route is special it sits above all of the other requests because we are + // using a JWT to authorize access to it, therefore it needs to be publicly // accessible. router.GET("/api/servers/:server/ws", getServerWebsocket) + // This request is called by another daemon when a server is going to be transferred out. + // This request does not need the AuthorizationMiddleware as the panel should never call it + // and requests are authenticated through a JWT the panel issues to the other daemon. + router.GET("/api/servers/:server/archive", getServerArchive) + // All of the routes beyond this mount will use an authorization middleware // and will not be accessible without the correct Authorization header provided. protected := router.Use(AuthorizationMiddleware) @@ -39,7 +50,8 @@ func Configure() *gin.Engine { server.POST("/reinstall", postServerReinstall) server.POST("/backup", postServerBackup) - server.GET("/archive", getServerArchive) + // This archive request causes the archive to start being created + // this should only be triggered by the panel. server.POST("/archive", postServerArchive) files := server.Group("/files") diff --git a/router/router_system.go b/router/router_system.go index 882708a..aa560d5 100644 --- a/router/router_system.go +++ b/router/router_system.go @@ -1,6 +1,7 @@ package router import ( + "bytes" "github.com/gin-gonic/gin" "github.com/pterodactyl/wings/installer" "github.com/pterodactyl/wings/server" @@ -30,10 +31,10 @@ func getAllServers(c *gin.Context) { // Creates a new server on the wings daemon and begins the installation process // for it. func postCreateServer(c *gin.Context) { - var data []byte - c.Bind(&data) + buf := bytes.Buffer{} + buf.ReadFrom(c.Request.Body) - install, err := installer.New(data) + install, err := installer.New(buf.Bytes()) if err != nil { TrackedError(err). SetMessage("Failed to validate the data provided in the request."). @@ -61,4 +62,4 @@ func postCreateServer(c *gin.Context) { }(install) c.Status(http.StatusAccepted) -} \ No newline at end of file +} diff --git a/router/router_transfer.go b/router/router_transfer.go index 5367f44..325177f 100644 --- a/router/router_transfer.go +++ b/router/router_transfer.go @@ -37,14 +37,14 @@ func getServerArchive(c *gin.Context) { } token := tokens.TransferPayload{} - if err := tokens.ParseToken([]byte(c.Query("token")), &token); err != nil { + if err := tokens.ParseToken([]byte(auth[1]), &token); err != nil { TrackedError(err).AbortWithServerError(c) return } if token.Subject != c.Param("server") { c.AbortWithStatusJSON(http.StatusForbidden, gin.H{ - "error": "You are not authorized to access this endpoint.", + "error": "( ..•˘___˘• .. )", }) return } @@ -54,7 +54,6 @@ func getServerArchive(c *gin.Context) { st, err := s.Archiver.Stat() if err != nil { if !os.IsNotExist(err) { - // zap.S().Errorw("failed to stat archive for reading", zap.String("server", s.Uuid), zap.Error(err)) TrackedServerError(err, s).SetMessage("failed to stat archive").AbortWithServerError(c) return } @@ -65,7 +64,6 @@ func getServerArchive(c *gin.Context) { checksum, err := s.Archiver.Checksum() if err != nil { - // zap.S().Errorw("failed to calculate checksum", zap.String("server", s.Uuid), zap.Error(err)) TrackedServerError(err, s).SetMessage("failed to calculate checksum").AbortWithServerError(c) return } @@ -75,7 +73,6 @@ func getServerArchive(c *gin.Context) { tserr := TrackedServerError(err, s) if !os.IsNotExist(err) { tserr.SetMessage("failed to open archive for reading") - // zap.S().Errorw("failed to open archive for reading", zap.String("server", s.Uuid), zap.Error(err)) } else { tserr.SetMessage("failed to open archive") } @@ -271,8 +268,6 @@ func postTransfer(c *gin.Context) { return } - zap.S().Debug(string(serverData)) - // Create a new server installer (note this does not execute the install script) i, err := installer.New(serverData) if err != nil { @@ -287,8 +282,19 @@ func postTransfer(c *gin.Context) { i.Execute() // Un-archive the archive. That sounds weird.. - archiver.NewTarGz().Unarchive(archivePath, i.Server().Filesystem.Path()) + if err := archiver.NewTarGz().Unarchive(archivePath, i.Server().Filesystem.Path()); err != nil { + zap.S().Errorw("failed to extract archive", zap.String("server", serverID), zap.Error(err)) + return + } + // We mark the process as being successful here as if we fail to send a transfer success, + // then a transfer failure won't probably be successful either. + // + // It may be useful to retry sending the transfer success every so often just in case of a small + // hiccup or the fix of whatever error causing the success request to fail. + hasError = false + + // Notify the panel that the transfer succeeded. rerr, err := api.NewRequester().SendTransferSuccess(serverID) if rerr != nil || err != nil { if err != nil { @@ -301,7 +307,6 @@ func postTransfer(c *gin.Context) { } zap.S().Debugw("successfully notified panel about transfer success", zap.String("server", serverID)) - hasError = false }(buf.Bytes()) c.Status(http.StatusAccepted)