From 620410c48023909753448be6c770a5c52af8d934 Mon Sep 17 00:00:00 2001 From: ltdk Date: Mon, 9 May 2022 13:10:16 -0400 Subject: [PATCH 1/6] Allow specifying SECRET_KEY_URI, similar to INTERNAL_TOKEN_URI This modifies the existing internal token loading and the new secret key loading such that: * Specifying both INTERNAL_TOKEN and INTERNAL_TOKEN_URI is an error, instead of just preferring the URI * The install lock isn't required to regenerate the internal token or the secret key; it will always be regenerated if it's absent --- custom/conf/app.example.ini | 7 ++- modules/setting/setting.go | 89 ++++++++++++++++++++++++------------- 2 files changed, 64 insertions(+), 32 deletions(-) diff --git a/custom/conf/app.example.ini b/custom/conf/app.example.ini index 1561a53da0f07..98a9eeaa165d7 100644 --- a/custom/conf/app.example.ini +++ b/custom/conf/app.example.ini @@ -385,8 +385,11 @@ SECRET_KEY = ;; Secret used to validate communication within Gitea binary. INTERNAL_TOKEN= ;; -;; Instead of defining internal token in the configuration, this configuration option can be used to give Gitea a path to a file that contains the internal token (example value: file:/etc/gitea/internal_token) -;INTERNAL_TOKEN_URI = ;e.g. /etc/gitea/internal_token +;; Alternative location to specify secret key, instead of this file; you cannot specify both this and SECRET_KEY, and must pick one +;SECRET_KEY_URI = file:/etc/gitea/secret_key +;; +;; Alternative location to specify internal token, instead of this file; you cannot specify both this and INTERNAL_TOKEN, and must pick one +;INTERNAL_TOKEN_URI = file:/etc/gitea/internal_token ;; ;; How long to remember that a user is logged in before requiring relogin (in days) ;LOGIN_REMEMBER_DAYS = 7 diff --git a/modules/setting/setting.go b/modules/setting/setting.go index 09e510ffa01b4..79dc8ef5370e0 100644 --- a/modules/setting/setting.go +++ b/modules/setting/setting.go @@ -921,9 +921,16 @@ func loadFromConf(allowEmpty bool, extraConfig string) { sec = Cfg.Section("security") InstallLock = sec.Key("INSTALL_LOCK").MustBool(false) - SecretKey = sec.Key("SECRET_KEY").MustString("!#@FDEWREWR&*(") LogInRememberDays = sec.Key("LOGIN_REMEMBER_DAYS").MustInt(7) CookieUserName = sec.Key("COOKIE_USERNAME").MustString("gitea_awesome") + SecretKey = loadSecret(sec, "SECRET_KEY_URI", "SECRET_KEY", func() (string, error) { + // FIXME: https://github.com/go-gitea/gitea/issues/16832 + // + // Until we properly support rotating an existing secret key, + // we shouldn't move users off of the default value + return "!#@FDEWREWR&*(", nil + }) + CookieRememberName = sec.Key("COOKIE_REMEMBER_NAME").MustString("gitea_incredible") ReverseProxyAuthUser = sec.Key("REVERSE_PROXY_AUTHENTICATION_USER").MustString("X-WEBAUTH-USER") @@ -946,11 +953,7 @@ func loadFromConf(allowEmpty bool, extraConfig string) { PasswordCheckPwn = sec.Key("PASSWORD_CHECK_PWN").MustBool(false) SuccessfulTokensCacheSize = sec.Key("SUCCESSFUL_TOKENS_CACHE_SIZE").MustInt(20) - InternalToken = loadInternalToken(sec) - if InstallLock && InternalToken == "" { - // if Gitea has been installed but the InternalToken hasn't been generated (upgrade from an old release), we should generate - generateSaveInternalToken() - } + InternalToken = loadSecret(sec, "INTERNAL_TOKEN_URI", "INTERNAL_TOKEN", generate.NewInternalToken) cfgdata := sec.Key("PASSWORD_COMPLEXITY").Strings(",") if len(cfgdata) == 0 { @@ -1139,51 +1142,73 @@ func parseAuthorizedPrincipalsAllow(values []string) ([]string, bool) { return authorizedPrincipalsAllow, true } -func loadInternalToken(sec *ini.Section) string { - uri := sec.Key("INTERNAL_TOKEN_URI").String() +func loadSecret( + sec *ini.Section, + uriKey string, + verbatimKey string, + generator func() (string, error), +) 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 sec.Key("INTERNAL_TOKEN").String() + // if verbatim isn't provided, generate one + if verbatim == "" { + secret, err := generator() + if err != nil { + log.Fatal("Error trying to generate %s: %v", verbatimKey, err) + } + CreateOrAppendToCustomConf(func(cfg *ini.File) { + cfg.Section(sec.Name()).Key(verbatimKey).SetValue(secret) + }) + return secret + } + + return verbatim } + tempURI, err := url.Parse(uri) if err != nil { - log.Fatal("Failed to parse INTERNAL_TOKEN_URI (%s): %v", uri, err) + 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 && !os.IsNotExist(err) { - log.Fatal("Failed to open InternalTokenURI (%s): %v", uri, err) + log.Fatal("Failed to open %s (%s): %v", uriKey, uri, err) } - // No token in the file, generate one and store it. + + // empty file; generate secret and store it if len(buf) == 0 { - token, err := generate.NewInternalToken() + token, err := generator() if err != nil { - log.Fatal("Error generate internal token: %v", err) + log.Fatal("Error generating %s: %v", verbatimKey, err) } + err = os.WriteFile(tempURI.RequestURI(), []byte(token), 0o600) if err != nil { - log.Fatal("Error writing to InternalTokenURI (%s): %v", uri, err) + log.Fatal("Error writing to %s (%s): %v", uriKey, uri, err) } + + // we assume generator gives pre-parsed token return token } + return strings.TrimSpace(string(buf)) + + // only file URIs are allowed default: log.Fatal("Unsupported URI-Scheme %q (INTERNAL_TOKEN_URI = %q)", tempURI.Scheme, uri) } - return "" -} -// generateSaveInternalToken generates and saves the internal token to app.ini -func generateSaveInternalToken() { - token, err := generate.NewInternalToken() - if err != nil { - log.Fatal("Error generate internal token: %v", err) - } - - InternalToken = token - CreateOrAppendToCustomConf(func(cfg *ini.File) { - cfg.Section("security").Key("INTERNAL_TOKEN").SetValue(token) - }) + // we should never get here + log.Fatal("Unknown error when loading %s", verbatimKey) + return "" } // MakeAbsoluteAssetURL returns the absolute asset url prefix without a trailing slash @@ -1248,6 +1273,11 @@ func MakeManifestData(appName, appURL, absoluteAssetURL string) []byte { // CreateOrAppendToCustomConf creates or updates the custom config. // Use the callback to set individual values. func CreateOrAppendToCustomConf(callback func(cfg *ini.File)) { + if CustomConf == "" { + log.Error("Custom config path must not be empty") + return + } + cfg := ini.Empty() isFile, err := util.IsFile(CustomConf) if err != nil { @@ -1262,8 +1292,6 @@ func CreateOrAppendToCustomConf(callback func(cfg *ini.File)) { callback(cfg) - log.Info("Settings saved to: %q", CustomConf) - if err := os.MkdirAll(filepath.Dir(CustomConf), os.ModePerm); err != nil { log.Fatal("failed to create '%s': %v", CustomConf, err) return @@ -1271,6 +1299,7 @@ func CreateOrAppendToCustomConf(callback func(cfg *ini.File)) { if err := cfg.SaveTo(CustomConf); err != nil { log.Fatal("error saving to custom config: %v", err) } + log.Info("Settings saved to: %q", CustomConf) // Change permissions to be more restrictive fi, err := os.Stat(CustomConf) From 5cbdab7b28789ecd117172eb215c3a445116e2cb Mon Sep 17 00:00:00 2001 From: wxiaoguang Date: Wed, 28 Sep 2022 20:48:32 +0800 Subject: [PATCH 2/6] make log clear when setting.CreateOrAppendToCustomConf --- cmd/web.go | 2 +- modules/setting/lfs.go | 2 +- modules/setting/setting.go | 6 +++--- services/auth/source/oauth2/jwtsigningkey.go | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/cmd/web.go b/cmd/web.go index e09560bb86cfe..1b9f7e420a916 100644 --- a/cmd/web.go +++ b/cmd/web.go @@ -203,7 +203,7 @@ func setPort(port string) error { defaultLocalURL += ":" + setting.HTTPPort + "/" // Save LOCAL_ROOT_URL if port changed - setting.CreateOrAppendToCustomConf(func(cfg *ini.File) { + setting.CreateOrAppendToCustomConf("server.LOCAL_ROOT_URL", func(cfg *ini.File) { cfg.Section("server").Key("LOCAL_ROOT_URL").SetValue(defaultLocalURL) }) } diff --git a/modules/setting/lfs.go b/modules/setting/lfs.go index 3179a67ce745a..686b043657422 100644 --- a/modules/setting/lfs.go +++ b/modules/setting/lfs.go @@ -62,7 +62,7 @@ func newLFSService() { } // Save secret - CreateOrAppendToCustomConf(func(cfg *ini.File) { + CreateOrAppendToCustomConf("server.LFS_JWT_SECRET", func(cfg *ini.File) { cfg.Section("server").Key("LFS_JWT_SECRET").SetValue(LFS.JWTSecretBase64) }) } diff --git a/modules/setting/setting.go b/modules/setting/setting.go index 79dc8ef5370e0..83dd76d777239 100644 --- a/modules/setting/setting.go +++ b/modules/setting/setting.go @@ -1163,7 +1163,7 @@ func loadSecret( if err != nil { log.Fatal("Error trying to generate %s: %v", verbatimKey, err) } - CreateOrAppendToCustomConf(func(cfg *ini.File) { + CreateOrAppendToCustomConf(sec.Name()+"."+verbatimKey, func(cfg *ini.File) { cfg.Section(sec.Name()).Key(verbatimKey).SetValue(secret) }) return secret @@ -1272,7 +1272,7 @@ func MakeManifestData(appName, appURL, absoluteAssetURL string) []byte { // CreateOrAppendToCustomConf creates or updates the custom config. // Use the callback to set individual values. -func CreateOrAppendToCustomConf(callback func(cfg *ini.File)) { +func CreateOrAppendToCustomConf(purpose string, callback func(cfg *ini.File)) { if CustomConf == "" { log.Error("Custom config path must not be empty") return @@ -1299,7 +1299,7 @@ func CreateOrAppendToCustomConf(callback func(cfg *ini.File)) { if err := cfg.SaveTo(CustomConf); err != nil { log.Fatal("error saving to custom config: %v", err) } - log.Info("Settings saved to: %q", CustomConf) + log.Info("Settings for %s saved to: %q", purpose, CustomConf) // Change permissions to be more restrictive fi, err := os.Stat(CustomConf) diff --git a/services/auth/source/oauth2/jwtsigningkey.go b/services/auth/source/oauth2/jwtsigningkey.go index d6b3c05a4fcbf..d9312ee820bd8 100644 --- a/services/auth/source/oauth2/jwtsigningkey.go +++ b/services/auth/source/oauth2/jwtsigningkey.go @@ -364,7 +364,7 @@ func loadOrCreateSymmetricKey() (interface{}, error) { return nil, err } - setting.CreateOrAppendToCustomConf(func(cfg *ini.File) { + setting.CreateOrAppendToCustomConf("oauth2.JWT_SECRET", func(cfg *ini.File) { secretBase64 := base64.RawURLEncoding.EncodeToString(key) cfg.Section("oauth2").Key("JWT_SECRET").SetValue(secretBase64) }) From 1119fee762841d2e562087fcd922d2961769a5d6 Mon Sep 17 00:00:00 2001 From: wxiaoguang Date: Wed, 28 Sep 2022 20:55:10 +0800 Subject: [PATCH 3/6] rename loadSecret to loadOrGenerateSecret and add more comments --- modules/setting/setting.go | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/modules/setting/setting.go b/modules/setting/setting.go index 023f1dbcb1753..4e28beb8daa39 100644 --- a/modules/setting/setting.go +++ b/modules/setting/setting.go @@ -925,11 +925,9 @@ func loadFromConf(allowEmpty bool, extraConfig string) { InstallLock = sec.Key("INSTALL_LOCK").MustBool(false) LogInRememberDays = sec.Key("LOGIN_REMEMBER_DAYS").MustInt(7) CookieUserName = sec.Key("COOKIE_USERNAME").MustString("gitea_awesome") - SecretKey = loadSecret(sec, "SECRET_KEY_URI", "SECRET_KEY", func() (string, error) { + SecretKey = loadOrGenerateSecret(sec, "SECRET_KEY_URI", "SECRET_KEY", func() (string, error) { // FIXME: https://github.com/go-gitea/gitea/issues/16832 - // - // Until we properly support rotating an existing secret key, - // we shouldn't move users off of the default value + // Until it supports rotating an existing secret key, we shouldn't move users off of the widely used default value return "!#@FDEWREWR&*(", nil }) @@ -955,7 +953,7 @@ func loadFromConf(allowEmpty bool, extraConfig string) { 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", generate.NewInternalToken) + InternalToken = loadOrGenerateSecret(sec, "INTERNAL_TOKEN_URI", "INTERNAL_TOKEN", generate.NewInternalToken) cfgdata := sec.Key("PASSWORD_COMPLEXITY").Strings(",") if len(cfgdata) == 0 { @@ -1144,7 +1142,9 @@ func parseAuthorizedPrincipalsAllow(values []string) ([]string, bool) { return authorizedPrincipalsAllow, true } -func loadSecret( +// loadOrGenerateSecret loads the secret if it exists in the config file, +// or generates a new one and saves it into the config file +func loadOrGenerateSecret( sec *ini.Section, uriKey string, verbatimKey string, From 7b5140c24797182f0e8732e56538acdd937b1255 Mon Sep 17 00:00:00 2001 From: wxiaoguang Date: Wed, 28 Sep 2022 21:00:30 +0800 Subject: [PATCH 4/6] do not write the default secret to config (keep the old behavior) --- modules/setting/setting.go | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/modules/setting/setting.go b/modules/setting/setting.go index 4e28beb8daa39..3ba8ec99f8475 100644 --- a/modules/setting/setting.go +++ b/modules/setting/setting.go @@ -925,11 +925,12 @@ func loadFromConf(allowEmpty bool, extraConfig string) { InstallLock = sec.Key("INSTALL_LOCK").MustBool(false) LogInRememberDays = sec.Key("LOGIN_REMEMBER_DAYS").MustInt(7) CookieUserName = sec.Key("COOKIE_USERNAME").MustString("gitea_awesome") - SecretKey = loadOrGenerateSecret(sec, "SECRET_KEY_URI", "SECRET_KEY", func() (string, error) { + SecretKey = loadOrGenerateSecret(sec, "SECRET_KEY_URI", "SECRET_KEY", nil) + 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 - return "!#@FDEWREWR&*(", nil - }) + SecretKey = "!#@FDEWREWR&*(" // nolint:gosec + } CookieRememberName = sec.Key("COOKIE_REMEMBER_NAME").MustString("gitea_incredible") @@ -1160,7 +1161,7 @@ func loadOrGenerateSecret( // if we have no URI, use verbatim if uri == "" { // if verbatim isn't provided, generate one - if verbatim == "" { + if verbatim == "" && generator != nil { secret, err := generator() if err != nil { log.Fatal("Error trying to generate %s: %v", verbatimKey, err) @@ -1186,7 +1187,7 @@ func loadOrGenerateSecret( } // empty file; generate secret and store it - if len(buf) == 0 { + if len(buf) == 0 && generator != nil { token, err := generator() if err != nil { log.Fatal("Error generating %s: %v", verbatimKey, err) From c2f0a01877c4a7e6ec024d84e1d21097eb90099c Mon Sep 17 00:00:00 2001 From: wxiaoguang Date: Wed, 28 Sep 2022 21:53:31 +0800 Subject: [PATCH 5/6] fix document --- custom/conf/app.example.ini | 10 ++++++---- docs/content/doc/advanced/config-cheat-sheet.en-us.md | 5 +++-- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/custom/conf/app.example.ini b/custom/conf/app.example.ini index 90841694e1ae1..3759428ed5cef 100644 --- a/custom/conf/app.example.ini +++ b/custom/conf/app.example.ini @@ -379,15 +379,17 @@ LOG_SQL = false ; if unset defaults to true ;; Whether the installer is disabled (set to true to disable the installer) INSTALL_LOCK = false ;; -;; Global secret key that will be used - if blank will be regenerated. +;; Global secret key that will be used +;; This key is VERY IMPORTANT. If you lose it, the data encrypted by it (like 2FA secret) can't be decrypted anymore. SECRET_KEY = ;; -;; Secret used to validate communication within Gitea binary. -INTERNAL_TOKEN= -;; ;; Alternative location to specify secret key, instead of this file; you cannot specify both this and SECRET_KEY, and must pick one +;; This key is VERY IMPORTANT. If you lose it, the data encrypted by it (like 2FA secret) can't be decrypted anymore. ;SECRET_KEY_URI = file:/etc/gitea/secret_key ;; +;; Secret used to validate communication within Gitea binary. +INTERNAL_TOKEN= +;; ;; Alternative location to specify internal token, instead of this file; you cannot specify both this and INTERNAL_TOKEN, and must pick one ;INTERNAL_TOKEN_URI = file:/etc/gitea/internal_token ;; diff --git a/docs/content/doc/advanced/config-cheat-sheet.en-us.md b/docs/content/doc/advanced/config-cheat-sheet.en-us.md index ef64c57246155..a93234e28acd5 100644 --- a/docs/content/doc/advanced/config-cheat-sheet.en-us.md +++ b/docs/content/doc/advanced/config-cheat-sheet.en-us.md @@ -494,7 +494,8 @@ Certain queues have defaults that override the defaults set in `[queue]` (this o ## Security (`security`) - `INSTALL_LOCK`: **false**: Controls access to the installation page. When set to "true", the installation page is not accessible. -- `SECRET_KEY`: **\**: Global secret key. This should be changed. +- `SECRET_KEY`: **\**: Global secret key. This key is VERY IMPORTANT, if you lost it, the data encrypted by it (like 2FA secret) can't be decrypted anymore. +- `SECRET_KEY_URI`: ****: Instead of defining SECRET_KEY, this option can be used to use the key stored in a file (example value: `file:/etc/gitea/secret_token`). It shouldn't be lost like SECRET_KEY. - `LOGIN_REMEMBER_DAYS`: **7**: Cookie lifetime, in days. - `COOKIE_USERNAME`: **gitea\_awesome**: Name of the cookie used to store the current username. - `COOKIE_REMEMBER_NAME`: **gitea\_incredible**: Name of cookie used to store authentication @@ -520,7 +521,7 @@ Certain queues have defaults that override the defaults set in `[queue]` (this o - `ONLY_ALLOW_PUSH_IF_GITEA_ENVIRONMENT_SET`: **true**: Set to `false` to allow local users to push to gitea-repositories without setting up the Gitea environment. This is not recommended and if you want local users to push to Gitea repositories you should set the environment appropriately. - `IMPORT_LOCAL_PATHS`: **false**: Set to `false` to prevent all users (including admin) from importing local path on server. - `INTERNAL_TOKEN`: **\**: Secret used to validate communication within Gitea binary. -- `INTERNAL_TOKEN_URI`: ****: Instead of defining internal token in the configuration, this configuration option can be used to give Gitea a path to a file that contains the internal token (example value: `file:/etc/gitea/internal_token`) +- `INTERNAL_TOKEN_URI`: ****: Instead of defining INTERNAL_TOKEN in the configuration, this configuration option can be used to give Gitea a path to a file that contains the internal token (example value: `file:/etc/gitea/internal_token`) - `PASSWORD_HASH_ALGO`: **pbkdf2**: The hash algorithm to use \[argon2, pbkdf2, scrypt, bcrypt\], argon2 will spend more memory than others. - `CSRF_COOKIE_HTTP_ONLY`: **true**: Set false to allow JavaScript to read CSRF cookie. - `MIN_PASSWORD_LENGTH`: **6**: Minimum password length for new users. From 0c237716e59dc161c738fffdef82dea8fd152d2f Mon Sep 17 00:00:00 2001 From: wxiaoguang Date: Thu, 29 Sep 2022 10:32:31 +0800 Subject: [PATCH 6/6] avoid writing incorrect config files --- .../doc/advanced/config-cheat-sheet.en-us.md | 2 +- modules/setting/setting.go | 52 +++---------------- routers/private/internal.go | 5 ++ 3 files changed, 12 insertions(+), 47 deletions(-) diff --git a/docs/content/doc/advanced/config-cheat-sheet.en-us.md b/docs/content/doc/advanced/config-cheat-sheet.en-us.md index a93234e28acd5..50571bd4433fb 100644 --- a/docs/content/doc/advanced/config-cheat-sheet.en-us.md +++ b/docs/content/doc/advanced/config-cheat-sheet.en-us.md @@ -495,7 +495,7 @@ Certain queues have defaults that override the defaults set in `[queue]` (this o - `INSTALL_LOCK`: **false**: Controls access to the installation page. When set to "true", the installation page is not accessible. - `SECRET_KEY`: **\**: Global secret key. This key is VERY IMPORTANT, if you lost it, the data encrypted by it (like 2FA secret) can't be decrypted anymore. -- `SECRET_KEY_URI`: ****: Instead of defining SECRET_KEY, this option can be used to use the key stored in a file (example value: `file:/etc/gitea/secret_token`). It shouldn't be lost like SECRET_KEY. +- `SECRET_KEY_URI`: ****: Instead of defining SECRET_KEY, this option can be used to use the key stored in a file (example value: `file:/etc/gitea/secret_key`). It shouldn't be lost like SECRET_KEY. - `LOGIN_REMEMBER_DAYS`: **7**: Cookie lifetime, in days. - `COOKIE_USERNAME`: **gitea\_awesome**: Name of the cookie used to store the current username. - `COOKIE_REMEMBER_NAME`: **gitea\_incredible**: Name of cookie used to store authentication diff --git a/modules/setting/setting.go b/modules/setting/setting.go index 3ba8ec99f8475..6233437bf5aac 100644 --- a/modules/setting/setting.go +++ b/modules/setting/setting.go @@ -21,7 +21,6 @@ import ( "text/template" "time" - "code.gitea.io/gitea/modules/generate" "code.gitea.io/gitea/modules/json" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/user" @@ -925,7 +924,7 @@ func loadFromConf(allowEmpty bool, extraConfig string) { InstallLock = sec.Key("INSTALL_LOCK").MustBool(false) LogInRememberDays = sec.Key("LOGIN_REMEMBER_DAYS").MustInt(7) CookieUserName = sec.Key("COOKIE_USERNAME").MustString("gitea_awesome") - SecretKey = loadOrGenerateSecret(sec, "SECRET_KEY_URI", "SECRET_KEY", nil) + 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 @@ -954,7 +953,7 @@ func loadFromConf(allowEmpty bool, extraConfig string) { PasswordCheckPwn = sec.Key("PASSWORD_CHECK_PWN").MustBool(false) SuccessfulTokensCacheSize = sec.Key("SUCCESSFUL_TOKENS_CACHE_SIZE").MustInt(20) - InternalToken = loadOrGenerateSecret(sec, "INTERNAL_TOKEN_URI", "INTERNAL_TOKEN", generate.NewInternalToken) + InternalToken = loadSecret(sec, "INTERNAL_TOKEN_URI", "INTERNAL_TOKEN") cfgdata := sec.Key("PASSWORD_COMPLEXITY").Strings(",") if len(cfgdata) == 0 { @@ -1143,14 +1142,7 @@ func parseAuthorizedPrincipalsAllow(values []string) ([]string, bool) { return authorizedPrincipalsAllow, true } -// loadOrGenerateSecret loads the secret if it exists in the config file, -// or generates a new one and saves it into the config file -func loadOrGenerateSecret( - sec *ini.Section, - uriKey string, - verbatimKey string, - generator func() (string, error), -) string { +func loadSecret(sec *ini.Section, uriKey, verbatimKey string) string { // don't allow setting both URI and verbatim string uri := sec.Key(uriKey).String() verbatim := sec.Key(verbatimKey).String() @@ -1160,18 +1152,6 @@ func loadOrGenerateSecret( // if we have no URI, use verbatim if uri == "" { - // if verbatim isn't provided, generate one - if verbatim == "" && generator != nil { - secret, err := generator() - if err != nil { - log.Fatal("Error trying to generate %s: %v", verbatimKey, err) - } - CreateOrAppendToCustomConf(sec.Name()+"."+verbatimKey, func(cfg *ini.File) { - cfg.Section(sec.Name()).Key(verbatimKey).SetValue(secret) - }) - return secret - } - return verbatim } @@ -1182,36 +1162,16 @@ func loadOrGenerateSecret( switch tempURI.Scheme { case "file": buf, err := os.ReadFile(tempURI.RequestURI()) - if err != nil && !os.IsNotExist(err) { - log.Fatal("Failed to open %s (%s): %v", uriKey, uri, err) - } - - // empty file; generate secret and store it - if len(buf) == 0 && generator != nil { - token, err := generator() - if err != nil { - log.Fatal("Error generating %s: %v", verbatimKey, err) - } - - err = os.WriteFile(tempURI.RequestURI(), []byte(token), 0o600) - if err != nil { - log.Fatal("Error writing to %s (%s): %v", uriKey, uri, err) - } - - // we assume generator gives pre-parsed token - return token + if err != nil { + log.Fatal("Failed to read %s (%s): %v", uriKey, tempURI.RequestURI(), err) } - return strings.TrimSpace(string(buf)) // only file URIs are allowed default: log.Fatal("Unsupported URI-Scheme %q (INTERNAL_TOKEN_URI = %q)", tempURI.Scheme, uri) + return "" } - - // we should never get here - log.Fatal("Unknown error when loading %s", verbatimKey) - return "" } // MakeAbsoluteAssetURL returns the absolute asset url prefix without a trailing slash diff --git a/routers/private/internal.go b/routers/private/internal.go index 061c7f3c822af..e9cc20a77dc6d 100644 --- a/routers/private/internal.go +++ b/routers/private/internal.go @@ -24,6 +24,11 @@ func CheckInternalToken(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { tokens := req.Header.Get("Authorization") fields := strings.SplitN(tokens, " ", 2) + if setting.InternalToken == "" { + log.Warn(`The INTERNAL_TOKEN setting is missing from the configuration file: %q, internal API can't work.`, setting.CustomConf) + http.Error(w, http.StatusText(http.StatusForbidden), http.StatusForbidden) + return + } if len(fields) != 2 || fields[0] != "Bearer" || fields[1] != setting.InternalToken { log.Debug("Forbidden attempt to access internal url: Authorization header: %s", tokens) http.Error(w, http.StatusText(http.StatusForbidden), http.StatusForbidden)