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>
This commit is contained in:
kermieisinthehouse
2021-10-04 07:16:01 +00:00
committed by GitHub
parent dcf58b99a6
commit f1da6cb1b2
12 changed files with 344 additions and 62 deletions

View File

@@ -138,6 +138,13 @@ const SlideshowDelay = "slideshow_delay"
const HandyKey = "handy_key"
const FunscriptOffset = "funscript_offset"
// Security
const TrustedProxies = "trusted_proxies"
const dangerousAllowPublicWithoutAuth = "dangerous_allow_public_without_auth"
const dangerousAllowPublicWithoutAuthDefault = "false"
const SecurityTripwireAccessedFromPublicInternet = "security_tripwire_accessed_from_public_internet"
const securityTripwireAccessedFromPublicInternetDefault = ""
// DLNA options
const DLNAServerName = "dlna.server_name"
const DLNADefaultEnabled = "dlna.default_enabled"
@@ -838,6 +845,31 @@ func (i *Instance) GetFunscriptOffset() int {
return viper.GetInt(FunscriptOffset)
}
// GetTrustedProxies returns a comma separated list of ip addresses that should allow proxying.
// When empty, allow from any private network
func (i *Instance) GetTrustedProxies() []string {
i.RLock()
defer i.RUnlock()
return viper.GetStringSlice(TrustedProxies)
}
// GetDangerousAllowPublicWithoutAuth determines if the security feature is enabled.
// See https://github.com/stashapp/stash/wiki/Authentication-Required-When-Accessing-Stash-From-the-Internet
func (i *Instance) GetDangerousAllowPublicWithoutAuth() bool {
i.RLock()
defer i.RUnlock()
return viper.GetBool(dangerousAllowPublicWithoutAuth)
}
// GetSecurityTripwireAccessedFromPublicInternet returns a public IP address if stash
// has been accessed from the public internet, with no auth enabled, and
// DangerousAllowPublicWithoutAuth disabled. Returns an empty string otherwise.
func (i *Instance) GetSecurityTripwireAccessedFromPublicInternet() string {
i.RLock()
defer i.RUnlock()
return viper.GetString(SecurityTripwireAccessedFromPublicInternet)
}
// GetDLNAServerName returns the visible name of the DLNA server. If empty,
// "stash" will be used.
func (i *Instance) GetDLNAServerName() string {
@@ -930,6 +962,14 @@ func (i *Instance) GetMaxUploadSize() int64 {
return ret << 20
}
// ActivatePublicAccessTripwire sets the security_tripwire_accessed_from_public_internet
// config field to the provided IP address to indicate that stash has been accessed
// from this public IP without authentication.
func (i *Instance) ActivatePublicAccessTripwire(requestIP string) error {
i.Set(SecurityTripwireAccessedFromPublicInternet, requestIP)
return i.Write()
}
func (i *Instance) Validate() error {
i.RLock()
defer i.RUnlock()
@@ -982,6 +1022,9 @@ func (i *Instance) setDefaultValues(write bool) error {
viper.SetDefault(Database, defaultDatabaseFilePath)
viper.SetDefault(dangerousAllowPublicWithoutAuth, dangerousAllowPublicWithoutAuthDefault)
viper.SetDefault(SecurityTripwireAccessedFromPublicInternet, securityTripwireAccessedFromPublicInternetDefault)
// Set generated to the metadata path for backwards compat
viper.SetDefault(Generated, viper.GetString(Metadata))

View File

@@ -97,6 +97,8 @@ func Initialize() *singleton {
panic(err)
}
}
initSecurity(cfg)
} else {
cfgFile := cfg.GetConfigFile()
if cfgFile != "" {
@@ -125,6 +127,12 @@ func Initialize() *singleton {
return instance
}
func initSecurity(cfg *config.Instance) {
if err := session.CheckExternalAccessTripwire(cfg); err != nil {
session.LogExternalAccessError(*err)
}
}
func initProfiling(cpuProfilePath string) {
if cpuProfilePath == "" {
return