Add logic to handle authenticating a websocket with the panel
This commit is contained in:
parent
806afc6ed6
commit
1899b1ab4b
|
@ -200,6 +200,18 @@ func ReadConfiguration(path string) (*Configuration, error) {
|
||||||
return c, nil
|
return c, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var _config *Configuration
|
||||||
|
|
||||||
|
// Set the global configuration instance.
|
||||||
|
func Set(c *Configuration) {
|
||||||
|
_config = c
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the global configuration instance.
|
||||||
|
func Get() *Configuration {
|
||||||
|
return _config
|
||||||
|
}
|
||||||
|
|
||||||
// Ensures that the Pterodactyl core user exists on the system. This user will be the
|
// Ensures that the Pterodactyl core user exists on the system. This user will be the
|
||||||
// owner of all data in the root data directory and is used as the user within containers.
|
// owner of all data in the root data directory and is used as the user within containers.
|
||||||
//
|
//
|
||||||
|
|
5
http.go
5
http.go
|
@ -67,9 +67,6 @@ func (rt *Router) AuthenticateToken(h httprouter.Handle) httprouter.Handle {
|
||||||
// protocol header and use that to pass the authorization token along to Wings without
|
// protocol header and use that to pass the authorization token along to Wings without
|
||||||
// exposing the token in the URL directly. Neat. 📸
|
// exposing the token in the URL directly. Neat. 📸
|
||||||
auth := strings.SplitN(r.Header.Get("Authorization"), " ", 2)
|
auth := strings.SplitN(r.Header.Get("Authorization"), " ", 2)
|
||||||
if r.Header.Get("Sec-WebSocket-Protocol") != "" {
|
|
||||||
auth = []string{"Bearer", r.Header.Get("Sec-WebSocket-Protocol")}
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(auth) != 2 || auth[0] != "Bearer" {
|
if len(auth) != 2 || auth[0] != "Bearer" {
|
||||||
w.Header().Set("WWW-Authenticate", "Bearer")
|
w.Header().Set("WWW-Authenticate", "Bearer")
|
||||||
|
@ -408,7 +405,7 @@ func (rt *Router) ConfigureRouter() *httprouter.Router {
|
||||||
router.GET("/", rt.routeIndex)
|
router.GET("/", rt.routeIndex)
|
||||||
router.GET("/api/servers", rt.AuthenticateToken(rt.routeAllServers))
|
router.GET("/api/servers", rt.AuthenticateToken(rt.routeAllServers))
|
||||||
router.GET("/api/servers/:server", rt.AuthenticateRequest(rt.routeServer))
|
router.GET("/api/servers/:server", rt.AuthenticateRequest(rt.routeServer))
|
||||||
router.GET("/api/servers/:server/ws", rt.AuthenticateRequest(rt.routeWebsocket))
|
router.GET("/api/servers/:server/ws/:token", rt.AuthenticateServer(rt.AuthenticateWebsocket(rt.routeWebsocket)))
|
||||||
router.GET("/api/servers/:server/logs", rt.AuthenticateRequest(rt.routeServerLogs))
|
router.GET("/api/servers/:server/logs", rt.AuthenticateRequest(rt.routeServerLogs))
|
||||||
router.GET("/api/servers/:server/files/contents", rt.AuthenticateRequest(rt.routeServerFileRead))
|
router.GET("/api/servers/:server/files/contents", rt.AuthenticateRequest(rt.routeServerFileRead))
|
||||||
router.GET("/api/servers/:server/files/list-directory", rt.AuthenticateRequest(rt.routeServerListDirectory))
|
router.GET("/api/servers/:server/files/list-directory", rt.AuthenticateRequest(rt.routeServerListDirectory))
|
||||||
|
|
59
websocket.go
59
websocket.go
|
@ -1,11 +1,16 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
|
"fmt"
|
||||||
"github.com/gorilla/websocket"
|
"github.com/gorilla/websocket"
|
||||||
"github.com/julienschmidt/httprouter"
|
"github.com/julienschmidt/httprouter"
|
||||||
|
"github.com/pterodactyl/wings/config"
|
||||||
"github.com/pterodactyl/wings/server"
|
"github.com/pterodactyl/wings/server"
|
||||||
"go.uber.org/zap"
|
"go.uber.org/zap"
|
||||||
|
"io/ioutil"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
|
@ -40,6 +45,54 @@ type WebsocketHandler struct {
|
||||||
Connection *websocket.Conn
|
Connection *websocket.Conn
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type socketCredentials struct {
|
||||||
|
ServerUuid string `json:"server_uuid"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rt *Router) AuthenticateWebsocket(h httprouter.Handle) httprouter.Handle {
|
||||||
|
return func(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
|
||||||
|
s := rt.Servers.Get(ps.ByName("server"))
|
||||||
|
|
||||||
|
j, err := json.Marshal(socketCredentials{ServerUuid: s.Uuid})
|
||||||
|
if err != nil {
|
||||||
|
zap.S().Errorw("failed to marshal json", zap.Error(err))
|
||||||
|
http.Error(w, "failed to marshal json", http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
url := strings.TrimRight(config.Get().PanelLocation, "/") + "/api/remote/websocket/" + ps.ByName("token")
|
||||||
|
req, err := http.NewRequest(http.MethodPost, url, bytes.NewBuffer(j))
|
||||||
|
if err != nil {
|
||||||
|
zap.S().Errorw("failed to generate a new HTTP request when validating websocket credentials", zap.Error(err))
|
||||||
|
http.Error(w, "failed to generate HTTP request", http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
req.Header.Set("Accept", "application/json")
|
||||||
|
req.Header.Set("Content-Type", "application/json")
|
||||||
|
req.Header.Set("Authorization", "Bearer "+config.Get().AuthenticationToken)
|
||||||
|
|
||||||
|
client := &http.Client{}
|
||||||
|
resp, err := client.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
zap.S().Errorw("failed to perform client HTTP request", zap.Error(err))
|
||||||
|
http.Error(w, "failed to perform client HTTP request", http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
defer resp.Body.Close()
|
||||||
|
if resp.StatusCode != http.StatusNoContent {
|
||||||
|
b, _ := ioutil.ReadAll(resp.Body)
|
||||||
|
|
||||||
|
zap.S().Warnw("failed to validate token with server", zap.String("response", string(b)))
|
||||||
|
http.Error(w, "failed to validate token with server", resp.StatusCode)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
h(w, r, ps)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Handle a request for a specific server websocket. This will handle inbound requests as well
|
// Handle a request for a specific server websocket. This will handle inbound requests as well
|
||||||
// as ensure that any console output is also passed down the wire on the socket.
|
// as ensure that any console output is also passed down the wire on the socket.
|
||||||
func (rt *Router) routeWebsocket(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
|
func (rt *Router) routeWebsocket(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
|
||||||
|
@ -50,6 +103,8 @@ func (rt *Router) routeWebsocket(w http.ResponseWriter, r *http.Request, ps http
|
||||||
}
|
}
|
||||||
defer c.Close()
|
defer c.Close()
|
||||||
|
|
||||||
|
fmt.Println("running websocket route")
|
||||||
|
|
||||||
s := rt.Servers.Get(ps.ByName("server"))
|
s := rt.Servers.Get(ps.ByName("server"))
|
||||||
handler := WebsocketHandler{
|
handler := WebsocketHandler{
|
||||||
Server: s,
|
Server: s,
|
||||||
|
@ -91,6 +146,7 @@ func (rt *Router) routeWebsocket(w http.ResponseWriter, r *http.Request, ps http
|
||||||
|
|
||||||
for {
|
for {
|
||||||
j := WebsocketMessage{inbound: true}
|
j := WebsocketMessage{inbound: true}
|
||||||
|
fmt.Println("running for{} loop...")
|
||||||
|
|
||||||
if _, _, err := c.ReadMessage(); err != nil {
|
if _, _, err := c.ReadMessage(); err != nil {
|
||||||
if !websocket.IsCloseError(err, websocket.CloseNormalClosure, websocket.CloseGoingAway, websocket.CloseNoStatusReceived, websocket.CloseServiceRestart) {
|
if !websocket.IsCloseError(err, websocket.CloseNormalClosure, websocket.CloseGoingAway, websocket.CloseNoStatusReceived, websocket.CloseServiceRestart) {
|
||||||
|
@ -103,6 +159,7 @@ func (rt *Router) routeWebsocket(w http.ResponseWriter, r *http.Request, ps http
|
||||||
// specific socket request. If we did a break here the client would get disconnected
|
// specific socket request. If we did a break here the client would get disconnected
|
||||||
// from the socket, which is NOT what we want to do.
|
// from the socket, which is NOT what we want to do.
|
||||||
if err := c.ReadJSON(&j); err != nil {
|
if err := c.ReadJSON(&j); err != nil {
|
||||||
|
fmt.Println("ReadJSON() error")
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -112,6 +169,8 @@ func (rt *Router) routeWebsocket(w http.ResponseWriter, r *http.Request, ps http
|
||||||
} else {
|
} else {
|
||||||
zap.S().Debugw("handled event", zap.String("event", j.Event), zap.Strings("args", j.Args))
|
zap.S().Debugw("handled event", zap.String("event", j.Event), zap.Strings("args", j.Args))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fmt.Println("finished looping...")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
8
wings.go
8
wings.go
|
@ -1,6 +1,7 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"crypto/tls"
|
||||||
"flag"
|
"flag"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/gorilla/websocket"
|
"github.com/gorilla/websocket"
|
||||||
|
@ -37,7 +38,14 @@ func main() {
|
||||||
|
|
||||||
if c.Debug {
|
if c.Debug {
|
||||||
zap.S().Debugw("running in debug mode")
|
zap.S().Debugw("running in debug mode")
|
||||||
|
zap.S().Infow("certificate checking is disabled")
|
||||||
|
|
||||||
|
http.DefaultTransport.(*http.Transport).TLSClientConfig = &tls.Config{
|
||||||
|
InsecureSkipVerify: true,
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
config.Set(c)
|
||||||
|
|
||||||
zap.S().Infof("checking for pterodactyl system user \"%s\"", c.System.User)
|
zap.S().Infof("checking for pterodactyl system user \"%s\"", c.System.User)
|
||||||
if su, err := c.EnsurePterodactylUser(); err != nil {
|
if su, err := c.EnsurePterodactylUser(); err != nil {
|
||||||
|
|
Loading…
Reference in New Issue
Block a user