mirror of https://github.com/go-gitea/gitea.git
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
186 lines
7.7 KiB
186 lines
7.7 KiB
// Copyright 2023 The Gitea Authors. All rights reserved. |
|
// SPDX-License-Identifier: MIT |
|
|
|
package setting |
|
|
|
import ( |
|
"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"` |
|
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, |
|
MinimumKeySizeCheck: true, |
|
MinimumKeySizes: map[string]int{"ed25519": 256, "ed25519-sk": 256, "ecdsa": 256, "ecdsa-sk": 256, "rsa": 3071}, |
|
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 = filepath.Join(homeDir, ".ssh") |
|
|
|
if err = sec.MapTo(&SSH); err != nil { |
|
log.Fatal("Failed to map SSH settings: %v", err) |
|
} |
|
|
|
serverCiphers := sec.Key("SSH_SERVER_CIPHERS").Strings(",") |
|
SSH.ServerCiphers = util.Iif(len(serverCiphers) > 0, serverCiphers, nil) |
|
|
|
serverKeyExchanges := sec.Key("SSH_SERVER_KEY_EXCHANGES").Strings(",") |
|
SSH.ServerKeyExchanges = util.Iif(len(serverKeyExchanges) > 0, serverKeyExchanges, nil) |
|
|
|
serverMACs := sec.Key("SSH_SERVER_MACS").Strings(",") |
|
SSH.ServerMACs = util.Iif(len(serverMACs) > 0, serverMACs, nil) |
|
|
|
for i, key := range SSH.ServerHostKeys { |
|
if !filepath.IsAbs(key) { |
|
SSH.ServerHostKeys[i] = filepath.Join(AppDataPath, key) |
|
} |
|
} |
|
|
|
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(false) |
|
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) |
|
}
|
|
|