gitea源码

ssh.go 7.7KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187
  1. // Copyright 2023 The Gitea Authors. All rights reserved.
  2. // SPDX-License-Identifier: MIT
  3. package setting
  4. import (
  5. "path/filepath"
  6. "strings"
  7. "text/template"
  8. "time"
  9. "code.gitea.io/gitea/modules/log"
  10. "code.gitea.io/gitea/modules/util"
  11. gossh "golang.org/x/crypto/ssh"
  12. )
  13. var SSH = struct {
  14. Disabled bool `ini:"DISABLE_SSH"`
  15. StartBuiltinServer bool `ini:"START_SSH_SERVER"`
  16. BuiltinServerUser string `ini:"BUILTIN_SSH_SERVER_USER"`
  17. UseProxyProtocol bool `ini:"SSH_SERVER_USE_PROXY_PROTOCOL"`
  18. Domain string `ini:"SSH_DOMAIN"`
  19. Port int `ini:"SSH_PORT"`
  20. User string `ini:"SSH_USER"`
  21. ListenHost string `ini:"SSH_LISTEN_HOST"`
  22. ListenPort int `ini:"SSH_LISTEN_PORT"`
  23. RootPath string `ini:"SSH_ROOT_PATH"`
  24. ServerCiphers []string `ini:"SSH_SERVER_CIPHERS"`
  25. ServerKeyExchanges []string `ini:"SSH_SERVER_KEY_EXCHANGES"`
  26. ServerMACs []string `ini:"SSH_SERVER_MACS"`
  27. ServerHostKeys []string `ini:"SSH_SERVER_HOST_KEYS"`
  28. AuthorizedKeysBackup bool `ini:"SSH_AUTHORIZED_KEYS_BACKUP"`
  29. AuthorizedPrincipalsBackup bool `ini:"SSH_AUTHORIZED_PRINCIPALS_BACKUP"`
  30. AuthorizedKeysCommandTemplate string `ini:"SSH_AUTHORIZED_KEYS_COMMAND_TEMPLATE"`
  31. AuthorizedKeysCommandTemplateTemplate *template.Template `ini:"-"`
  32. MinimumKeySizeCheck bool `ini:"-"`
  33. MinimumKeySizes map[string]int `ini:"-"`
  34. CreateAuthorizedKeysFile bool `ini:"SSH_CREATE_AUTHORIZED_KEYS_FILE"`
  35. CreateAuthorizedPrincipalsFile bool `ini:"SSH_CREATE_AUTHORIZED_PRINCIPALS_FILE"`
  36. ExposeAnonymous bool `ini:"SSH_EXPOSE_ANONYMOUS"`
  37. AuthorizedPrincipalsAllow []string `ini:"SSH_AUTHORIZED_PRINCIPALS_ALLOW"`
  38. AuthorizedPrincipalsEnabled bool `ini:"-"`
  39. TrustedUserCAKeys []string `ini:"SSH_TRUSTED_USER_CA_KEYS"`
  40. TrustedUserCAKeysFile string `ini:"SSH_TRUSTED_USER_CA_KEYS_FILENAME"`
  41. TrustedUserCAKeysParsed []gossh.PublicKey `ini:"-"`
  42. PerWriteTimeout time.Duration `ini:"SSH_PER_WRITE_TIMEOUT"`
  43. PerWritePerKbTimeout time.Duration `ini:"SSH_PER_WRITE_PER_KB_TIMEOUT"`
  44. }{
  45. Disabled: false,
  46. StartBuiltinServer: false,
  47. Domain: "",
  48. Port: 22,
  49. MinimumKeySizeCheck: true,
  50. MinimumKeySizes: map[string]int{"ed25519": 256, "ed25519-sk": 256, "ecdsa": 256, "ecdsa-sk": 256, "rsa": 3071},
  51. ServerHostKeys: []string{"ssh/gitea.rsa", "ssh/gogs.rsa"},
  52. AuthorizedKeysCommandTemplate: "{{.AppPath}} --config={{.CustomConf}} serv key-{{.Key.ID}}",
  53. PerWriteTimeout: PerWriteTimeout,
  54. PerWritePerKbTimeout: PerWritePerKbTimeout,
  55. }
  56. func parseAuthorizedPrincipalsAllow(values []string) ([]string, bool) {
  57. anything := false
  58. email := false
  59. username := false
  60. for _, value := range values {
  61. v := strings.ToLower(strings.TrimSpace(value))
  62. switch v {
  63. case "off":
  64. return []string{"off"}, false
  65. case "email":
  66. email = true
  67. case "username":
  68. username = true
  69. case "anything":
  70. anything = true
  71. }
  72. }
  73. if anything {
  74. return []string{"anything"}, true
  75. }
  76. authorizedPrincipalsAllow := []string{}
  77. if username {
  78. authorizedPrincipalsAllow = append(authorizedPrincipalsAllow, "username")
  79. }
  80. if email {
  81. authorizedPrincipalsAllow = append(authorizedPrincipalsAllow, "email")
  82. }
  83. return authorizedPrincipalsAllow, true
  84. }
  85. func loadSSHFrom(rootCfg ConfigProvider) {
  86. sec := rootCfg.Section("server")
  87. if len(SSH.Domain) == 0 {
  88. SSH.Domain = Domain
  89. }
  90. homeDir, err := util.HomeDir()
  91. if err != nil {
  92. log.Fatal("Failed to get home directory: %v", err)
  93. }
  94. homeDir = strings.ReplaceAll(homeDir, "\\", "/")
  95. SSH.RootPath = filepath.Join(homeDir, ".ssh")
  96. if err = sec.MapTo(&SSH); err != nil {
  97. log.Fatal("Failed to map SSH settings: %v", err)
  98. }
  99. serverCiphers := sec.Key("SSH_SERVER_CIPHERS").Strings(",")
  100. SSH.ServerCiphers = util.Iif(len(serverCiphers) > 0, serverCiphers, nil)
  101. serverKeyExchanges := sec.Key("SSH_SERVER_KEY_EXCHANGES").Strings(",")
  102. SSH.ServerKeyExchanges = util.Iif(len(serverKeyExchanges) > 0, serverKeyExchanges, nil)
  103. serverMACs := sec.Key("SSH_SERVER_MACS").Strings(",")
  104. SSH.ServerMACs = util.Iif(len(serverMACs) > 0, serverMACs, nil)
  105. for i, key := range SSH.ServerHostKeys {
  106. if !filepath.IsAbs(key) {
  107. SSH.ServerHostKeys[i] = filepath.Join(AppDataPath, key)
  108. }
  109. }
  110. SSH.Port = sec.Key("SSH_PORT").MustInt(22)
  111. SSH.ListenPort = sec.Key("SSH_LISTEN_PORT").MustInt(SSH.Port)
  112. SSH.UseProxyProtocol = sec.Key("SSH_SERVER_USE_PROXY_PROTOCOL").MustBool(false)
  113. // When disable SSH, start builtin server value is ignored.
  114. if SSH.Disabled {
  115. SSH.StartBuiltinServer = false
  116. }
  117. SSH.TrustedUserCAKeysFile = sec.Key("SSH_TRUSTED_USER_CA_KEYS_FILENAME").MustString(filepath.Join(SSH.RootPath, "gitea-trusted-user-ca-keys.pem"))
  118. for _, caKey := range SSH.TrustedUserCAKeys {
  119. pubKey, _, _, _, err := gossh.ParseAuthorizedKey([]byte(caKey))
  120. if err != nil {
  121. log.Fatal("Failed to parse TrustedUserCaKeys: %s %v", caKey, err)
  122. }
  123. SSH.TrustedUserCAKeysParsed = append(SSH.TrustedUserCAKeysParsed, pubKey)
  124. }
  125. if len(SSH.TrustedUserCAKeys) > 0 {
  126. // Set the default as email,username otherwise we can leave it empty
  127. sec.Key("SSH_AUTHORIZED_PRINCIPALS_ALLOW").MustString("username,email")
  128. } else {
  129. sec.Key("SSH_AUTHORIZED_PRINCIPALS_ALLOW").MustString("off")
  130. }
  131. SSH.AuthorizedPrincipalsAllow, SSH.AuthorizedPrincipalsEnabled = parseAuthorizedPrincipalsAllow(sec.Key("SSH_AUTHORIZED_PRINCIPALS_ALLOW").Strings(","))
  132. SSH.MinimumKeySizeCheck = sec.Key("MINIMUM_KEY_SIZE_CHECK").MustBool(SSH.MinimumKeySizeCheck)
  133. minimumKeySizes := rootCfg.Section("ssh.minimum_key_sizes").Keys()
  134. for _, key := range minimumKeySizes {
  135. if key.MustInt() != -1 {
  136. SSH.MinimumKeySizes[strings.ToLower(key.Name())] = key.MustInt()
  137. } else {
  138. delete(SSH.MinimumKeySizes, strings.ToLower(key.Name()))
  139. }
  140. }
  141. SSH.AuthorizedKeysBackup = sec.Key("SSH_AUTHORIZED_KEYS_BACKUP").MustBool(false)
  142. SSH.CreateAuthorizedKeysFile = sec.Key("SSH_CREATE_AUTHORIZED_KEYS_FILE").MustBool(true)
  143. SSH.AuthorizedPrincipalsBackup = false
  144. SSH.CreateAuthorizedPrincipalsFile = false
  145. if SSH.AuthorizedPrincipalsEnabled {
  146. SSH.AuthorizedPrincipalsBackup = sec.Key("SSH_AUTHORIZED_PRINCIPALS_BACKUP").MustBool(true)
  147. SSH.CreateAuthorizedPrincipalsFile = sec.Key("SSH_CREATE_AUTHORIZED_PRINCIPALS_FILE").MustBool(true)
  148. }
  149. SSH.ExposeAnonymous = sec.Key("SSH_EXPOSE_ANONYMOUS").MustBool(false)
  150. SSH.AuthorizedKeysCommandTemplate = sec.Key("SSH_AUTHORIZED_KEYS_COMMAND_TEMPLATE").MustString(SSH.AuthorizedKeysCommandTemplate)
  151. SSH.AuthorizedKeysCommandTemplateTemplate = template.Must(template.New("").Parse(SSH.AuthorizedKeysCommandTemplate))
  152. SSH.PerWriteTimeout = sec.Key("SSH_PER_WRITE_TIMEOUT").MustDuration(PerWriteTimeout)
  153. SSH.PerWritePerKbTimeout = sec.Key("SSH_PER_WRITE_PER_KB_TIMEOUT").MustDuration(PerWritePerKbTimeout)
  154. // ensure parseRunModeSetting has been executed before this
  155. SSH.BuiltinServerUser = rootCfg.Section("server").Key("BUILTIN_SSH_SERVER_USER").MustString(RunUser)
  156. SSH.User = rootCfg.Section("server").Key("SSH_USER").MustString(SSH.BuiltinServerUser)
  157. }