mirror of
https://github.com/yudai/gotty.git
synced 2026-01-26 11:16:11 +01:00
Authenticate WS connection using token
Safari doesn't support basic authentication for websocket sessions. This commit introduces a token-based authentication only for websocket connection. The token is shared by all clients and that might be not secure. However, basic authentication itself is insecure and the credential is already shared by clients, so don't mind.
This commit is contained in:
parent
e7e607b3d7
commit
0e81c484a9
4 changed files with 47 additions and 34 deletions
70
app/app.go
70
app/app.go
|
|
@ -29,8 +29,9 @@ type App struct {
|
|||
command []string
|
||||
options *Options
|
||||
|
||||
upgrader *websocket.Upgrader
|
||||
server *manners.GracefulServer
|
||||
upgrader *websocket.Upgrader
|
||||
server *manners.GracefulServer
|
||||
authToken string
|
||||
|
||||
titleTemplate *template.Template
|
||||
}
|
||||
|
|
@ -88,19 +89,13 @@ func New(command []string, options *Options) (*App, error) {
|
|||
WriteBufferSize: 1024,
|
||||
Subprotocols: []string{"gotty"},
|
||||
},
|
||||
authToken: generateRandomString(20),
|
||||
|
||||
titleTemplate: titleTemplate,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func ApplyConfigFile(options *Options, configFilePath string) error {
|
||||
if err := applyConfigFile(options, configFilePath); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func applyConfigFile(options *Options, filePath string) error {
|
||||
func ApplyConfigFile(options *Options, filePath string) error {
|
||||
filePath = ExpandHomeDir(filePath)
|
||||
if _, err := os.Stat(filePath); os.IsNotExist(err) {
|
||||
return err
|
||||
|
|
@ -146,19 +141,15 @@ func applyConfigFile(options *Options, filePath string) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func ExpandHomeDir(path string) string {
|
||||
if path[0:2] == "~/" {
|
||||
return os.Getenv("HOME") + path[1:]
|
||||
} else {
|
||||
return path
|
||||
}
|
||||
}
|
||||
|
||||
func (app *App) Run() error {
|
||||
if app.options.PermitWrite {
|
||||
log.Printf("Permitting clients to write input to the PTY.")
|
||||
}
|
||||
|
||||
if app.options.Once {
|
||||
log.Printf("Once option is provided, accepting only one client")
|
||||
}
|
||||
|
||||
path := ""
|
||||
if app.options.EnableRandomUrl {
|
||||
path += "/" + generateRandomString(app.options.RandomUrlLength)
|
||||
|
|
@ -167,31 +158,23 @@ func (app *App) Run() error {
|
|||
endpoint := net.JoinHostPort(app.options.Address, app.options.Port)
|
||||
|
||||
wsHandler := http.HandlerFunc(app.handleWS)
|
||||
customIndexHandler := http.HandlerFunc(app.handleCustomIndex)
|
||||
authTokenHandler := http.HandlerFunc(app.handleAuthToken)
|
||||
staticHandler := http.FileServer(
|
||||
&assetfs.AssetFS{Asset: Asset, AssetDir: AssetDir, Prefix: "static"},
|
||||
)
|
||||
|
||||
if app.options.Once {
|
||||
log.Printf("Once option is provided, accepting only one client")
|
||||
}
|
||||
|
||||
var siteMux = http.NewServeMux()
|
||||
|
||||
if app.options.IndexFile != "" {
|
||||
log.Printf("Using index file at " + app.options.IndexFile)
|
||||
indexHandler := http.HandlerFunc(
|
||||
func(w http.ResponseWriter, r *http.Request) {
|
||||
http.ServeFile(w, r, ExpandHomeDir(app.options.IndexFile))
|
||||
},
|
||||
)
|
||||
siteMux.Handle(path+"/", indexHandler)
|
||||
siteMux.Handle(path+"/", customIndexHandler)
|
||||
} else {
|
||||
siteMux.Handle(path+"/", http.StripPrefix(path+"/", staticHandler))
|
||||
}
|
||||
|
||||
siteMux.Handle(path+"/auth_token.js", authTokenHandler)
|
||||
siteMux.Handle(path+"/js/", http.StripPrefix(path+"/", staticHandler))
|
||||
siteMux.Handle(path+"/favicon.png", http.StripPrefix(path+"/", staticHandler))
|
||||
siteMux.Handle(path+"/ws", wsHandler)
|
||||
|
||||
siteHandler := http.Handler(siteMux)
|
||||
|
||||
|
|
@ -200,6 +183,11 @@ func (app *App) Run() error {
|
|||
siteHandler = wrapBasicAuth(siteHandler, app.options.Credential)
|
||||
}
|
||||
|
||||
wsMux := http.NewServeMux()
|
||||
wsMux.Handle("/", siteHandler)
|
||||
wsMux.Handle(path+"/ws", wsHandler)
|
||||
siteHandler = (http.Handler(wsMux))
|
||||
|
||||
siteHandler = wrapLogger(siteHandler)
|
||||
|
||||
scheme := "http"
|
||||
|
|
@ -264,6 +252,12 @@ func (app *App) handleWS(w http.ResponseWriter, r *http.Request) {
|
|||
return
|
||||
}
|
||||
|
||||
_, initMessage, err := conn.ReadMessage()
|
||||
if err != nil || string(initMessage) != app.authToken {
|
||||
log.Print("Failed to authenticate websocket connection")
|
||||
return
|
||||
}
|
||||
|
||||
cmd := exec.Command(app.command[0], app.command[1:]...)
|
||||
ptyIo, err := pty.Start(cmd)
|
||||
if err != nil {
|
||||
|
|
@ -283,6 +277,14 @@ func (app *App) handleWS(w http.ResponseWriter, r *http.Request) {
|
|||
context.goHandleClient()
|
||||
}
|
||||
|
||||
func (app *App) handleCustomIndex(w http.ResponseWriter, r *http.Request) {
|
||||
http.ServeFile(w, r, ExpandHomeDir(app.options.IndexFile))
|
||||
}
|
||||
|
||||
func (app *App) handleAuthToken(w http.ResponseWriter, r *http.Request) {
|
||||
w.Write([]byte("var gotty_auth_token = '" + app.authToken + "';"))
|
||||
}
|
||||
|
||||
func (app *App) Exit() (firstCall bool) {
|
||||
if app.server != nil {
|
||||
log.Printf("Received Exit command, waiting for all clients to close sessions...")
|
||||
|
|
@ -355,3 +357,11 @@ func listAddresses() (addresses []string) {
|
|||
|
||||
return
|
||||
}
|
||||
|
||||
func ExpandHomeDir(path string) string {
|
||||
if path[0:2] == "~/" {
|
||||
return os.Getenv("HOME") + path[1:]
|
||||
} else {
|
||||
return path
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue