Send errors back over the socket to users depending on permission

This commit is contained in:
Dane Everitt 2019-09-28 13:01:04 -07:00
parent 71d5b0fe83
commit 640f4b3a98
No known key found for this signature in database
GPG Key ID: EEA66103B3D71F53
3 changed files with 61 additions and 5 deletions

1
go.mod
View File

@ -15,6 +15,7 @@ require (
github.com/gabriel-vasile/mimetype v0.1.4 github.com/gabriel-vasile/mimetype v0.1.4
github.com/gbrlsnchs/jwt/v3 v3.0.0-rc.0 github.com/gbrlsnchs/jwt/v3 v3.0.0-rc.0
github.com/gogo/protobuf v1.2.1 // indirect github.com/gogo/protobuf v1.2.1 // indirect
github.com/google/uuid v1.1.1
github.com/gorilla/websocket v1.4.0 github.com/gorilla/websocket v1.4.0
github.com/gotestyourself/gotestyourself v2.2.0+incompatible // indirect github.com/gotestyourself/gotestyourself v2.2.0+incompatible // indirect
github.com/julienschmidt/httprouter v1.2.0 github.com/julienschmidt/httprouter v1.2.0

2
go.sum
View File

@ -29,6 +29,8 @@ github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5y
github.com/google/go-cmp v0.2.0 h1:+dTQ8DZQJz0Mb/HjFlkptS1FeQ4cWSnN941F8aEG4SQ= github.com/google/go-cmp v0.2.0 h1:+dTQ8DZQJz0Mb/HjFlkptS1FeQ4cWSnN941F8aEG4SQ=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY=
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/gorilla/websocket v1.4.0 h1:WDFjx/TMzVgy9VdMMQi2K2Emtwi2QcUQsztZ/zLaH/Q= github.com/gorilla/websocket v1.4.0 h1:WDFjx/TMzVgy9VdMMQi2K2Emtwi2QcUQsztZ/zLaH/Q=
github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
github.com/gotestyourself/gotestyourself v2.2.0+incompatible h1:AQwinXlbQR2HvPjQZOmDhRqsv5mZf+Jb1RnSLxcqZcI= github.com/gotestyourself/gotestyourself v2.2.0+incompatible h1:AQwinXlbQR2HvPjQZOmDhRqsv5mZf+Jb1RnSLxcqZcI=

View File

@ -3,7 +3,9 @@ package main
import ( import (
"encoding/json" "encoding/json"
"errors" "errors"
"fmt"
"github.com/gbrlsnchs/jwt/v3" "github.com/gbrlsnchs/jwt/v3"
"github.com/google/uuid"
"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/config"
@ -19,9 +21,11 @@ import (
const ( const (
TokenExpiringEvent = "token expiring" TokenExpiringEvent = "token expiring"
TokenExpiredEvent = "token expired" TokenExpiredEvent = "token expired"
AuthenticationEvent = "auth"
SetStateEvent = "set state" SetStateEvent = "set state"
SendServerLogsEvent = "send logs" SendServerLogsEvent = "send logs"
SendCommandEvent = "send command" SendCommandEvent = "send command"
ErrorEvent = "daemon error"
) )
type WebsocketMessage struct { type WebsocketMessage struct {
@ -60,6 +64,7 @@ const (
PermissionConnect = "connect" PermissionConnect = "connect"
PermissionSendCommand = "send-command" PermissionSendCommand = "send-command"
PermissionSendPower = "send-power" PermissionSendPower = "send-power"
PermissionReceiveErrors = "receive-errors"
) )
// Checks if the given token payload has a permission string. // Checks if the given token payload has a permission string.
@ -248,8 +253,7 @@ func (rt *Router) routeWebsocket(w http.ResponseWriter, r *http.Request, ps http
} }
if err := handler.HandleInbound(j); err != nil { if err := handler.HandleInbound(j); err != nil {
zap.S().Warnw("error handling inbound websocket request", zap.Error(err)) handler.SendErrorJson(err)
break
} }
} }
} }
@ -264,6 +268,38 @@ func (wsh *WebsocketHandler) SendJson(v interface{}) error {
return wsh.Connection.WriteJSON(v) return wsh.Connection.WriteJSON(v)
} }
// Sends an error back to the connected websocket instance by checking the permissions
// of the token. If the user has the "receive-errors" grant we will send back the actual
// error message, otherwise we just send back a standard error message.
func (wsh *WebsocketHandler) SendErrorJson(err error) error {
wsh.Mutex.Lock()
defer wsh.Mutex.Unlock()
message := "an unexpected error was encountered during the websocket lifecycle"
if wsh.JWT != nil && wsh.JWT.HasPermission(PermissionReceiveErrors) {
message = err.Error()
}
m, u := wsh.GetErrorMessage(message)
wsm := WebsocketMessage{Event: ErrorEvent}
wsm.Args = []string{m}
zap.S().Warnw("an error was encountered in the websocket process", zap.String("error_identifier", u.String()), zap.Error(err))
return wsh.Connection.WriteJSON(wsm)
}
// Converts an error message into a more readable representation and returns a UUID
// that can be cross-referenced to find the specific error that triggered.
func (wsh *WebsocketHandler) GetErrorMessage(msg string) (string, uuid.UUID) {
u, _ := uuid.NewRandom()
m := fmt.Sprintf("Error Event [%s]: %s", u.String(), msg)
return m, u
}
// Handle the inbound socket request and route it to the proper server action. // Handle the inbound socket request and route it to the proper server action.
func (wsh *WebsocketHandler) HandleInbound(m WebsocketMessage) error { func (wsh *WebsocketHandler) HandleInbound(m WebsocketMessage) error {
if !m.inbound { if !m.inbound {
@ -277,6 +313,19 @@ func (wsh *WebsocketHandler) HandleInbound(m WebsocketMessage) error {
} }
switch m.Event { switch m.Event {
case AuthenticationEvent:
{
token, err := ParseJWT([]byte(strings.Join(m.Args, "")))
if err != nil {
return nil
}
if token.HasPermission(PermissionConnect) {
wsh.JWT = token
}
return nil
}
case SetStateEvent: case SetStateEvent:
{ {
if !wsh.JWT.HasPermission(PermissionSendPower) { if !wsh.JWT.HasPermission(PermissionSendPower) {
@ -328,6 +377,10 @@ func (wsh *WebsocketHandler) HandleInbound(m WebsocketMessage) error {
return nil return nil
} }
if wsh.Server.State == server.ProcessOfflineState {
return nil
}
return wsh.Server.Environment.SendCommand(strings.Join(m.Args, "")) return wsh.Server.Environment.SendCommand(strings.Join(m.Args, ""))
} }
} }