mirror of
				https://gitcode.com/gitea/gitea.git
				synced 2025-10-25 12:26:40 +08:00 
			
		
		
		
	 7a8a4f5432
			
		
	
	7a8a4f5432
	
	
	
		
			
			Without this patch, the setting SSH.StartBuiltinServer decides whether the native (Go) implementation is used rather than calling 'ssh-keygen'. It's possible for 'using ssh-keygen' and 'using the built-in server' to be independent. In fact, the gitea rootless container doesn't ship ssh-keygen and can be configured to use the host's SSH server - which will cause the public key parsing mechanism to break. This commit changes the decision to be based on SSH.KeygenPath instead. Any existing configurations with a custom KeygenPath set will continue to function. The new default value of '' selects the native version. The downside of this approach is that anyone who has relying on plain 'ssh-keygen' to have special properties will now be using the native version instead. I assume the exec-variant is only there because /x/crypto/ssh didn't support ssh-ed25519 until 2016. I don't see any other reason for using it so it might be an acceptable risk. Fixes #23363 EDIT: this message was garbled when I tried to get the commit description back in.. Trying to reconstruct it: ## ⚠️ BREAKING ⚠️ Users who don't have SSH.KeygenPath explicitly set and rely on the ssh-keygen binary need to set SSH.KeygenPath to 'ssh-keygen' in order to be able to continue using it for public key parsing. There was something else but I can't remember at the moment. EDIT2: It was about `make test` and `make lint`. Can't get them to run. To reproduce the issue, I installed `golang` in `docker.io/node:16` and got: ``` ... go: mvdan.cc/xurls/v2@v2.4.0: unknown revision mvdan.cc/xurls/v2.4.0 go: gotest.tools/v3@v3.4.0: unknown revision gotest.tools/v3.4.0 ... go: gotest.tools/v3@v3.0.3: unknown revision gotest.tools/v3.0.3 ... go: error loading module requirements ``` Signed-off-by: Leon M. Busch-George <leon@georgemail.eu>
		
			
				
	
	
		
			198 lines
		
	
	
		
			8.5 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			198 lines
		
	
	
		
			8.5 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| // Copyright 2023 The Gitea Authors. All rights reserved.
 | |
| // SPDX-License-Identifier: MIT
 | |
| 
 | |
| package setting
 | |
| 
 | |
| import (
 | |
| 	"os"
 | |
| 	"path"
 | |
| 	"path/filepath"
 | |
| 	"strings"
 | |
| 	"text/template"
 | |
| 	"time"
 | |
| 
 | |
| 	"code.gitea.io/gitea/modules/log"
 | |
| 	"code.gitea.io/gitea/modules/util"
 | |
| 
 | |
| 	gossh "golang.org/x/crypto/ssh"
 | |
| )
 | |
| 
 | |
| var SSH = struct {
 | |
| 	Disabled                              bool               `ini:"DISABLE_SSH"`
 | |
| 	StartBuiltinServer                    bool               `ini:"START_SSH_SERVER"`
 | |
| 	BuiltinServerUser                     string             `ini:"BUILTIN_SSH_SERVER_USER"`
 | |
| 	UseProxyProtocol                      bool               `ini:"SSH_SERVER_USE_PROXY_PROTOCOL"`
 | |
| 	Domain                                string             `ini:"SSH_DOMAIN"`
 | |
| 	Port                                  int                `ini:"SSH_PORT"`
 | |
| 	User                                  string             `ini:"SSH_USER"`
 | |
| 	ListenHost                            string             `ini:"SSH_LISTEN_HOST"`
 | |
| 	ListenPort                            int                `ini:"SSH_LISTEN_PORT"`
 | |
| 	RootPath                              string             `ini:"SSH_ROOT_PATH"`
 | |
| 	ServerCiphers                         []string           `ini:"SSH_SERVER_CIPHERS"`
 | |
| 	ServerKeyExchanges                    []string           `ini:"SSH_SERVER_KEY_EXCHANGES"`
 | |
| 	ServerMACs                            []string           `ini:"SSH_SERVER_MACS"`
 | |
| 	ServerHostKeys                        []string           `ini:"SSH_SERVER_HOST_KEYS"`
 | |
| 	KeyTestPath                           string             `ini:"SSH_KEY_TEST_PATH"`
 | |
| 	KeygenPath                            string             `ini:"SSH_KEYGEN_PATH"`
 | |
| 	AuthorizedKeysBackup                  bool               `ini:"SSH_AUTHORIZED_KEYS_BACKUP"`
 | |
| 	AuthorizedPrincipalsBackup            bool               `ini:"SSH_AUTHORIZED_PRINCIPALS_BACKUP"`
 | |
| 	AuthorizedKeysCommandTemplate         string             `ini:"SSH_AUTHORIZED_KEYS_COMMAND_TEMPLATE"`
 | |
| 	AuthorizedKeysCommandTemplateTemplate *template.Template `ini:"-"`
 | |
| 	MinimumKeySizeCheck                   bool               `ini:"-"`
 | |
| 	MinimumKeySizes                       map[string]int     `ini:"-"`
 | |
| 	CreateAuthorizedKeysFile              bool               `ini:"SSH_CREATE_AUTHORIZED_KEYS_FILE"`
 | |
| 	CreateAuthorizedPrincipalsFile        bool               `ini:"SSH_CREATE_AUTHORIZED_PRINCIPALS_FILE"`
 | |
| 	ExposeAnonymous                       bool               `ini:"SSH_EXPOSE_ANONYMOUS"`
 | |
| 	AuthorizedPrincipalsAllow             []string           `ini:"SSH_AUTHORIZED_PRINCIPALS_ALLOW"`
 | |
| 	AuthorizedPrincipalsEnabled           bool               `ini:"-"`
 | |
| 	TrustedUserCAKeys                     []string           `ini:"SSH_TRUSTED_USER_CA_KEYS"`
 | |
| 	TrustedUserCAKeysFile                 string             `ini:"SSH_TRUSTED_USER_CA_KEYS_FILENAME"`
 | |
| 	TrustedUserCAKeysParsed               []gossh.PublicKey  `ini:"-"`
 | |
| 	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:                    "",
 | |
| 	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,
 | |
| }
 | |
| 
 | |
| func parseAuthorizedPrincipalsAllow(values []string) ([]string, bool) {
 | |
| 	anything := false
 | |
| 	email := false
 | |
| 	username := false
 | |
| 	for _, value := range values {
 | |
| 		v := strings.ToLower(strings.TrimSpace(value))
 | |
| 		switch v {
 | |
| 		case "off":
 | |
| 			return []string{"off"}, false
 | |
| 		case "email":
 | |
| 			email = true
 | |
| 		case "username":
 | |
| 			username = true
 | |
| 		case "anything":
 | |
| 			anything = true
 | |
| 		}
 | |
| 	}
 | |
| 	if anything {
 | |
| 		return []string{"anything"}, true
 | |
| 	}
 | |
| 
 | |
| 	authorizedPrincipalsAllow := []string{}
 | |
| 	if username {
 | |
| 		authorizedPrincipalsAllow = append(authorizedPrincipalsAllow, "username")
 | |
| 	}
 | |
| 	if email {
 | |
| 		authorizedPrincipalsAllow = append(authorizedPrincipalsAllow, "email")
 | |
| 	}
 | |
| 
 | |
| 	return authorizedPrincipalsAllow, true
 | |
| }
 | |
| 
 | |
| func loadSSHFrom(rootCfg ConfigProvider) {
 | |
| 	sec := rootCfg.Section("server")
 | |
| 	if len(SSH.Domain) == 0 {
 | |
| 		SSH.Domain = Domain
 | |
| 	}
 | |
| 
 | |
| 	homeDir, err := util.HomeDir()
 | |
| 	if err != nil {
 | |
| 		log.Fatal("Failed to get home directory: %v", err)
 | |
| 	}
 | |
| 	homeDir = strings.ReplaceAll(homeDir, "\\", "/")
 | |
| 
 | |
| 	SSH.RootPath = path.Join(homeDir, ".ssh")
 | |
| 	serverCiphers := sec.Key("SSH_SERVER_CIPHERS").Strings(",")
 | |
| 	if len(serverCiphers) > 0 {
 | |
| 		SSH.ServerCiphers = serverCiphers
 | |
| 	}
 | |
| 	serverKeyExchanges := sec.Key("SSH_SERVER_KEY_EXCHANGES").Strings(",")
 | |
| 	if len(serverKeyExchanges) > 0 {
 | |
| 		SSH.ServerKeyExchanges = serverKeyExchanges
 | |
| 	}
 | |
| 	serverMACs := sec.Key("SSH_SERVER_MACS").Strings(",")
 | |
| 	if len(serverMACs) > 0 {
 | |
| 		SSH.ServerMACs = serverMACs
 | |
| 	}
 | |
| 	SSH.KeyTestPath = os.TempDir()
 | |
| 	if err = sec.MapTo(&SSH); err != nil {
 | |
| 		log.Fatal("Failed to map SSH settings: %v", err)
 | |
| 	}
 | |
| 	for i, key := range SSH.ServerHostKeys {
 | |
| 		if !filepath.IsAbs(key) {
 | |
| 			SSH.ServerHostKeys[i] = filepath.Join(AppDataPath, key)
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	SSH.KeygenPath = sec.Key("SSH_KEYGEN_PATH").String()
 | |
| 	SSH.Port = sec.Key("SSH_PORT").MustInt(22)
 | |
| 	SSH.ListenPort = sec.Key("SSH_LISTEN_PORT").MustInt(SSH.Port)
 | |
| 	SSH.UseProxyProtocol = sec.Key("SSH_SERVER_USE_PROXY_PROTOCOL").MustBool(false)
 | |
| 
 | |
| 	// When disable SSH, start builtin server value is ignored.
 | |
| 	if SSH.Disabled {
 | |
| 		SSH.StartBuiltinServer = false
 | |
| 	}
 | |
| 
 | |
| 	SSH.TrustedUserCAKeysFile = sec.Key("SSH_TRUSTED_USER_CA_KEYS_FILENAME").MustString(filepath.Join(SSH.RootPath, "gitea-trusted-user-ca-keys.pem"))
 | |
| 
 | |
| 	for _, caKey := range SSH.TrustedUserCAKeys {
 | |
| 		pubKey, _, _, _, err := gossh.ParseAuthorizedKey([]byte(caKey))
 | |
| 		if err != nil {
 | |
| 			log.Fatal("Failed to parse TrustedUserCaKeys: %s %v", caKey, err)
 | |
| 		}
 | |
| 
 | |
| 		SSH.TrustedUserCAKeysParsed = append(SSH.TrustedUserCAKeysParsed, pubKey)
 | |
| 	}
 | |
| 	if len(SSH.TrustedUserCAKeys) > 0 {
 | |
| 		// Set the default as email,username otherwise we can leave it empty
 | |
| 		sec.Key("SSH_AUTHORIZED_PRINCIPALS_ALLOW").MustString("username,email")
 | |
| 	} else {
 | |
| 		sec.Key("SSH_AUTHORIZED_PRINCIPALS_ALLOW").MustString("off")
 | |
| 	}
 | |
| 
 | |
| 	SSH.AuthorizedPrincipalsAllow, SSH.AuthorizedPrincipalsEnabled = parseAuthorizedPrincipalsAllow(sec.Key("SSH_AUTHORIZED_PRINCIPALS_ALLOW").Strings(","))
 | |
| 
 | |
| 	SSH.MinimumKeySizeCheck = sec.Key("MINIMUM_KEY_SIZE_CHECK").MustBool(SSH.MinimumKeySizeCheck)
 | |
| 	minimumKeySizes := rootCfg.Section("ssh.minimum_key_sizes").Keys()
 | |
| 	for _, key := range minimumKeySizes {
 | |
| 		if key.MustInt() != -1 {
 | |
| 			SSH.MinimumKeySizes[strings.ToLower(key.Name())] = key.MustInt()
 | |
| 		} else {
 | |
| 			delete(SSH.MinimumKeySizes, strings.ToLower(key.Name()))
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	SSH.AuthorizedKeysBackup = sec.Key("SSH_AUTHORIZED_KEYS_BACKUP").MustBool(true)
 | |
| 	SSH.CreateAuthorizedKeysFile = sec.Key("SSH_CREATE_AUTHORIZED_KEYS_FILE").MustBool(true)
 | |
| 
 | |
| 	SSH.AuthorizedPrincipalsBackup = false
 | |
| 	SSH.CreateAuthorizedPrincipalsFile = false
 | |
| 	if SSH.AuthorizedPrincipalsEnabled {
 | |
| 		SSH.AuthorizedPrincipalsBackup = sec.Key("SSH_AUTHORIZED_PRINCIPALS_BACKUP").MustBool(true)
 | |
| 		SSH.CreateAuthorizedPrincipalsFile = sec.Key("SSH_CREATE_AUTHORIZED_PRINCIPALS_FILE").MustBool(true)
 | |
| 	}
 | |
| 
 | |
| 	SSH.ExposeAnonymous = sec.Key("SSH_EXPOSE_ANONYMOUS").MustBool(false)
 | |
| 	SSH.AuthorizedKeysCommandTemplate = sec.Key("SSH_AUTHORIZED_KEYS_COMMAND_TEMPLATE").MustString(SSH.AuthorizedKeysCommandTemplate)
 | |
| 
 | |
| 	SSH.AuthorizedKeysCommandTemplateTemplate = template.Must(template.New("").Parse(SSH.AuthorizedKeysCommandTemplate))
 | |
| 
 | |
| 	SSH.PerWriteTimeout = sec.Key("SSH_PER_WRITE_TIMEOUT").MustDuration(PerWriteTimeout)
 | |
| 	SSH.PerWritePerKbTimeout = sec.Key("SSH_PER_WRITE_PER_KB_TIMEOUT").MustDuration(PerWritePerKbTimeout)
 | |
| 
 | |
| 	// ensure parseRunModeSetting has been executed before this
 | |
| 	SSH.BuiltinServerUser = rootCfg.Section("server").Key("BUILTIN_SSH_SERVER_USER").MustString(RunUser)
 | |
| 	SSH.User = rootCfg.Section("server").Key("SSH_USER").MustString(SSH.BuiltinServerUser)
 | |
| }
 |