From 5142ce196e3114c0902c6e8dfa1693f472809b6e Mon Sep 17 00:00:00 2001 From: Andrew Thornton Date: Sat, 4 Mar 2023 14:08:19 +0000 Subject: [PATCH] Autodetermine if work-path needs to be specified in SSH authorized_keys If a user's `app.ini` contains a `APP_DATA_PATH` which refers to a non-absolute path then `gitea serv` etc. become dependent on the `AppWorkPath`. `gitea serv` may then require `--work-path` to be set in the `authorized_keys` if the `AppWorkPath` determined by `gitea web` and `gitea serv` are different. This would occur if `GITEA_WORK_DIR` is set, `--work-path` is used to run `gitea web` or if the AppPath cannot be determined at start-up. This PR adds some code to attempt to automatically determine if this is necessary and adds some documentation to suggest adding `--work-path` to the template. This should prevent SSH failures from happening as described in https://github.com/go-gitea/gitea/pull/19317 Replace #22754 Signed-off-by: Andrew Thornton --- .../doc/advanced/config-cheat-sheet.en-us.md | 3 ++ modules/setting/server.go | 11 +++++- modules/setting/setting.go | 14 +++++-- modules/setting/ssh.go | 37 +++++++++++-------- 4 files changed, 44 insertions(+), 21 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 c4ff8bafb9209..d2f46caaab652 100644 --- a/docs/content/doc/advanced/config-cheat-sheet.en-us.md +++ b/docs/content/doc/advanced/config-cheat-sheet.en-us.md @@ -338,6 +338,9 @@ The following configuration set `Content-Type: application/vnd.android.package-a - `SSH_CREATE_AUTHORIZED_PRINCIPALS_FILE`: **false/true**: Gitea will create a authorized_principals file by default when it is not using the internal ssh server and `SSH_AUTHORIZED_PRINCIPALS_ALLOW` is not `off`. - `SSH_AUTHORIZED_PRINCIPALS_BACKUP`: **false/true**: Enable SSH Authorized Principals Backup when rewriting all keys, default is true if `SSH_AUTHORIZED_PRINCIPALS_ALLOW` is not `off`. - `SSH_AUTHORIZED_KEYS_COMMAND_TEMPLATE`: **{{.AppPath}} --config={{.CustomConf}} serv key-{{.Key.ID}}**: Set the template for the command to passed on authorized keys. Possible keys are: AppPath, AppWorkPath, CustomConf, CustomPath, Key - where Key is a `models/asymkey.PublicKey` and the others are strings which are shellquoted. + - Gitea will add `--work-path={{.AppWorkPath}}` to the default template + if it determines that this is needed, however, you may need to add this explicitly + to any non-default template if your `APP_DATA_PATH` is not an absolute path. - `SSH_SERVER_CIPHERS`: **chacha20-poly1305@openssh.com, aes128-ctr, aes192-ctr, aes256-ctr, aes128-gcm@openssh.com, aes256-gcm@openssh.com**: For the built-in SSH server, choose the ciphers to support for SSH connections, for system SSH this setting has no effect. - `SSH_SERVER_KEY_EXCHANGES`: **curve25519-sha256, ecdh-sha2-nistp256, ecdh-sha2-nistp384, ecdh-sha2-nistp521, diffie-hellman-group14-sha256, diffie-hellman-group14-sha1**: For the built-in SSH server, choose the key exchange algorithms to support for SSH connections, for system SSH this setting has no effect. - `SSH_SERVER_MACS`: **hmac-sha2-256-etm@openssh.com, hmac-sha2-256, hmac-sha1**: For the built-in SSH server, choose the MACs to support for SSH connections, for system SSH this setting has no effect diff --git a/modules/setting/server.go b/modules/setting/server.go index 183906268576c..d3317bbebf008 100644 --- a/modules/setting/server.go +++ b/modules/setting/server.go @@ -53,7 +53,8 @@ var ( AppSubURL string // AppDataPath is the default path for storing data. // It maps to ini:"APP_DATA_PATH" in [server] and defaults to AppWorkPath + "/data" - AppDataPath string + AppDataPath string + AppDataPathDependentOnWorkPath bool // This is a helper marker used to inform if the AppDataPath is dependent on a user-provided or auto-determined work path // LocalURL is the url for locally running applications to contact Gitea. It always has a '/' suffix // It maps to ini:"LOCAL_ROOT_URL" in [server] LocalURL string @@ -323,8 +324,14 @@ func loadServerFrom(rootCfg ConfigProvider) { } StaticRootPath = sec.Key("STATIC_ROOT_PATH").MustString(StaticRootPath) StaticCacheTime = sec.Key("STATIC_CACHE_TIME").MustDuration(6 * time.Hour) - AppDataPath = sec.Key("APP_DATA_PATH").MustString(path.Join(AppWorkPath, "data")) + AppDataPathDependentOnWorkPath = false + AppDataPath = sec.Key("APP_DATA_PATH").MustString("") + if AppDataPath == "" { + AppDataPath = path.Join(AppWorkPath, "data") + AppDataPathDependentOnWorkPath = appWorkPathIsNotDefault + } if !filepath.IsAbs(AppDataPath) { + AppDataPathDependentOnWorkPath = appWorkPathIsNotDefault log.Info("The provided APP_DATA_PATH: %s is not absolute - it will be made absolute against the work path: %s", AppDataPath, AppWorkPath) AppDataPath = filepath.ToSlash(filepath.Join(AppWorkPath, AppDataPath)) } diff --git a/modules/setting/setting.go b/modules/setting/setting.go index 87b1e2797fe7d..38557ab4b23c8 100644 --- a/modules/setting/setting.go +++ b/modules/setting/setting.go @@ -39,7 +39,8 @@ var ( // If that is not set it is the default set here by the linker or failing that the directory of AppPath. // // AppWorkPath is used as the base path for several other paths. - AppWorkPath string + AppWorkPath string + appWorkPathIsNotDefault bool // Global setting objects CfgProvider ConfigProvider @@ -82,8 +83,8 @@ func getAppPath() (string, error) { func getWorkPath(appPath string) string { workPath := AppWorkPath - if giteaWorkPath, ok := os.LookupEnv("GITEA_WORK_DIR"); ok { + appWorkPathIsNotDefault = true workPath = giteaWorkPath } if len(workPath) == 0 { @@ -96,6 +97,7 @@ func getWorkPath(appPath string) string { } workPath = strings.ReplaceAll(workPath, "\\", "/") if !filepath.IsAbs(workPath) { + appWorkPathIsNotDefault = true log.Info("Provided work path %s is not absolute - will be made absolute against the current working directory", workPath) absPath, err := filepath.Abs(workPath) @@ -159,10 +161,14 @@ func createPIDFile(pidPath string) { // SetCustomPathAndConf will set CustomPath and CustomConf with reference to the // GITEA_CUSTOM environment variable and with provided overrides before stepping -// back to the default +// back to the defaultAppWorkPath = func SetCustomPathAndConf(providedCustom, providedConf, providedWorkPath string) { if len(providedWorkPath) != 0 { - AppWorkPath = filepath.ToSlash(providedWorkPath) + providedWorkPath = filepath.ToSlash(providedWorkPath) + if providedWorkPath != AppWorkPath { + appWorkPathIsNotDefault = true + AppWorkPath = providedWorkPath + } } if giteaCustom, ok := os.LookupEnv("GITEA_CUSTOM"); ok { CustomPath = giteaCustom diff --git a/modules/setting/ssh.go b/modules/setting/ssh.go index e8796f98d6f14..7b7f0fc7f5c8e 100644 --- a/modules/setting/ssh.go +++ b/modules/setting/ssh.go @@ -37,6 +37,7 @@ var SSH = struct { AuthorizedKeysBackup bool `ini:"SSH_AUTHORIZED_KEYS_BACKUP"` AuthorizedPrincipalsBackup bool `ini:"SSH_AUTHORIZED_PRINCIPALS_BACKUP"` AuthorizedKeysCommandTemplate string `ini:"SSH_AUTHORIZED_KEYS_COMMAND_TEMPLATE"` + AuthorizedKeysCommandWorkPathTemplate string `ini:"-"` AuthorizedKeysCommandTemplateTemplate *template.Template `ini:"-"` MinimumKeySizeCheck bool `ini:"-"` MinimumKeySizes map[string]int `ini:"-"` @@ -51,20 +52,21 @@ var SSH = struct { PerWriteTimeout time.Duration `ini:"SSH_PER_WRITE_TIMEOUT"` PerWritePerKbTimeout time.Duration `ini:"SSH_PER_WRITE_PER_KB_TIMEOUT"` }{ - Disabled: false, - StartBuiltinServer: false, - Domain: "", - Port: 22, - ServerCiphers: []string{"chacha20-poly1305@openssh.com", "aes128-ctr", "aes192-ctr", "aes256-ctr", "aes128-gcm@openssh.com", "aes256-gcm@openssh.com"}, - ServerKeyExchanges: []string{"curve25519-sha256", "ecdh-sha2-nistp256", "ecdh-sha2-nistp384", "ecdh-sha2-nistp521", "diffie-hellman-group14-sha256", "diffie-hellman-group14-sha1"}, - ServerMACs: []string{"hmac-sha2-256-etm@openssh.com", "hmac-sha2-256", "hmac-sha1"}, - KeygenPath: "ssh-keygen", - MinimumKeySizeCheck: true, - MinimumKeySizes: map[string]int{"ed25519": 256, "ed25519-sk": 256, "ecdsa": 256, "ecdsa-sk": 256, "rsa": 2047}, - ServerHostKeys: []string{"ssh/gitea.rsa", "ssh/gogs.rsa"}, - AuthorizedKeysCommandTemplate: "{{.AppPath}} --config={{.CustomConf}} serv key-{{.Key.ID}}", - PerWriteTimeout: PerWriteTimeout, - PerWritePerKbTimeout: PerWritePerKbTimeout, + Disabled: false, + StartBuiltinServer: false, + Domain: "", + Port: 22, + ServerCiphers: []string{"chacha20-poly1305@openssh.com", "aes128-ctr", "aes192-ctr", "aes256-ctr", "aes128-gcm@openssh.com", "aes256-gcm@openssh.com"}, + ServerKeyExchanges: []string{"curve25519-sha256", "ecdh-sha2-nistp256", "ecdh-sha2-nistp384", "ecdh-sha2-nistp521", "diffie-hellman-group14-sha256", "diffie-hellman-group14-sha1"}, + ServerMACs: []string{"hmac-sha2-256-etm@openssh.com", "hmac-sha2-256", "hmac-sha1"}, + KeygenPath: "ssh-keygen", + MinimumKeySizeCheck: true, + MinimumKeySizes: map[string]int{"ed25519": 256, "ed25519-sk": 256, "ecdsa": 256, "ecdsa-sk": 256, "rsa": 2047}, + ServerHostKeys: []string{"ssh/gitea.rsa", "ssh/gogs.rsa"}, + AuthorizedKeysCommandTemplate: "{{.AppPath}} --config={{.CustomConf}} serv key-{{.Key.ID}}", + AuthorizedKeysCommandWorkPathTemplate: "{{.AppPath}} --config={{.CustomConf}} --work-path={{.AppWorkPath}} serv key-{{.Key.ID}}", + PerWriteTimeout: PerWriteTimeout, + PerWritePerKbTimeout: PerWritePerKbTimeout, } func parseAuthorizedPrincipalsAllow(values []string) ([]string, bool) { @@ -184,7 +186,12 @@ func loadSSHFrom(rootCfg ConfigProvider) { } SSH.ExposeAnonymous = sec.Key("SSH_EXPOSE_ANONYMOUS").MustBool(false) - SSH.AuthorizedKeysCommandTemplate = sec.Key("SSH_AUTHORIZED_KEYS_COMMAND_TEMPLATE").MustString(SSH.AuthorizedKeysCommandTemplate) + // Now if the AppData path is dependent on the work path we need to set a different default Template + if AppDataPathDependentOnWorkPath { + SSH.AuthorizedKeysCommandTemplate = sec.Key("SSH_AUTHORIZED_KEYS_COMMAND_TEMPLATE").MustString(SSH.AuthorizedKeysCommandWorkPathTemplate) + } else { + SSH.AuthorizedKeysCommandTemplate = sec.Key("SSH_AUTHORIZED_KEYS_COMMAND_TEMPLATE").MustString(SSH.AuthorizedKeysCommandTemplate) + } SSH.AuthorizedKeysCommandTemplateTemplate = template.Must(template.New("").Parse(SSH.AuthorizedKeysCommandTemplate))