@@ -92,14 +92,20 @@ func (s *Server) configureHeaders(e *echo.Echo) error {
9292 cspBuilder := cspbuilder.Builder {
9393 Directives : map [string ][]string {
9494 cspbuilder .DefaultSrc : {"'self'" },
95- cspbuilder .ScriptSrc : append (
96- // Warning: script-src 'self' may not be safe to use if we're hosting user-uploaded content.
97- // Then we'll need to provide hashes for scripts & styles we include by URL, and we'll need
98- // to add the SRI integrity attribute to the tags including those files; however, it's
99- // unclear how well-supported they are by browsers.
100- []string {"'self'" , "'unsafe-inline'" },
101- s .Inlines .ComputeJSHashesForCSP ()... ,
102- ),
95+ // Note: the following is needed for the Tailscale web GUI to check device status (but the GUI
96+ // still works without this permission, it just doesn't report device status):
97+ cspbuilder .ConnectSrc : {"*" },
98+ // Note: script-src "unsafe-inline" (which is ignored if we provide one or more hashes for
99+ // CSP) is needed by the Tailscale web GUI:
100+ cspbuilder .ScriptSrc : {"'self'" , "'unsafe-inline'" },
101+ // cspbuilder.ScriptSrc: append(
102+ // // Warning: script-src 'self' may not be safe to use if we're hosting user-uploaded content.
103+ // // Then we'll need to provide hashes for scripts & styles we include by URL, and we'll need
104+ // // to add the SRI integrity attribute to the tags including those files; however, it's
105+ // // unclear how well-supported they are by browsers.
106+ // []string{"'self'", "'unsafe-inline'"},
107+ // s.Inlines.ComputeJSHashesForCSP()...,
108+ // ),
103109 cspbuilder .StyleSrc : append (
104110 []string {
105111 "'self'" ,
@@ -108,9 +114,10 @@ func (s *Server) configureHeaders(e *echo.Echo) error {
108114 },
109115 s .Inlines .ComputeCSSHashesForCSP ()... ,
110116 ),
111- cspbuilder .ObjectSrc : {"'none'" },
112- cspbuilder .ChildSrc : {"'self'" },
113- cspbuilder .ImgSrc : {"*" },
117+ cspbuilder .ObjectSrc : {"'none'" },
118+ cspbuilder .ChildSrc : {"'self'" },
119+ // Note: img-src with scheme "data:" is needed by the Tailscale web GUI:
120+ cspbuilder .ImgSrc : {"*" , "data:" },
114121 cspbuilder .BaseURI : {"'none'" },
115122 cspbuilder .FormAction : {"'self'" },
116123 cspbuilder .FrameAncestors : {"'none'" },
@@ -145,17 +152,23 @@ func (s *Server) Register(e *echo.Echo) error {
145152 // Compression Middleware
146153 e .Use (middleware .Decompress ())
147154 e .Use (middleware .GzipWithConfig (middleware.GzipConfig {
148- Level : s .Globals .Config .HTTP .GzipLevel ,
155+ Level : s .Globals .Config .HTTP .GzipLevel ,
156+ Skipper : s .Handlers .GzipSkipper ,
149157 }))
150158
151159 // Other Middleware
152- e .Pre (middleware .RemoveTrailingSlash ())
153- e .Use (gmw .RequireContentTypes (echo .MIMEApplicationForm ))
160+ e .Pre (middleware .RemoveTrailingSlashWithConfig (middleware.TrailingSlashConfig {
161+ Skipper : s .Handlers .TrailingSlashSkipper ,
162+ }))
163+ // application/JSON is needed by the Tailscale web GUI:
164+ e .Use (gmw .RequireContentTypes (echo .MIMEApplicationForm , echo .MIMEApplicationJSON ))
154165 // TODO: enable Prometheus and rate-limiting
155166
156167 // Handlers
157168 e .HTTPErrorHandler = NewHTTPErrorHandler (s .Renderer , s .Embeds .TemplatesFS )
158- s .Handlers .Register (e , s .Embeds )
169+ if err := s .Handlers .Register (e , s .Embeds ); err != nil {
170+ return errors .Wrap (err , "couldn't register HTTP route handlers" )
171+ }
159172
160173 return nil
161174}
@@ -185,6 +198,7 @@ func (s *Server) Shutdown(ctx context.Context, e *echo.Echo) (err error) {
185198 // FIXME: e.Shutdown calls e.Server.Shutdown, which doesn't wait for WebSocket connections. When
186199 // starting Echo, we need to call e.Server.RegisterOnShutdown with a function to gracefully close
187200 // WebSocket connections!
201+ s .Globals .Tailscale .Shutdown ()
188202 if errEcho := e .Shutdown (ctx ); errEcho != nil {
189203 s .Globals .Base .Logger .Error (errors .Wrap (errEcho , "couldn't shut down http server" ))
190204 err = errEcho
0 commit comments