diff options
author | Joe Chen <jc@unknwon.io> | 2023-02-12 13:10:41 +0800 |
---|---|---|
committer | GitHub <noreply@github.com> | 2023-02-12 13:10:41 +0800 |
commit | b34ee734972429cc16686edc87dd38ad5a2f65aa (patch) | |
tree | 253cf458dcf9212fbf58fb1229cec09d0cf0f1f3 /internal | |
parent | 92f66c9eac950c7b28a83f5b02c1209897bb89b7 (diff) |
feat(ssh): support dynamic list of algorithms (#7345)
Diffstat (limited to 'internal')
-rw-r--r-- | internal/cmd/backup.go | 11 | ||||
-rw-r--r-- | internal/conf/static.go | 1 | ||||
-rw-r--r-- | internal/conf/testdata/TestInit.golden.ini | 1 | ||||
-rw-r--r-- | internal/route/install.go | 3 | ||||
-rw-r--r-- | internal/ssh/ssh.go | 76 |
5 files changed, 63 insertions, 29 deletions
diff --git a/internal/cmd/backup.go b/internal/cmd/backup.go index 63a73a71..e258bc3d 100644 --- a/internal/cmd/backup.go +++ b/internal/cmd/backup.go @@ -10,17 +10,18 @@ import ( "os" "path" "path/filepath" + "strconv" "time" "github.com/pkg/errors" "github.com/unknwon/cae/zip" - "github.com/unknwon/com" "github.com/urfave/cli" "gopkg.in/ini.v1" log "unknwon.dev/clog/v2" "gogs.io/gogs/internal/conf" "gogs.io/gogs/internal/db" + "gogs.io/gogs/internal/osutil" ) var Backup = cli.Command{ @@ -62,7 +63,7 @@ func runBackup(c *cli.Context) error { } tmpDir := c.String("tempdir") - if !com.IsExist(tmpDir) { + if !osutil.IsExist(tmpDir) { log.Fatal("'--tempdir' does not exist: %s", tmpDir) } rootDir, err := os.MkdirTemp(tmpDir, "gogs-backup-") @@ -74,7 +75,7 @@ func runBackup(c *cli.Context) error { // Metadata metaFile := path.Join(rootDir, "metadata.ini") metadata := ini.Empty() - metadata.Section("").Key("VERSION").SetValue(com.ToStr(currentBackupFormatVersion)) + metadata.Section("").Key("VERSION").SetValue(strconv.Itoa(currentBackupFormatVersion)) metadata.Section("").Key("DATE_TIME").SetValue(time.Now().String()) metadata.Section("").Key("GOGS_VERSION").SetValue(conf.App.Version) if err = metadata.SaveTo(metaFile); err != nil { @@ -109,9 +110,9 @@ func runBackup(c *cli.Context) error { } // Data files - for _, dir := range []string{"attachments", "avatars", "repo-avatars"} { + for _, dir := range []string{"ssh", "attachments", "avatars", "repo-avatars"} { dirPath := filepath.Join(conf.Server.AppDataPath, dir) - if !com.IsDir(dirPath) { + if !osutil.IsDir(dirPath) { continue } diff --git a/internal/conf/static.go b/internal/conf/static.go index 24e9acab..016fd139 100644 --- a/internal/conf/static.go +++ b/internal/conf/static.go @@ -305,6 +305,7 @@ type SSHOpts struct { ListenPort int `ini:"SSH_LISTEN_PORT"` ServerCiphers []string `ini:"SSH_SERVER_CIPHERS"` ServerMACs []string `ini:"SSH_SERVER_MACS"` + ServerAlgorithms []string `ini:"SSH_SERVER_ALGORITHMS"` } // SSH settings diff --git a/internal/conf/testdata/TestInit.golden.ini b/internal/conf/testdata/TestInit.golden.ini index 80f222df..1f0e50b7 100644 --- a/internal/conf/testdata/TestInit.golden.ini +++ b/internal/conf/testdata/TestInit.golden.ini @@ -32,6 +32,7 @@ SSH_LISTEN_HOST=0.0.0.0 SSH_LISTEN_PORT=22 SSH_SERVER_CIPHERS=aes128-ctr,aes192-ctr,aes256-ctr,aes128-gcm@openssh.com,arcfour256,arcfour128 SSH_SERVER_MACS=hmac-sha2-256-etm@openssh.com,hmac-sha2-256,hmac-sha1 +SSH_SERVER_ALGORITHMS=rsa,ecdsa,ed25519 [repository] ROOT=/tmp/gogs-repositories diff --git a/internal/route/install.go b/internal/route/install.go index 15ff4439..2c1dde1d 100644 --- a/internal/route/install.go +++ b/internal/route/install.go @@ -98,10 +98,11 @@ func GlobalInit(customConf string) error { } if conf.SSH.StartBuiltinServer { - ssh.Listen(conf.SSH.ListenHost, conf.SSH.ListenPort, conf.SSH.ServerCiphers, conf.SSH.ServerMACs) + ssh.Listen(conf.SSH, conf.Server.AppDataPath) log.Info("SSH server started on %s:%v", conf.SSH.ListenHost, conf.SSH.ListenPort) log.Trace("SSH server cipher list: %v", conf.SSH.ServerCiphers) log.Trace("SSH server MAC list: %v", conf.SSH.ServerMACs) + log.Trace("SSH server algorithms: %v", conf.SSH.ServerAlgorithms) } if conf.SSH.RewriteAuthorizedKeysAtStart { diff --git a/internal/ssh/ssh.go b/internal/ssh/ssh.go index dfbd2281..9fbd4a69 100644 --- a/internal/ssh/ssh.go +++ b/internal/ssh/ssh.go @@ -5,6 +5,7 @@ package ssh import ( + "context" "fmt" "io" "net" @@ -12,13 +13,17 @@ import ( "os/exec" "path/filepath" "strings" + "syscall" + "github.com/pkg/errors" + "github.com/sourcegraph/run" "github.com/unknwon/com" "golang.org/x/crypto/ssh" log "unknwon.dev/clog/v2" "gogs.io/gogs/internal/conf" "gogs.io/gogs/internal/db" + "gogs.io/gogs/internal/osutil" ) func cleanCommand(cmd string) string { @@ -144,8 +149,8 @@ func listen(config *ssh.ServerConfig, host string, port int) { log.Trace("SSH: Handshaking for %s", conn.RemoteAddr()) sConn, chans, reqs, err := ssh.NewServerConn(conn, config) if err != nil { - if err == io.EOF { - log.Warn("SSH: Handshaking was terminated: %v", err) + if err == io.EOF || errors.Is(err, syscall.ECONNRESET) { + log.Trace("SSH: Handshaking was terminated: %v", err) } else { log.Error("SSH: Error on handshaking: %v", err) } @@ -161,11 +166,11 @@ func listen(config *ssh.ServerConfig, host string, port int) { } // Listen starts a SSH server listens on given port. -func Listen(host string, port int, ciphers, macs []string) { +func Listen(opts conf.SSHOpts, appDataPath string) { config := &ssh.ServerConfig{ Config: ssh.Config{ - Ciphers: ciphers, - MACs: macs, + Ciphers: opts.ServerCiphers, + MACs: opts.ServerMACs, }, PublicKeyCallback: func(conn ssh.ConnMetadata, key ssh.PublicKey) (*ssh.Permissions, error) { pkey, err := db.SearchPublicKeyByContent(strings.TrimSpace(string(ssh.MarshalAuthorizedKey(key)))) @@ -177,27 +182,52 @@ func Listen(host string, port int, ciphers, macs []string) { }, } - keyPath := filepath.Join(conf.Server.AppDataPath, "ssh", "gogs.rsa") - if !com.IsExist(keyPath) { - if err := os.MkdirAll(filepath.Dir(keyPath), os.ModePerm); err != nil { - panic(err) - } - _, stderr, err := com.ExecCmd(conf.SSH.KeygenPath, "-f", keyPath, "-t", "rsa", "-m", "PEM", "-N", "") - if err != nil { - panic(fmt.Sprintf("Failed to generate private key: %v - %s", err, stderr)) - } - log.Trace("SSH: New private key is generateed: %s", keyPath) - } - - privateBytes, err := os.ReadFile(keyPath) + keys, err := setupHostKeys(appDataPath, opts.ServerAlgorithms) if err != nil { - panic("SSH: Failed to load private key: " + err.Error()) + log.Fatal("SSH: Failed to setup host keys: %v", err) } - private, err := ssh.ParsePrivateKey(privateBytes) + for _, key := range keys { + config.AddHostKey(key) + } + + go listen(config, opts.ListenHost, opts.ListenPort) +} + +func setupHostKeys(appDataPath string, algorithms []string) ([]ssh.Signer, error) { + dir := filepath.Join(appDataPath, "ssh") + err := os.MkdirAll(dir, os.ModePerm) if err != nil { - panic("SSH: Failed to parse private key: " + err.Error()) + return nil, errors.Wrapf(err, "create host key directory") } - config.AddHostKey(private) - go listen(config, host, port) + var hostKeys []ssh.Signer + for _, algo := range algorithms { + keyPath := filepath.Join(dir, "gogs."+algo) + if !osutil.IsExist(keyPath) { + args := []string{ + conf.SSH.KeygenPath, + "-t", algo, + "-f", keyPath, + "-m", "PEM", + "-N", run.Arg(""), + } + err = run.Cmd(context.Background(), args...).Run().Wait() + if err != nil { + return nil, errors.Wrapf(err, "generate host key with args %v", args) + } + log.Trace("SSH: New private key is generated: %s", keyPath) + } + + keyData, err := os.ReadFile(keyPath) + if err != nil { + return nil, errors.Wrapf(err, "read host key %q", keyPath) + } + signer, err := ssh.ParsePrivateKey(keyData) + if err != nil { + return nil, errors.Wrapf(err, "parse host key %q", keyPath) + } + + hostKeys = append(hostKeys, signer) + } + return hostKeys, nil } |