Add authentication for another node to access GET /api/servers/:server/archive, add POST /api/transfer route

This commit is contained in:
Matthew Penner 2020-04-04 00:51:35 -06:00
parent c096d8802f
commit ef0ad45b71
2 changed files with 61 additions and 4 deletions

32
archive_auth.go Normal file
View File

@ -0,0 +1,32 @@
package main
import (
"github.com/gbrlsnchs/jwt/v3"
"github.com/pterodactyl/wings/config"
"time"
)
// 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
}

33
http.go
View File

@ -33,7 +33,7 @@ type Router struct {
upgrader websocket.Upgrader upgrader websocket.Upgrader
// The authentication token defined in the config.yml file that allows // The authentication token defined in the config.yml file that allows
// a request to perform any action aganist the daemon. // a request to perform any action against the daemon.
token string token string
} }
@ -79,7 +79,7 @@ func (rt *Router) AuthenticateToken(h httprouter.Handle) httprouter.Handle {
return return
} }
// Try to match the request aganist the global token for the Daemon, regardless // Try to match the request against the global token for the Daemon, regardless
// of the permission type. If nothing is matched we will fall through to the Panel // of the permission type. If nothing is matched we will fall through to the Panel
// API to try and validate permissions for a server. // API to try and validate permissions for a server.
if auth[1] == rt.token { if auth[1] == rt.token {
@ -156,7 +156,7 @@ func (rt *Router) routeServerPower(w http.ResponseWriter, r *http.Request, ps ht
return return
} }
// Because we route all of the actual bootup process to a seperate thread we need to // Because we route all of the actual bootup process to a separate thread we need to
// check the suspension status here, otherwise the user will hit the endpoint and then // check the suspension status here, otherwise the user will hit the endpoint and then
// just sit there wondering why it returns a success but nothing actually happens. // just sit there wondering why it returns a success but nothing actually happens.
// //
@ -606,6 +606,25 @@ func (rt *Router) routeRequestServerArchive(w http.ResponseWriter, _ *http.Reque
} }
func (rt *Router) routeGetServerArchive(w http.ResponseWriter, r *http.Request, ps httprouter.Params) { func (rt *Router) routeGetServerArchive(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
auth := strings.SplitN(r.Header.Get("Authorization"), " ", 2)
if len(auth) != 2 || auth[0] != "Bearer" {
w.Header().Set("WWW-Authenticate", "Bearer")
http.Error(w, "authorization failed", http.StatusUnauthorized)
return
}
token, err := ParseArchiveJWT([]byte(auth[1]))
if err != nil {
http.Error(w, "authorization failed", http.StatusUnauthorized)
return
}
if token.Subject != ps.ByName("server") {
http.Error(w, "forbidden", http.StatusForbidden)
return
}
s := rt.GetServer(ps.ByName("server")) s := rt.GetServer(ps.ByName("server"))
st, err := s.Archiver.Stat() st, err := s.Archiver.Stat()
@ -647,6 +666,10 @@ func (rt *Router) routeGetServerArchive(w http.ResponseWriter, r *http.Request,
bufio.NewReader(file).WriteTo(w) bufio.NewReader(file).WriteTo(w)
} }
func (rt *Router) routeIncomingTransfer(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
w.WriteHeader(204)
}
func (rt *Router) ReaderToBytes(r io.Reader) []byte { func (rt *Router) ReaderToBytes(r io.Reader) []byte {
buf := bytes.Buffer{} buf := bytes.Buffer{}
buf.ReadFrom(r) buf.ReadFrom(r)
@ -684,7 +707,9 @@ func (rt *Router) ConfigureRouter() *httprouter.Router {
router.DELETE("/api/servers/:server", rt.AuthenticateRequest(rt.routeServerDelete)) router.DELETE("/api/servers/:server", rt.AuthenticateRequest(rt.routeServerDelete))
router.POST("/api/servers/:server/archive", rt.AuthenticateRequest(rt.routeRequestServerArchive)) router.POST("/api/servers/:server/archive", rt.AuthenticateRequest(rt.routeRequestServerArchive))
router.GET("/api/servers/:server/archive", rt.AuthenticateRequest(rt.routeGetServerArchive)) router.GET("/api/servers/:server/archive", rt.AuthenticateServer(rt.routeGetServerArchive))
router.POST("/api/transfer", rt.AuthenticateToken(rt.routeIncomingTransfer))
return router return router
} }