From 0e81c484a997eebf830fd48df150fe5a2ce8898f Mon Sep 17 00:00:00 2001 From: Iwasaki Yudai Date: Mon, 31 Aug 2015 15:54:34 +0900 Subject: [PATCH] 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. --- app/app.go | 70 +++++++++++++++++++++++++------------------- app/resource.go | 8 ++--- resources/gotty.js | 2 ++ resources/index.html | 1 + 4 files changed, 47 insertions(+), 34 deletions(-) diff --git a/app/app.go b/app/app.go index 31481e1..81b3a30 100644 --- a/app/app.go +++ b/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 + } +} diff --git a/app/resource.go b/app/resource.go index 416047a..c8e7acd 100644 --- a/app/resource.go +++ b/app/resource.go @@ -91,7 +91,7 @@ func staticFaviconPng() (*asset, error) { return a, nil } -var _staticIndexHtml = []byte("\x1f\x8b\x08\x00\x00\x09\x6e\x88\x00\xff\x74\x90\xc1\x52\xc4\x20\x0c\x86\xef\x3e\x45\xc4\xf1\xe6\x94\x7a\xdd\xd2\x5e\x7d\x81\xbd\x78\x64\x4b\xb6\x64\xa5\xc0\x40\x5c\xed\x38\xbe\xbb\xb0\xd8\x93\xe3\x89\x3f\xf9\xff\x49\x3e\xa2\xee\x4d\x98\x79\x8b\x08\x96\x57\x37\xdd\xa9\xf6\x00\x28\x8b\xda\x54\x51\x24\x13\x3b\x9c\x5e\xc2\xf1\xf8\xaa\x64\x2b\x9a\x91\x79\x2b\xfa\x14\xcc\xf6\x04\x0f\x8c\x69\x25\xaf\x1d\x7c\xc5\x90\x89\x29\xf8\x03\xe8\x53\x0e\xee\x9d\x71\x00\x8b\xb4\x58\x3e\xc0\x73\xdf\x3f\x0e\xf0\x41\x86\xed\x5e\xac\x3a\x2d\x54\xc2\x7d\xfc\x1c\xbe\x95\x6c\x43\xdb\x02\x47\xfe\x0d\x12\xba\x51\xd0\x1c\xbc\x80\x4a\x5a\xf4\xaa\x17\x94\xd1\x2f\x02\x6c\xc2\xf3\x28\xce\xfa\x5a\xfd\xae\xb6\x6e\xf0\x72\xa7\x57\x15\xee\x77\x98\xa1\x2b\x90\x19\xc5\x0e\x2a\x26\x25\x4b\x6f\xff\xcb\x9c\x28\x32\xe4\x34\x8f\xa2\x93\x97\x2c\x6d\xcd\x75\x97\x5c\x63\xcd\xfc\x2f\xb9\x04\xe6\xed\x4f\x52\xc9\xb6\xbb\xc0\xdc\x6e\xfa\x13\x00\x00\xff\xff\x96\x39\xb4\x53\x6b\x01\x00\x00") +var _staticIndexHtml = []byte("\x1f\x8b\x08\x00\x00\x09\x6e\x88\x00\xff\x84\x90\xc1\x52\xc3\x20\x10\x86\xef\x3e\xc5\x8a\xe3\xcd\x09\xf1\xda\x90\x5c\x7d\x81\x5e\x3c\x39\x34\x6c\xc3\xb6\x04\x32\xb0\xad\x66\x1c\xdf\x5d\x28\xe6\xa4\x33\x9e\xf8\x77\xf7\x9b\xe5\x03\x75\x6f\xc2\xc8\xeb\x82\x60\x79\x76\xc3\x9d\xaa\x07\x80\xb2\xa8\x4d\x09\x39\x32\xb1\xc3\xe1\x25\xec\xf7\xaf\x4a\xd6\xa2\x0e\x12\xaf\x39\x1f\x82\x59\x9f\xe0\x81\x31\xce\xe4\xb5\x83\xcf\x25\x24\x62\x0a\x7e\x07\xfa\x90\x82\xbb\x30\x76\x60\x91\x26\xcb\x3b\x78\x6e\xdb\xc7\x0e\xde\xc9\xb0\xdd\x8a\x59\xc7\x89\x32\xdc\x2e\x1f\xdd\x97\x92\x75\x69\xbd\xc0\x91\x3f\x43\x44\xd7\x0b\x1a\x83\x17\x50\x4c\x73\x9e\xf5\x84\x72\xf1\x93\x00\x1b\xf1\xd8\x8b\xa3\xbe\x96\x79\x53\x5a\x37\x79\xb9\xd9\xab\x22\xf7\xb3\xcc\xd0\x15\xc8\xf4\x62\x13\x15\x83\x92\xb9\xb7\xbd\x65\x8c\xb4\x30\xa4\x38\xf6\xa2\x91\xa7\x24\x6d\xe1\x9a\x53\x2a\x58\x1d\xfe\x49\xea\x0b\xdb\x37\x0e\x67\xf4\xff\xb3\x79\xeb\x14\x98\xd7\x5f\xa4\x92\xd5\x33\x8b\xdf\xfe\xff\x3b\x00\x00\xff\xff\x35\xaf\x3e\x6a\x97\x01\x00\x00") func staticIndexHtmlBytes() ([]byte, error) { return bindataRead( @@ -106,12 +106,12 @@ func staticIndexHtml() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "static/index.html", size: 363, mode: os.FileMode(436), modTime: time.Unix(1440919621, 0)} + info := bindataFileInfo{name: "static/index.html", size: 407, mode: os.FileMode(436), modTime: time.Unix(1441003850, 0)} a := &asset{bytes: bytes, info: info} return a, nil } -var _staticJsGottyJs = []byte("\x1f\x8b\x08\x00\x00\x09\x6e\x88\x00\xff\x94\x56\x5f\x6f\xe2\x38\x10\x7f\xef\xa7\xb0\xf2\x82\x73\x47\x5d\xb8\x93\xee\x01\xc4\x9d\x74\x55\x57\xda\x3f\xda\xae\x0a\xbb\x7d\xa8\xfa\x60\x92\x01\xb2\x18\x1b\xd9\x4e\x23\x76\xc5\x77\xdf\x99\x10\x20\x04\xa7\x6c\xfd\x10\x19\xcf\xcc\x6f\xfe\x78\x7e\x63\xf8\x2c\xd7\x89\xcf\x8c\xe6\x31\xfb\x79\xc5\x70\xbd\x48\xcb\x16\xde\xaf\xdd\x9d\x96\x53\x05\x29\x1b\xb1\x22\xd3\xa9\x29\x84\x32\x89\x24\x55\xb1\xb6\xc6\x9b\xc4\x28\x36\x1a\xb1\xa8\xd4\x1d\x44\xc3\x83\x71\x6e\x51\xc0\xf8\x09\xc6\x7f\xac\x53\x38\x37\xb8\xb9\xe9\xb0\x01\x6d\x69\x17\xb3\x3f\xcf\x90\x17\xc6\xf9\xc0\xf1\x5a\xfa\x85\x96\x2b\x40\x11\x1a\x77\x8e\xbe\xf6\x91\x38\xf4\xf8\x14\xcd\x8d\xf7\x9b\xe8\xf9\x28\x96\xb9\x37\x0f\x90\x18\xad\x21\xf1\xa8\x72\xdd\x1f\x5e\x1d\x84\x66\x0d\xfa\x91\x0c\xcf\x4a\xb0\xd7\x28\x48\xaa\xa1\x60\x8f\x30\x1d\x9b\x64\x09\x9e\x63\x72\xdd\xa3\xd7\xb8\x82\xdb\x1b\x78\xb0\xab\xda\x51\xe1\x84\xd1\xe4\xa6\xee\x04\x5e\x40\xfb\xba\x27\x5a\x0b\xb2\x14\x29\xcc\x64\xae\xfc\xd8\x1b\x2b\xe7\x50\xf9\x56\xd9\x54\x54\x27\xe2\x13\x16\x44\xf1\x78\x78\xd1\x56\x24\x0a\xa4\xe5\xf5\xf8\x68\x91\x66\x05\xbb\xb3\x9a\xe0\x27\xd3\x3b\xcc\x33\x4d\x31\x07\xff\xc5\xc2\xcc\xf1\x58\x38\xcc\x3d\x72\xa0\xd3\x6b\xd0\x89\x49\x33\x3d\x8f\xba\x2c\xb2\xb2\x88\x82\x96\x46\xef\x91\x1f\x40\xa6\x9b\xb6\x22\xd7\x6b\x97\x19\xd4\x2a\x8d\x33\x23\xd6\xb9\x5b\x9c\xc5\x44\x0b\x65\x46\x7f\x9b\x7c\x84\x8d\xf3\xd6\x2c\xa1\x8e\x8c\x27\x21\xf0\xea\x26\x28\x78\x1e\xf5\x22\xec\x21\x52\x1c\x9e\xe9\x6d\xc3\xee\xc8\x6e\xec\x2d\x66\x8c\xbe\x9a\xee\xdb\x22\x3c\x66\xef\xb2\x1f\x27\x41\x62\xdb\xe4\x2b\xed\xba\xcc\x9a\xc2\x5d\x0a\x37\x28\xa4\x15\xf5\x29\x8f\x0f\xe3\xfb\xcf\xc2\x95\xb1\x65\xb3\x4d\xbb\x36\xad\xb0\xa3\xfa\xaa\x22\x1b\xec\x37\xdd\x8b\x16\x94\xc2\xa0\xfc\xbe\xae\xbb\x6d\x95\xc6\x41\xc9\xf9\x69\xe8\x6e\x76\xbd\xa2\x9d\x97\x4a\xe1\x85\x4c\x8d\xb4\x69\x93\x1b\x4d\xbb\x8a\x2a\x09\x92\xc4\x03\x4f\x4d\x92\xaf\x90\x8d\xd4\xe8\x77\x0a\x68\xfb\xff\xe6\x3d\x76\x89\xaf\xae\x2f\x8a\x6b\x78\xdb\x26\xb3\x57\xe0\xdc\x8e\xa7\xaf\x93\x3b\x95\x5e\xa2\x52\x29\x13\xf4\x43\x38\x95\x25\xc0\xfb\x8d\x60\x5d\x91\xf9\x64\xc1\x8f\x7a\x4f\xbd\xe7\x26\x56\x22\x1d\xb0\x4e\xaf\x33\x68\x29\x87\x11\x85\xcd\x3c\x7c\x9d\xbc\xeb\xff\xc3\x09\x23\xd0\xe8\x53\x0b\x72\x39\x0c\xc0\xf6\xdb\x60\x91\xfd\x8f\xe5\x48\x9e\x64\x5e\xc1\x9b\x71\xff\x0a\xe0\xae\x71\xb0\x80\xc5\x61\x02\x34\x64\xcb\x4e\x5e\x4b\xeb\x5a\xc1\xef\xa7\xdf\x71\x86\x8b\x25\x32\x8f\xd7\x6c\x63\x31\x33\xf6\x4e\x62\xd9\x0e\x77\x80\x2a\x6d\xbc\xc2\x97\xc0\x19\x05\xf8\xb0\xcc\x79\x34\x06\xef\x89\xd5\xc4\x24\xb4\xc1\x6f\x34\x28\x7f\xd4\x63\x7b\x42\xc9\x73\x20\x9c\x43\x69\x1a\x33\x12\xd5\xbb\xbf\x63\xbf\x7d\x4b\xfd\xfe\x0e\xd4\xaf\xf9\xb4\x5d\xae\x60\x00\xfe\x48\xcc\x6d\xa3\xb7\x13\x65\xdc\xe5\xce\xce\x66\x8c\x53\x15\x42\xf5\x2e\xab\x93\xeb\x0b\x04\x3d\x68\xd2\xa4\x5d\x98\xe2\xfe\x05\xac\x92\x1b\x1e\xdd\xee\x32\x43\xd7\xec\x96\x62\x49\xf1\xbd\xd1\xb9\x52\x4d\x82\x9f\xd2\xdb\x6e\x0e\x35\xa9\x7b\x6a\x66\x07\xd6\x1a\x7b\x92\x1d\x1d\x34\xb3\x68\x47\xab\x61\xee\xfe\x45\x1c\xde\xaa\xf2\x5f\x40\xcd\xae\xed\xe5\xa3\xca\x9d\x5e\xe1\xbf\x23\xd6\x6b\x46\x80\x1d\x35\xc9\x56\x60\x72\xcf\x77\x7e\xba\x8d\x7b\xff\x83\xf5\x7b\xbd\x5e\x20\xb6\x6d\xcc\xe3\xab\x5f\x01\x00\x00\xff\xff\x42\x24\x4b\x87\xdd\x09\x00\x00") +var _staticJsGottyJs = []byte("\x1f\x8b\x08\x00\x00\x09\x6e\x88\x00\xff\x94\x56\xdd\x8e\xda\x3a\x10\xbe\xdf\xa7\xb0\x72\x83\x73\x0e\x9b\x85\x73\xa4\x5e\x80\x68\xa5\xae\xb6\x52\x7f\xd4\xad\x16\xda\xbd\x58\xad\x56\x26\x19\x48\x8a\xb1\x91\x3d\xd9\x88\x56\xbc\x7b\xc7\x21\x40\x08\xce\xd2\xf5\x45\x64\x3c\xdf\x7c\xf3\xe3\x99\x31\x7c\x96\xab\x18\x33\xad\x78\xc8\x7e\x5f\x30\x5a\xcf\xc2\xb0\x14\x71\x65\x6f\x94\x98\x4a\x48\xd8\x88\x15\x99\x4a\x74\x11\x49\x1d\x0b\x07\x8d\x56\x46\xa3\x8e\xb5\x64\xa3\x11\x0b\x4a\xec\x20\x18\xee\x95\x73\x43\x02\xc6\x8f\x38\xde\xb1\x4e\x61\xed\xe0\xea\xaa\xc3\x06\x6e\xeb\x76\x21\xfb\xf7\x84\x39\xd5\x16\x3d\xc7\x2b\x81\xa9\x12\x4b\x20\x11\x29\x77\x0e\xb6\x76\x9e\x58\xb2\xf8\x10\xcc\x35\xe2\x3a\x78\x3c\x88\x45\x8e\xfa\x0e\x62\xad\x14\xc4\x48\x90\xcb\xfe\xf0\x62\x2f\xd4\x2b\x50\xf7\x4e\xf1\x24\x05\x3b\x44\xe1\xa4\x0a\x0a\x76\x0f\xd3\xb1\x8e\x17\x80\x9c\x82\xeb\x1e\xac\x86\x15\xdd\x4e\x01\xc1\x2c\x6b\x47\x85\x8d\xb4\x72\x66\xea\x46\xe0\x19\x14\xd6\x2d\x55\x48\x0b\x2a\xe1\x65\x04\x4f\xe4\x76\xfa\x84\x7a\x01\xaa\x6e\xc0\xad\xd4\x59\x88\x12\x98\x89\x5c\xe2\x18\xb5\x11\x73\xa8\x7c\x94\xd9\x34\xaa\x4e\xa2\x2f\x94\x38\xc9\xc3\xe1\x59\xdd\x28\x96\x20\x0c\x6f\x9a\x71\xc8\x8a\x76\xab\x35\xa1\x4f\xa6\xb6\x9c\x27\xc8\x68\x0e\xf8\xcd\xc0\xcc\xf2\x90\xa2\x40\x1e\xb8\x50\x2e\x41\xc5\x3a\xc9\xd4\x3c\xe8\xb2\xc0\x88\x22\xf0\x6a\x6a\xb5\x63\xbe\x03\x91\xac\xdb\x2e\xa3\x9e\xe3\x4c\x13\xaa\x54\xce\x74\xb4\xca\x6d\x7a\xe2\x93\x5b\x24\xd3\xea\xc7\xe4\x33\xac\x2d\x1a\xca\x64\x9d\x99\x4e\x7c\xe4\xf5\x7b\x08\x7a\x01\xd5\x9a\x03\x0e\x4f\x70\x1b\xbf\x39\xa7\x37\x46\x43\x11\x93\xad\xa6\xf9\x36\x0f\x0f\xd1\xdb\xec\xd7\x91\x93\x54\x5e\xf9\x52\xd9\x2e\x33\xba\xb0\xe7\xdc\xf5\x0a\xdd\x0a\xfa\x2e\x8e\x4f\xe3\xdb\xaf\x91\x2d\x7d\xcb\x66\xeb\x76\xb4\x5b\x7e\x43\xf5\x55\x79\x36\xd8\x6d\xba\x67\x35\x5c\x08\x83\xf2\xfb\x32\x76\xd3\x2a\x0d\xbd\x92\xd3\x53\xdf\xdd\x6c\x6b\x45\x59\x14\x52\xd2\x85\x4c\xb5\x30\x49\xb3\x37\x9a\x7a\x55\xab\xc4\xd4\x24\x08\x3c\xd1\x71\xbe\xa4\xae\x75\x85\x7e\x23\xc1\x6d\xdf\xaf\x3f\x52\x95\x60\x75\x7d\x41\x58\xe3\xdb\x34\x27\xc0\x12\xac\xdd\xf6\xe9\xcb\x43\x20\x11\x28\x08\x54\xca\x22\xf7\x23\xb2\x32\x8b\x81\xf7\x1b\xce\xda\x22\xc3\x38\xe5\x07\xdc\x43\xef\xb1\xc9\x15\x0b\x0b\xac\xd3\xeb\x0c\x5a\xd2\xa1\xa3\xc2\x64\x08\xdf\x27\x1f\xfa\x6f\xb8\xe3\xf0\x14\xfa\xd4\x80\x58\x0c\x3d\xb4\xfd\x36\x5a\xea\xfe\xfb\x72\x74\x4f\x32\x94\xf0\x6a\xde\xff\x3c\xbc\x2b\x1a\x2c\x60\x68\x98\x80\x1b\xc6\x65\x25\xaf\x84\xb1\xad\xe4\xb7\xd3\x9f\x34\xeb\xa3\x05\x75\x1e\xaf\xe9\x86\xd1\x4c\x9b\x1b\x41\x69\xdb\xdf\x01\x41\xda\xfa\x8a\x5e\x0c\xab\x25\xd0\x03\x34\xe7\xc1\x18\x10\x5d\x57\xbb\x4e\x22\x1d\xfa\x06\x83\xf2\x47\xdd\xb7\x07\x92\x3c\x7a\xdc\xd9\xa7\xa6\x31\x23\x09\xde\xfd\x1b\xfd\xcd\x6b\xf2\xf7\xbf\x27\x7f\xcd\x27\xf0\x7c\x06\x3d\xf4\x87\xc6\xdc\x34\x6a\x3b\x96\xda\x9e\xaf\xec\x6c\xc6\xb8\xcb\x82\x2f\xdf\x65\x76\x72\x75\xa6\x41\xf7\x48\x37\x69\x53\x5d\xdc\x3e\x83\x91\x62\xcd\x83\xeb\x6d\x64\x64\x9a\x5d\x3b\x5f\x12\x7a\x6f\x54\x2e\x65\xb3\xc1\x8f\xdb\xdb\xac\xf7\x39\xa9\x5b\x6a\x46\x07\xc6\x68\x73\x14\x9d\x3b\x68\x46\xd1\xce\x56\xe3\xdc\xfe\xdb\xd8\xbf\x55\xe5\xbf\x85\x9a\x5e\xdb\xcb\xe7\x32\x77\x7c\x85\x6f\x47\xac\xd7\xf4\x80\x2a\x6a\x92\x2d\x41\xe7\xc8\xb7\x76\xba\x8d\x7b\xff\x87\xf5\x7b\xbd\x9e\xc7\xb7\x4d\xc8\xc3\x8b\x3f\x01\x00\x00\xff\xff\x4f\xa7\xa4\xe9\x05\x0a\x00\x00") func staticJsGottyJsBytes() ([]byte, error) { return bindataRead( @@ -126,7 +126,7 @@ func staticJsGottyJs() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "static/js/gotty.js", size: 2525, mode: os.FileMode(436), modTime: time.Unix(1440919623, 0)} + info := bindataFileInfo{name: "static/js/gotty.js", size: 2565, mode: os.FileMode(436), modTime: time.Unix(1441003850, 0)} a := &asset{bytes: bytes, info: info} return a, nil } diff --git a/resources/gotty.js b/resources/gotty.js index b2293e5..5e10a9e 100644 --- a/resources/gotty.js +++ b/resources/gotty.js @@ -10,6 +10,8 @@ var term; ws.onopen = function(event) { + ws.send(gotty_auth_token); + hterm.defaultStorage = new lib.Storage.Local(); hterm.defaultStorage.clear(); diff --git a/resources/index.html b/resources/index.html index b33ff9f..5212a47 100644 --- a/resources/index.html +++ b/resources/index.html @@ -8,6 +8,7 @@
+