123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171 |
- // 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.")
- }
- }
|