mirror of
				https://github.com/go-gitea/gitea.git
				synced 2025-11-03 08:02:36 +09:00 
			
		
		
		
	Previously, the default was a week. As most instances don't set the setting, this leads to a bad user experience by default. ## ⚠️ Breaking If your instance requires a high level of security, you may want to set `[security].LOGIN_REMEMBER_DAYS` so that logins are not valid as long. --------- Co-authored-by: Jason Song <i@wolfogre.com>
		
			
				
	
	
		
			172 lines
		
	
	
		
			6.6 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			172 lines
		
	
	
		
			6.6 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
// Copyright 2023 The Gitea Authors. All rights reserved.
 | 
						|
// SPDX-License-Identifier: MIT
 | 
						|
 | 
						|
package setting
 | 
						|
 | 
						|
import (
 | 
						|
	"net/url"
 | 
						|
	"os"
 | 
						|
	"strings"
 | 
						|
 | 
						|
	"code.gitea.io/gitea/modules/auth/password/hash"
 | 
						|
	"code.gitea.io/gitea/modules/generate"
 | 
						|
	"code.gitea.io/gitea/modules/log"
 | 
						|
)
 | 
						|
 | 
						|
var (
 | 
						|
	// Security settings
 | 
						|
	InstallLock                        bool
 | 
						|
	SecretKey                          string
 | 
						|
	InternalToken                      string // internal access token
 | 
						|
	LogInRememberDays                  int
 | 
						|
	CookieRememberName                 string
 | 
						|
	ReverseProxyAuthUser               string
 | 
						|
	ReverseProxyAuthEmail              string
 | 
						|
	ReverseProxyAuthFullName           string
 | 
						|
	ReverseProxyLimit                  int
 | 
						|
	ReverseProxyTrustedProxies         []string
 | 
						|
	MinPasswordLength                  int
 | 
						|
	ImportLocalPaths                   bool
 | 
						|
	DisableGitHooks                    bool
 | 
						|
	DisableWebhooks                    bool
 | 
						|
	OnlyAllowPushIfGiteaEnvironmentSet bool
 | 
						|
	PasswordComplexity                 []string
 | 
						|
	PasswordHashAlgo                   string
 | 
						|
	PasswordCheckPwn                   bool
 | 
						|
	SuccessfulTokensCacheSize          int
 | 
						|
	DisableQueryAuthToken              bool
 | 
						|
	CSRFCookieName                     = "_csrf"
 | 
						|
	CSRFCookieHTTPOnly                 = true
 | 
						|
)
 | 
						|
 | 
						|
// loadSecret load the secret from ini by uriKey or verbatimKey, only one of them could be set
 | 
						|
// If the secret is loaded from uriKey (file), the file should be non-empty, to guarantee the behavior stable and clear.
 | 
						|
func loadSecret(sec ConfigSection, uriKey, verbatimKey string) string {
 | 
						|
	// don't allow setting both URI and verbatim string
 | 
						|
	uri := sec.Key(uriKey).String()
 | 
						|
	verbatim := sec.Key(verbatimKey).String()
 | 
						|
	if uri != "" && verbatim != "" {
 | 
						|
		log.Fatal("Cannot specify both %s and %s", uriKey, verbatimKey)
 | 
						|
	}
 | 
						|
 | 
						|
	// if we have no URI, use verbatim
 | 
						|
	if uri == "" {
 | 
						|
		return verbatim
 | 
						|
	}
 | 
						|
 | 
						|
	tempURI, err := url.Parse(uri)
 | 
						|
	if err != nil {
 | 
						|
		log.Fatal("Failed to parse %s (%s): %v", uriKey, uri, err)
 | 
						|
	}
 | 
						|
	switch tempURI.Scheme {
 | 
						|
	case "file":
 | 
						|
		buf, err := os.ReadFile(tempURI.RequestURI())
 | 
						|
		if err != nil {
 | 
						|
			log.Fatal("Failed to read %s (%s): %v", uriKey, tempURI.RequestURI(), err)
 | 
						|
		}
 | 
						|
		val := strings.TrimSpace(string(buf))
 | 
						|
		if val == "" {
 | 
						|
			// The file shouldn't be empty, otherwise we can not know whether the user has ever set the KEY or KEY_URI
 | 
						|
			// For example: if INTERNAL_TOKEN_URI=file:///empty-file,
 | 
						|
			// Then if the token is re-generated during installation and saved to INTERNAL_TOKEN
 | 
						|
			// Then INTERNAL_TOKEN and INTERNAL_TOKEN_URI both exist, that's a fatal error (they shouldn't)
 | 
						|
			log.Fatal("Failed to read %s (%s): the file is empty", uriKey, tempURI.RequestURI())
 | 
						|
		}
 | 
						|
		return val
 | 
						|
 | 
						|
	// only file URIs are allowed
 | 
						|
	default:
 | 
						|
		log.Fatal("Unsupported URI-Scheme %q (%q = %q)", tempURI.Scheme, uriKey, uri)
 | 
						|
		return ""
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
// generateSaveInternalToken generates and saves the internal token to app.ini
 | 
						|
func generateSaveInternalToken(rootCfg ConfigProvider) {
 | 
						|
	token, err := generate.NewInternalToken()
 | 
						|
	if err != nil {
 | 
						|
		log.Fatal("Error generate internal token: %v", err)
 | 
						|
	}
 | 
						|
 | 
						|
	InternalToken = token
 | 
						|
	saveCfg, err := rootCfg.PrepareSaving()
 | 
						|
	if err != nil {
 | 
						|
		log.Fatal("Error saving internal token: %v", err)
 | 
						|
	}
 | 
						|
	rootCfg.Section("security").Key("INTERNAL_TOKEN").SetValue(token)
 | 
						|
	saveCfg.Section("security").Key("INTERNAL_TOKEN").SetValue(token)
 | 
						|
	if err = saveCfg.Save(); err != nil {
 | 
						|
		log.Fatal("Error saving internal token: %v", err)
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
func loadSecurityFrom(rootCfg ConfigProvider) {
 | 
						|
	sec := rootCfg.Section("security")
 | 
						|
	InstallLock = HasInstallLock(rootCfg)
 | 
						|
	LogInRememberDays = sec.Key("LOGIN_REMEMBER_DAYS").MustInt(31)
 | 
						|
	SecretKey = loadSecret(sec, "SECRET_KEY_URI", "SECRET_KEY")
 | 
						|
	if SecretKey == "" {
 | 
						|
		// FIXME: https://github.com/go-gitea/gitea/issues/16832
 | 
						|
		// Until it supports rotating an existing secret key, we shouldn't move users off of the widely used default value
 | 
						|
		SecretKey = "!#@FDEWREWR&*(" //nolint:gosec
 | 
						|
	}
 | 
						|
 | 
						|
	CookieRememberName = sec.Key("COOKIE_REMEMBER_NAME").MustString("gitea_incredible")
 | 
						|
 | 
						|
	ReverseProxyAuthUser = sec.Key("REVERSE_PROXY_AUTHENTICATION_USER").MustString("X-WEBAUTH-USER")
 | 
						|
	ReverseProxyAuthEmail = sec.Key("REVERSE_PROXY_AUTHENTICATION_EMAIL").MustString("X-WEBAUTH-EMAIL")
 | 
						|
	ReverseProxyAuthFullName = sec.Key("REVERSE_PROXY_AUTHENTICATION_FULL_NAME").MustString("X-WEBAUTH-FULLNAME")
 | 
						|
 | 
						|
	ReverseProxyLimit = sec.Key("REVERSE_PROXY_LIMIT").MustInt(1)
 | 
						|
	ReverseProxyTrustedProxies = sec.Key("REVERSE_PROXY_TRUSTED_PROXIES").Strings(",")
 | 
						|
	if len(ReverseProxyTrustedProxies) == 0 {
 | 
						|
		ReverseProxyTrustedProxies = []string{"127.0.0.0/8", "::1/128"}
 | 
						|
	}
 | 
						|
 | 
						|
	MinPasswordLength = sec.Key("MIN_PASSWORD_LENGTH").MustInt(8)
 | 
						|
	ImportLocalPaths = sec.Key("IMPORT_LOCAL_PATHS").MustBool(false)
 | 
						|
	DisableGitHooks = sec.Key("DISABLE_GIT_HOOKS").MustBool(true)
 | 
						|
	DisableWebhooks = sec.Key("DISABLE_WEBHOOKS").MustBool(false)
 | 
						|
	OnlyAllowPushIfGiteaEnvironmentSet = sec.Key("ONLY_ALLOW_PUSH_IF_GITEA_ENVIRONMENT_SET").MustBool(true)
 | 
						|
 | 
						|
	// Ensure that the provided default hash algorithm is a valid hash algorithm
 | 
						|
	var algorithm *hash.PasswordHashAlgorithm
 | 
						|
	PasswordHashAlgo, algorithm = hash.SetDefaultPasswordHashAlgorithm(sec.Key("PASSWORD_HASH_ALGO").MustString(""))
 | 
						|
	if algorithm == nil {
 | 
						|
		log.Fatal("The provided password hash algorithm was invalid: %s", sec.Key("PASSWORD_HASH_ALGO").MustString(""))
 | 
						|
	}
 | 
						|
 | 
						|
	CSRFCookieHTTPOnly = sec.Key("CSRF_COOKIE_HTTP_ONLY").MustBool(true)
 | 
						|
	PasswordCheckPwn = sec.Key("PASSWORD_CHECK_PWN").MustBool(false)
 | 
						|
	SuccessfulTokensCacheSize = sec.Key("SUCCESSFUL_TOKENS_CACHE_SIZE").MustInt(20)
 | 
						|
 | 
						|
	InternalToken = loadSecret(sec, "INTERNAL_TOKEN_URI", "INTERNAL_TOKEN")
 | 
						|
	if InstallLock && InternalToken == "" {
 | 
						|
		// if Gitea has been installed but the InternalToken hasn't been generated (upgrade from an old release), we should generate
 | 
						|
		// some users do cluster deployment, they still depend on this auto-generating behavior.
 | 
						|
		generateSaveInternalToken(rootCfg)
 | 
						|
	}
 | 
						|
 | 
						|
	cfgdata := sec.Key("PASSWORD_COMPLEXITY").Strings(",")
 | 
						|
	if len(cfgdata) == 0 {
 | 
						|
		cfgdata = []string{"off"}
 | 
						|
	}
 | 
						|
	PasswordComplexity = make([]string, 0, len(cfgdata))
 | 
						|
	for _, name := range cfgdata {
 | 
						|
		name := strings.ToLower(strings.Trim(name, `"`))
 | 
						|
		if name != "" {
 | 
						|
			PasswordComplexity = append(PasswordComplexity, name)
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	sectionHasDisableQueryAuthToken := sec.HasKey("DISABLE_QUERY_AUTH_TOKEN")
 | 
						|
 | 
						|
	// TODO: default value should be true in future releases
 | 
						|
	DisableQueryAuthToken = sec.Key("DISABLE_QUERY_AUTH_TOKEN").MustBool(false)
 | 
						|
 | 
						|
	// warn if the setting is set to false explicitly
 | 
						|
	if sectionHasDisableQueryAuthToken && !DisableQueryAuthToken {
 | 
						|
		log.Warn("Enabling Query API Auth tokens is not recommended. DISABLE_QUERY_AUTH_TOKEN will default to true in gitea 1.23 and will be removed in gitea 1.24.")
 | 
						|
	}
 | 
						|
}
 |