mirror of
https://codeberg.org/forgejo/forgejo.git
synced 2024-11-28 20:26:19 +01:00
Always load or generate oauth2 jwt secret (#30942)
Fix #30923
(cherry picked from commit effb405cae88474c27f5c8322a2627019af1cf64)
Signed-off-by: Gergely Nagy <forgejo@gergo.csillger.hu>
Conflicts:
- modules/setting/oauth2.go
Conflicted due to different ways of logging. Since the log
message is removed anyway, resolved by removing it.
- modules/setting/oauth2_test.go
Manually copied the test added by Gitea.
- routers/install/install.go
Not a conflict per se, but adjusted to use NewJwtSecret().
(cherry picked from commit 193ac67176
)
Equivalent to the Gitea v1.22 commit
(cherry picked from commit 5b7e54f72f7b85b3394d7af20b27152d26e26256)
This commit is contained in:
parent
391e311bbe
commit
37228ea080
3 changed files with 44 additions and 12 deletions
|
@ -124,16 +124,15 @@ func loadOAuth2From(rootCfg ConfigProvider) {
|
|||
OAuth2.Enabled = sec.Key("ENABLE").MustBool(OAuth2.Enabled)
|
||||
}
|
||||
|
||||
if !OAuth2.Enabled {
|
||||
return
|
||||
}
|
||||
|
||||
jwtSecretBase64 := loadSecret(sec, "JWT_SECRET_URI", "JWT_SECRET")
|
||||
|
||||
if !filepath.IsAbs(OAuth2.JWTSigningPrivateKeyFile) {
|
||||
OAuth2.JWTSigningPrivateKeyFile = filepath.Join(AppDataPath, OAuth2.JWTSigningPrivateKeyFile)
|
||||
}
|
||||
|
||||
// FIXME: at the moment, no matter oauth2 is enabled or not, it must generate a "oauth2 JWT_SECRET"
|
||||
// Because this secret is also used as GeneralTokenSigningSecret (as a quick not-that-breaking fix for some legacy problems).
|
||||
// Including: CSRF token, account validation token, etc ...
|
||||
// In main branch, the signing token should be refactored (eg: one unique for LFS/OAuth2/etc ...)
|
||||
jwtSecretBase64 := loadSecret(sec, "JWT_SECRET_URI", "JWT_SECRET")
|
||||
if InstallLock {
|
||||
jwtSecretBytes, err := generate.DecodeJwtSecret(jwtSecretBase64)
|
||||
if err != nil {
|
||||
|
@ -155,8 +154,6 @@ func loadOAuth2From(rootCfg ConfigProvider) {
|
|||
}
|
||||
}
|
||||
|
||||
// generalSigningSecret is used as container for a []byte value
|
||||
// instead of an additional mutex, we use CompareAndSwap func to change the value thread save
|
||||
var generalSigningSecret atomic.Pointer[[]byte]
|
||||
|
||||
func GetGeneralTokenSigningSecret() []byte {
|
||||
|
@ -164,11 +161,9 @@ func GetGeneralTokenSigningSecret() []byte {
|
|||
if old == nil || len(*old) == 0 {
|
||||
jwtSecret, _, err := generate.NewJwtSecret()
|
||||
if err != nil {
|
||||
log.Fatal("Unable to generate general JWT secret: %s", err.Error())
|
||||
log.Fatal("Unable to generate general JWT secret: %v", err)
|
||||
}
|
||||
if generalSigningSecret.CompareAndSwap(old, &jwtSecret) {
|
||||
// FIXME: in main branch, the signing token should be refactored (eg: one unique for LFS/OAuth2/etc ...)
|
||||
log.Warn("OAuth2 is not enabled, unable to use a persistent signing secret, a new one is generated, which is not persistent between restarts and cluster nodes")
|
||||
return jwtSecret
|
||||
}
|
||||
return *generalSigningSecret.Load()
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
package setting
|
||||
|
||||
import (
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"code.gitea.io/gitea/modules/generate"
|
||||
|
@ -14,7 +15,7 @@ import (
|
|||
|
||||
func TestGetGeneralSigningSecret(t *testing.T) {
|
||||
// when there is no general signing secret, it should be generated, and keep the same value
|
||||
assert.Nil(t, generalSigningSecret.Load())
|
||||
generalSigningSecret.Store(nil)
|
||||
s1 := GetGeneralTokenSigningSecret()
|
||||
assert.NotNil(t, s1)
|
||||
s2 := GetGeneralTokenSigningSecret()
|
||||
|
@ -32,3 +33,28 @@ JWT_SECRET = BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB
|
|||
assert.Len(t, actual, 32)
|
||||
assert.EqualValues(t, expected, actual)
|
||||
}
|
||||
|
||||
func TestGetGeneralSigningSecretSave(t *testing.T) {
|
||||
defer test.MockVariableValue(&InstallLock, true)()
|
||||
|
||||
old := GetGeneralTokenSigningSecret()
|
||||
assert.Len(t, old, 32)
|
||||
|
||||
tmpFile := t.TempDir() + "/app.ini"
|
||||
_ = os.WriteFile(tmpFile, nil, 0o644)
|
||||
cfg, _ := NewConfigProviderFromFile(tmpFile)
|
||||
loadOAuth2From(cfg)
|
||||
generated := GetGeneralTokenSigningSecret()
|
||||
assert.Len(t, generated, 32)
|
||||
assert.NotEqual(t, old, generated)
|
||||
|
||||
generalSigningSecret.Store(nil)
|
||||
cfg, _ = NewConfigProviderFromFile(tmpFile)
|
||||
loadOAuth2From(cfg)
|
||||
again := GetGeneralTokenSigningSecret()
|
||||
assert.Equal(t, generated, again)
|
||||
|
||||
iniContent, err := os.ReadFile(tmpFile)
|
||||
assert.NoError(t, err)
|
||||
assert.Contains(t, string(iniContent), "JWT_SECRET = ")
|
||||
}
|
||||
|
|
|
@ -486,6 +486,17 @@ func SubmitInstall(ctx *context.Context) {
|
|||
cfg.Section("security").Key("INTERNAL_TOKEN").SetValue(internalToken)
|
||||
}
|
||||
|
||||
// FIXME: at the moment, no matter oauth2 is enabled or not, it must generate a "oauth2 JWT_SECRET"
|
||||
// see the "loadOAuth2From" in "setting/oauth2.go"
|
||||
if !cfg.Section("oauth2").HasKey("JWT_SECRET") && !cfg.Section("oauth2").HasKey("JWT_SECRET_URI") {
|
||||
_, jwtSecretBase64, err := generate.NewJwtSecret()
|
||||
if err != nil {
|
||||
ctx.RenderWithErr(ctx.Tr("install.secret_key_failed", err), tplInstall, &form)
|
||||
return
|
||||
}
|
||||
cfg.Section("oauth2").Key("JWT_SECRET").SetValue(jwtSecretBase64)
|
||||
}
|
||||
|
||||
// if there is already a SECRET_KEY, we should not overwrite it, otherwise the encrypted data will not be able to be decrypted
|
||||
if setting.SecretKey == "" {
|
||||
var secretKey string
|
||||
|
|
Loading…
Reference in a new issue