From 001ebb8e39417d4296e0134374e437cbb332587a Mon Sep 17 00:00:00 2001 From: danez Date: Thu, 18 Dec 2025 20:55:56 +0100 Subject: [PATCH] feat: Add support for loading db password from file via PASSD_URI (#10421) Adds a new PASSWD_URI ini setting similar to all the other secrets Fixes #9365 Fixes #6530 Co-authored-by: Daniel Tschinder <231804+danez@users.noreply.github.com> Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/10421 Reviewed-by: Gusted Co-authored-by: danez Co-committed-by: danez --- custom/conf/app.example.ini | 4 +++ modules/setting/database.go | 2 +- modules/setting/database_test.go | 52 ++++++++++++++++++++++++++++++++ 3 files changed, 57 insertions(+), 1 deletion(-) diff --git a/custom/conf/app.example.ini b/custom/conf/app.example.ini index 792c3bc5e3..a432a142c3 100644 --- a/custom/conf/app.example.ini +++ b/custom/conf/app.example.ini @@ -373,6 +373,8 @@ DB_TYPE = sqlite3 ;NAME = gitea ;USER = root ;PASSWD = ;Use PASSWD = `your password` for quoting if you use special characters in the password. +;; Alternative location to specify mysql password. You cannot specify both this and PASSWD, and must pick one +;PASSWD_URI = file:/etc/forgejo/db_passwd ;SSL_MODE = false ; either "false" (default), "true", or "skip-verify" ;CHARSET_COLLATION = ; Empty as default, Forgejo will try to find a case-sensitive collation. Don't change it unless you clearly know what you need. ;; @@ -385,6 +387,8 @@ DB_TYPE = sqlite3 ;NAME = gitea ;USER = root ;PASSWD = +;; Alternative location to specify postgres password. You cannot specify both this and PASSWD, and must pick one +;PASSWD_URI = file:/etc/forgejo/db_passwd ;SCHEMA = ;SSL_MODE=disable ;either "disable" (default), "require", or "verify-full" ;; diff --git a/modules/setting/database.go b/modules/setting/database.go index bbea81352b..91b3c90747 100644 --- a/modules/setting/database.go +++ b/modules/setting/database.go @@ -79,7 +79,7 @@ func loadDBSetting(rootCfg ConfigProvider) { Database.Name = sec.Key("NAME").String() Database.User = sec.Key("USER").String() if len(Database.Passwd) == 0 { - Database.Passwd = sec.Key("PASSWD").String() + Database.Passwd = loadSecret(sec, "PASSWD_URI", "PASSWD") } Database.Schema = sec.Key("SCHEMA").String() Database.SSLMode = sec.Key("SSL_MODE").MustString("disable") diff --git a/modules/setting/database_test.go b/modules/setting/database_test.go index fb3c884690..5e320be41e 100644 --- a/modules/setting/database_test.go +++ b/modules/setting/database_test.go @@ -4,10 +4,16 @@ package setting import ( + "fmt" + "os" + "path/filepath" "strings" "testing" + "forgejo.org/modules/test" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ) func Test_parsePostgreSQLHostPort(t *testing.T) { @@ -253,3 +259,49 @@ func Test_getPostgreSQLEngineGroupConnectionStrings(t *testing.T) { assert.Equal(t, test.outputReplicas, replicas) } } + +func Test_loadDBSetting(t *testing.T) { + defer test.MockProtect(&Database)() + t.Run("Does not overwrite Passwd", func(t *testing.T) { + expectedPassword := "already_set" + + cfg, _ := NewConfigProviderFromData(` + [database] + PASSWD="new password" + `) + + Database.Passwd = expectedPassword + loadDBSetting(cfg) + + assert.Equal(t, expectedPassword, Database.Passwd) + }) + t.Run("uses PASSWD", func(t *testing.T) { + expectedPassword := "testpassword" + + cfg, _ := NewConfigProviderFromData(fmt.Sprintf(` + [database] + PASSWD="%s" + `, expectedPassword)) + + Database.Passwd = "" + loadDBSetting(cfg) + + assert.Equal(t, expectedPassword, Database.Passwd) + }) + t.Run("Uses PASSWD_URI", func(t *testing.T) { + expectedPassword := "testpassworduri" + + uri := filepath.Join(t.TempDir(), "db_passwd") + require.NoError(t, os.WriteFile(uri, []byte(expectedPassword), 0o644)) + + cfg, _ := NewConfigProviderFromData(fmt.Sprintf(` + [database] + PASSWD_URI="file:%s" + `, uri)) + + Database.Passwd = "" + loadDBSetting(cfg) + + assert.Equal(t, expectedPassword, Database.Passwd) + }) +}