mirror of
https://github.com/octoleo/syncthing.git
synced 2025-02-02 11:58:28 +00:00
Use different session cookies per device
This commit is contained in:
parent
0450d48f89
commit
089fca2319
@ -52,6 +52,7 @@ var (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type apiSvc struct {
|
type apiSvc struct {
|
||||||
|
id protocol.DeviceID
|
||||||
cfg config.GUIConfiguration
|
cfg config.GUIConfiguration
|
||||||
assetDir string
|
assetDir string
|
||||||
model *model.Model
|
model *model.Model
|
||||||
@ -62,8 +63,9 @@ type apiSvc struct {
|
|||||||
eventSub *events.BufferedSubscription
|
eventSub *events.BufferedSubscription
|
||||||
}
|
}
|
||||||
|
|
||||||
func newAPISvc(cfg config.GUIConfiguration, assetDir string, m *model.Model, eventSub *events.BufferedSubscription) (*apiSvc, error) {
|
func newAPISvc(id protocol.DeviceID, cfg config.GUIConfiguration, assetDir string, m *model.Model, eventSub *events.BufferedSubscription) (*apiSvc, error) {
|
||||||
svc := &apiSvc{
|
svc := &apiSvc{
|
||||||
|
id: id,
|
||||||
cfg: cfg,
|
cfg: cfg,
|
||||||
assetDir: assetDir,
|
assetDir: assetDir,
|
||||||
model: m,
|
model: m,
|
||||||
@ -188,14 +190,14 @@ func (s *apiSvc) Serve() {
|
|||||||
|
|
||||||
// Wrap everything in CSRF protection. The /rest prefix should be
|
// Wrap everything in CSRF protection. The /rest prefix should be
|
||||||
// protected, other requests will grant cookies.
|
// protected, other requests will grant cookies.
|
||||||
handler := csrfMiddleware("/rest", s.cfg.APIKey, mux)
|
handler := csrfMiddleware(s.id.String()[:5], "/rest", s.cfg.APIKey, mux)
|
||||||
|
|
||||||
// Add our version as a header to responses
|
// Add our version and ID as a header to responses
|
||||||
handler = withVersionMiddleware(handler)
|
handler = withDetailsMiddleware(s.id, handler)
|
||||||
|
|
||||||
// Wrap everything in basic auth, if user/password is set.
|
// Wrap everything in basic auth, if user/password is set.
|
||||||
if len(s.cfg.User) > 0 && len(s.cfg.Password) > 0 {
|
if len(s.cfg.User) > 0 && len(s.cfg.Password) > 0 {
|
||||||
handler = basicAuthAndSessionMiddleware(s.cfg, handler)
|
handler = basicAuthAndSessionMiddleware("sessionid-"+s.id.String()[:5], s.cfg, handler)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Redirect to HTTPS if we are supposed to
|
// Redirect to HTTPS if we are supposed to
|
||||||
@ -334,9 +336,10 @@ func noCacheMiddleware(h http.Handler) http.Handler {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func withVersionMiddleware(h http.Handler) http.Handler {
|
func withDetailsMiddleware(id protocol.DeviceID, h http.Handler) http.Handler {
|
||||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
w.Header().Set("X-Syncthing-Version", Version)
|
w.Header().Set("X-Syncthing-Version", Version)
|
||||||
|
w.Header().Set("X-Syncthing-ID", id.String())
|
||||||
h.ServeHTTP(w, r)
|
h.ServeHTTP(w, r)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -24,14 +24,15 @@ var (
|
|||||||
sessionsMut = sync.NewMutex()
|
sessionsMut = sync.NewMutex()
|
||||||
)
|
)
|
||||||
|
|
||||||
func basicAuthAndSessionMiddleware(cfg config.GUIConfiguration, next http.Handler) http.Handler {
|
func basicAuthAndSessionMiddleware(cookieName string, cfg config.GUIConfiguration, next http.Handler) http.Handler {
|
||||||
|
|
||||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
if cfg.APIKey != "" && r.Header.Get("X-API-Key") == cfg.APIKey {
|
if cfg.APIKey != "" && r.Header.Get("X-API-Key") == cfg.APIKey {
|
||||||
next.ServeHTTP(w, r)
|
next.ServeHTTP(w, r)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
cookie, err := r.Cookie("sessionid")
|
cookie, err := r.Cookie(cookieName)
|
||||||
if err == nil && cookie != nil {
|
if err == nil && cookie != nil {
|
||||||
sessionsMut.Lock()
|
sessionsMut.Lock()
|
||||||
_, ok := sessions[cookie.Value]
|
_, ok := sessions[cookie.Value]
|
||||||
@ -86,7 +87,7 @@ func basicAuthAndSessionMiddleware(cfg config.GUIConfiguration, next http.Handle
|
|||||||
sessions[sessionid] = true
|
sessions[sessionid] = true
|
||||||
sessionsMut.Unlock()
|
sessionsMut.Unlock()
|
||||||
http.SetCookie(w, &http.Cookie{
|
http.SetCookie(w, &http.Cookie{
|
||||||
Name: "sessionid",
|
Name: cookieName,
|
||||||
Value: sessionid,
|
Value: sessionid,
|
||||||
MaxAge: 0,
|
MaxAge: 0,
|
||||||
})
|
})
|
||||||
|
@ -24,7 +24,7 @@ var csrfMut = sync.NewMutex()
|
|||||||
// Check for CSRF token on /rest/ URLs. If a correct one is not given, reject
|
// Check for CSRF token on /rest/ URLs. If a correct one is not given, reject
|
||||||
// the request with 403. For / and /index.html, set a new CSRF cookie if none
|
// the request with 403. For / and /index.html, set a new CSRF cookie if none
|
||||||
// is currently set.
|
// is currently set.
|
||||||
func csrfMiddleware(prefix, apiKey string, next http.Handler) http.Handler {
|
func csrfMiddleware(unique, prefix, apiKey string, next http.Handler) http.Handler {
|
||||||
loadCsrfTokens()
|
loadCsrfTokens()
|
||||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
// Allow requests carrying a valid API key
|
// Allow requests carrying a valid API key
|
||||||
@ -35,10 +35,10 @@ func csrfMiddleware(prefix, apiKey string, next http.Handler) http.Handler {
|
|||||||
|
|
||||||
// Allow requests for the front page, and set a CSRF cookie if there isn't already a valid one.
|
// Allow requests for the front page, and set a CSRF cookie if there isn't already a valid one.
|
||||||
if !strings.HasPrefix(r.URL.Path, prefix) {
|
if !strings.HasPrefix(r.URL.Path, prefix) {
|
||||||
cookie, err := r.Cookie("CSRF-Token")
|
cookie, err := r.Cookie("CSRF-Token-" + unique)
|
||||||
if err != nil || !validCsrfToken(cookie.Value) {
|
if err != nil || !validCsrfToken(cookie.Value) {
|
||||||
cookie = &http.Cookie{
|
cookie = &http.Cookie{
|
||||||
Name: "CSRF-Token",
|
Name: "CSRF-Token-" + unique,
|
||||||
Value: newCsrfToken(),
|
Value: newCsrfToken(),
|
||||||
}
|
}
|
||||||
http.SetCookie(w, cookie)
|
http.SetCookie(w, cookie)
|
||||||
@ -54,7 +54,7 @@ func csrfMiddleware(prefix, apiKey string, next http.Handler) http.Handler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Verify the CSRF token
|
// Verify the CSRF token
|
||||||
token := r.Header.Get("X-CSRF-Token")
|
token := r.Header.Get("X-CSRF-Token-" + unique)
|
||||||
if !validCsrfToken(token) {
|
if !validCsrfToken(token) {
|
||||||
http.Error(w, "CSRF Error", 403)
|
http.Error(w, "CSRF Error", 403)
|
||||||
return
|
return
|
||||||
|
@ -808,7 +808,7 @@ func setupGUI(mainSvc *suture.Supervisor, cfg *config.Wrapper, m *model.Model, a
|
|||||||
|
|
||||||
urlShow := fmt.Sprintf("%s://%s/", proto, net.JoinHostPort(hostShow, strconv.Itoa(addr.Port)))
|
urlShow := fmt.Sprintf("%s://%s/", proto, net.JoinHostPort(hostShow, strconv.Itoa(addr.Port)))
|
||||||
l.Infoln("Starting web GUI on", urlShow)
|
l.Infoln("Starting web GUI on", urlShow)
|
||||||
api, err := newAPISvc(guiCfg, guiAssets, m, apiSub)
|
api, err := newAPISvc(myID, guiCfg, guiAssets, m, apiSub)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
l.Fatalln("Cannot start GUI:", err)
|
l.Fatalln("Cannot start GUI:", err)
|
||||||
}
|
}
|
||||||
|
@ -17,10 +17,9 @@ var syncthing = angular.module('syncthing', [
|
|||||||
|
|
||||||
var urlbase = 'rest';
|
var urlbase = 'rest';
|
||||||
var guiVersion = null;
|
var guiVersion = null;
|
||||||
|
var deviceId = null;
|
||||||
|
|
||||||
syncthing.config(function ($httpProvider, $translateProvider, LocaleServiceProvider) {
|
syncthing.config(function ($httpProvider, $translateProvider, LocaleServiceProvider) {
|
||||||
$httpProvider.defaults.xsrfHeaderName = 'X-CSRF-Token';
|
|
||||||
$httpProvider.defaults.xsrfCookieName = 'CSRF-Token';
|
|
||||||
$httpProvider.interceptors.push(function () {
|
$httpProvider.interceptors.push(function () {
|
||||||
return {
|
return {
|
||||||
response: function (response) {
|
response: function (response) {
|
||||||
@ -30,6 +29,14 @@ syncthing.config(function ($httpProvider, $translateProvider, LocaleServiceProvi
|
|||||||
} else if (guiVersion != responseVersion) {
|
} else if (guiVersion != responseVersion) {
|
||||||
document.location.reload(true);
|
document.location.reload(true);
|
||||||
}
|
}
|
||||||
|
if (!deviceId) {
|
||||||
|
deviceId = response.headers()['x-syncthing-id'];
|
||||||
|
if (deviceId) {
|
||||||
|
var deviceIdShort = deviceId.substring(0, 5);
|
||||||
|
$httpProvider.defaults.xsrfHeaderName = 'X-CSRF-Token-' + deviceIdShort;
|
||||||
|
$httpProvider.defaults.xsrfCookieName = 'CSRF-Token-' + deviceIdShort;
|
||||||
|
}
|
||||||
|
}
|
||||||
return response;
|
return response;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
File diff suppressed because one or more lines are too long
Loading…
x
Reference in New Issue
Block a user