-
-
Notifications
You must be signed in to change notification settings - Fork 803
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Disallow access in publicly exposed services (#1761)
* Add security against publicly exposed services * Add trusted proxies setting, validate proxy chain against internet access * Validate chain on local proxies too * Move authentication handler to separate file * Add startup check and log if tripwire is active Co-authored-by: WithoutPants <53250216+WithoutPants@users.noreply.github.com>
- Loading branch information
1 parent
dcf58b9
commit f1da6cb
Showing
12 changed files
with
344 additions
and
62 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,138 @@ | ||
package api | ||
|
||
import ( | ||
"net" | ||
"net/http" | ||
"net/url" | ||
"strings" | ||
|
||
"github.com/stashapp/stash/pkg/logger" | ||
"github.com/stashapp/stash/pkg/manager" | ||
"github.com/stashapp/stash/pkg/manager/config" | ||
"github.com/stashapp/stash/pkg/session" | ||
) | ||
|
||
const loginEndPoint = "/login" | ||
|
||
const ( | ||
tripwireActivatedErrMsg = "Stash is exposed to the public internet without authentication, and is not serving any more content to protect your privacy. " + | ||
"More information and fixes are available at https://github.com/stashapp/stash/wiki/Authentication-Required-When-Accessing-Stash-From-the-Internet" | ||
|
||
externalAccessErrMsg = "You have attempted to access Stash over the internet, and authentication is not enabled. " + | ||
"This is extremely dangerous! The whole world can see your your stash page and browse your files! " + | ||
"Stash is not answering any other requests to protect your privacy. " + | ||
"Please read the log entry or visit https://github.com/stashapp/stash/wiki/Authentication-Required-When-Accessing-Stash-From-the-Internet" | ||
) | ||
|
||
func allowUnauthenticated(r *http.Request) bool { | ||
return strings.HasPrefix(r.URL.Path, loginEndPoint) || r.URL.Path == "/css" | ||
} | ||
|
||
func authenticateHandler() func(http.Handler) http.Handler { | ||
return func(next http.Handler) http.Handler { | ||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { | ||
c := config.GetInstance() | ||
|
||
if !checkSecurityTripwireActivated(c, w) { | ||
return | ||
} | ||
|
||
userID, err := manager.GetInstance().SessionStore.Authenticate(w, r) | ||
if err != nil { | ||
if err != session.ErrUnauthorized { | ||
w.WriteHeader(http.StatusInternalServerError) | ||
_, err = w.Write([]byte(err.Error())) | ||
if err != nil { | ||
logger.Error(err) | ||
} | ||
return | ||
} | ||
|
||
// unauthorized error | ||
w.Header().Add("WWW-Authenticate", `FormBased`) | ||
w.WriteHeader(http.StatusUnauthorized) | ||
return | ||
} | ||
|
||
if err := session.CheckAllowPublicWithoutAuth(c, r); err != nil { | ||
switch err := err.(type) { | ||
case session.ExternalAccessError: | ||
securityActivateTripwireAccessedFromInternetWithoutAuth(c, err, w) | ||
return | ||
case session.UntrustedProxyError: | ||
logger.Warnf("Rejected request from untrusted proxy: %s", net.IP(err).String()) | ||
w.WriteHeader(http.StatusForbidden) | ||
return | ||
default: | ||
logger.Errorf("Error checking external access security: %s", err.Error()) | ||
w.WriteHeader(http.StatusInternalServerError) | ||
return | ||
} | ||
} | ||
|
||
ctx := r.Context() | ||
|
||
if c.HasCredentials() { | ||
// authentication is required | ||
if userID == "" && !allowUnauthenticated(r) { | ||
// authentication was not received, redirect | ||
// if graphql was requested, we just return a forbidden error | ||
if r.URL.Path == "/graphql" { | ||
w.Header().Add("WWW-Authenticate", `FormBased`) | ||
w.WriteHeader(http.StatusUnauthorized) | ||
return | ||
} | ||
|
||
// otherwise redirect to the login page | ||
u := url.URL{ | ||
Path: "/login", | ||
} | ||
q := u.Query() | ||
q.Set(returnURLParam, r.URL.Path) | ||
u.RawQuery = q.Encode() | ||
http.Redirect(w, r, u.String(), http.StatusFound) | ||
return | ||
} | ||
} | ||
|
||
ctx = session.SetCurrentUserID(ctx, userID) | ||
|
||
r = r.WithContext(ctx) | ||
|
||
next.ServeHTTP(w, r) | ||
}) | ||
} | ||
} | ||
|
||
func checkSecurityTripwireActivated(c *config.Instance, w http.ResponseWriter) bool { | ||
if accessErr := session.CheckExternalAccessTripwire(c); accessErr != nil { | ||
w.WriteHeader(http.StatusForbidden) | ||
_, err := w.Write([]byte(tripwireActivatedErrMsg)) | ||
if err != nil { | ||
logger.Error(err) | ||
} | ||
return false | ||
} | ||
|
||
return true | ||
} | ||
|
||
func securityActivateTripwireAccessedFromInternetWithoutAuth(c *config.Instance, accessErr session.ExternalAccessError, w http.ResponseWriter) { | ||
session.LogExternalAccessError(accessErr) | ||
|
||
err := c.ActivatePublicAccessTripwire(net.IP(accessErr).String()) | ||
if err != nil { | ||
logger.Error(err) | ||
} | ||
|
||
w.WriteHeader(http.StatusForbidden) | ||
_, err = w.Write([]byte(externalAccessErrMsg)) | ||
if err != nil { | ||
logger.Error(err) | ||
} | ||
|
||
err = manager.GetInstance().Shutdown() | ||
if err != nil { | ||
logger.Error(err) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.