aboutsummaryrefslogtreecommitdiff
path: root/internal
diff options
context:
space:
mode:
authorJoe Chen <jc@unknwon.io>2023-02-12 13:10:41 +0800
committerGitHub <noreply@github.com>2023-02-12 13:10:41 +0800
commitb34ee734972429cc16686edc87dd38ad5a2f65aa (patch)
tree253cf458dcf9212fbf58fb1229cec09d0cf0f1f3 /internal
parent92f66c9eac950c7b28a83f5b02c1209897bb89b7 (diff)
feat(ssh): support dynamic list of algorithms (#7345)
Diffstat (limited to 'internal')
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
}