mirror of
https://github.com/drakkan/sftpgo.git
synced 2025-12-07 23:00:55 +03:00
allows host keys auto generation inside a user configured directory
Fixes #124
This commit is contained in:
@@ -53,7 +53,7 @@ The configuration file contains the following sections:
|
|||||||
- `hook`, string. Absolute path to the command to execute or HTTP URL to notify.
|
- `hook`, string. Absolute path to the command to execute or HTTP URL to notify.
|
||||||
- `keys`, struct array. Deprecated, please use `host_keys`.
|
- `keys`, struct array. Deprecated, please use `host_keys`.
|
||||||
- `private_key`, path to the private key file. It can be a path relative to the config dir or an absolute one.
|
- `private_key`, path to the private key file. It can be a path relative to the config dir or an absolute one.
|
||||||
- `host_keys`, list of strings. It contains the daemon's private host keys. Each host key can be defined as a path relative to the configuration directory or an absolute one. If empty or missing, the daemon will search or try to generate `id_rsa` and `id_ecdsa` keys inside the configuration directory.
|
- `host_keys`, list of strings. It contains the daemon's private host keys. Each host key can be defined as a path relative to the configuration directory or an absolute one. If empty, the daemon will search or try to generate `id_rsa` and `id_ecdsa` keys inside the configuration directory. If you configure absolute paths to files named `id_rsa` and/or `id_ecdsa` then SFTPGo will try to generate these keys using the default settings.
|
||||||
- `kex_algorithms`, list of strings. Available KEX (Key Exchange) algorithms in preference order. Leave empty to use default values. The supported values can be found here: [`crypto/ssh`](https://github.com/golang/crypto/blob/master/ssh/common.go#L46 "Supported kex algos")
|
- `kex_algorithms`, list of strings. Available KEX (Key Exchange) algorithms in preference order. Leave empty to use default values. The supported values can be found here: [`crypto/ssh`](https://github.com/golang/crypto/blob/master/ssh/common.go#L46 "Supported kex algos")
|
||||||
- `ciphers`, list of strings. Allowed ciphers. Leave empty to use default values. The supported values can be found here: [`crypto/ssh`](https://github.com/golang/crypto/blob/master/ssh/common.go#L28 "Supported ciphers")
|
- `ciphers`, list of strings. Allowed ciphers. Leave empty to use default values. The supported values can be found here: [`crypto/ssh`](https://github.com/golang/crypto/blob/master/ssh/common.go#L28 "Supported ciphers")
|
||||||
- `macs`, list of strings. Available MAC (message authentication code) algorithms in preference order. Leave empty to use default values. The supported values can be found here: [`crypto/ssh`](https://github.com/golang/crypto/blob/master/ssh/common.go#L84 "Supported MACs")
|
- `macs`, list of strings. Available MAC (message authentication code) algorithms in preference order. Leave empty to use default values. The supported values can be found here: [`crypto/ssh`](https://github.com/golang/crypto/blob/master/ssh/common.go#L84 "Supported MACs")
|
||||||
@@ -119,23 +119,28 @@ The configuration file contains the following sections:
|
|||||||
|
|
||||||
A full example showing the default config (in JSON format) can be found [here](../sftpgo.json).
|
A full example showing the default config (in JSON format) can be found [here](../sftpgo.json).
|
||||||
|
|
||||||
If you want to use a private key that use an algorithm different from RSA or ECDSA, or more private keys, then generate your own keys and replace the empty `keys` array with something like this:
|
If you want to use a private host key that use an algorithm/setting different from the auto generated RSA/ECDSA keys, or more than two private keys, you can generate your own keys and replace the empty `keys` array with something like this:
|
||||||
|
|
||||||
```json
|
```json
|
||||||
"keys": [
|
"host_keys": [
|
||||||
{
|
"id_rsa",
|
||||||
"private_key": "id_rsa"
|
"id_ecdsa",
|
||||||
},
|
"id_ed25519"
|
||||||
{
|
|
||||||
"private_key": "id_ecdsa"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"private_key": "id_ed25519"
|
|
||||||
}
|
|
||||||
]
|
]
|
||||||
```
|
```
|
||||||
|
|
||||||
where `id_rsa`, `id_ecdsa` and `id_ed25519` in this example are files containing your generated keys. You can use absolute paths or paths relative to the configuration directory.
|
where `id_rsa`, `id_ecdsa` and `id_ed25519`, in this example, are files containing your generated keys. You can use absolute paths or paths relative to the configuration directory.
|
||||||
|
|
||||||
|
If you want the default host keys generation in a directory different from the config dir, please specify absolute paths to files named `id_rsa` or `id_ecdsa` like this:
|
||||||
|
|
||||||
|
```json
|
||||||
|
"host_keys": [
|
||||||
|
"/etc/sftpgo/keys/id_rsa",
|
||||||
|
"/etc/sftpgo/keys/id_ecdsa"
|
||||||
|
]
|
||||||
|
```
|
||||||
|
|
||||||
|
then SFTPGo will try to create `id_rsa` and `id_ecdsa`, if they are missing, inside the existing directory `/etc/sftpgo/keys`.
|
||||||
|
|
||||||
The configuration can be read from JSON, TOML, YAML, HCL, envfile and Java properties config files. If your `config-file` flag is set to `sftpgo` (default value), you need to create a configuration file called `sftpgo.json` or `sftpgo.yaml` and so on inside `config-dir`.
|
The configuration can be read from JSON, TOML, YAML, HCL, envfile and Java properties config files. If your `config-file` flag is set to `sftpgo` (default value), you need to create a configuration file called `sftpgo.json` or `sftpgo.yaml` and so on inside `config-dir`.
|
||||||
|
|
||||||
|
|||||||
@@ -1803,18 +1803,53 @@ func TestProxyProtocolVersion(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestLoadHostKeys(t *testing.T) {
|
func TestLoadHostKeys(t *testing.T) {
|
||||||
|
configDir := ".."
|
||||||
|
serverConfig := &ssh.ServerConfig{}
|
||||||
c := Configuration{}
|
c := Configuration{}
|
||||||
c.HostKeys = []string{".", "missing file"}
|
c.HostKeys = []string{".", "missing file"}
|
||||||
err := c.checkAndLoadHostKeys("..", &ssh.ServerConfig{})
|
err := c.checkAndLoadHostKeys(configDir, serverConfig)
|
||||||
assert.Error(t, err)
|
assert.Error(t, err)
|
||||||
testfile := filepath.Join(os.TempDir(), "invalidkey")
|
testfile := filepath.Join(os.TempDir(), "invalidkey")
|
||||||
err = ioutil.WriteFile(testfile, []byte("some bytes"), 0666)
|
err = ioutil.WriteFile(testfile, []byte("some bytes"), 0666)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
c.HostKeys = []string{testfile}
|
c.HostKeys = []string{testfile}
|
||||||
err = c.checkAndLoadHostKeys("..", &ssh.ServerConfig{})
|
err = c.checkAndLoadHostKeys(configDir, serverConfig)
|
||||||
assert.Error(t, err)
|
assert.Error(t, err)
|
||||||
err = os.Remove(testfile)
|
err = os.Remove(testfile)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
keysDir := filepath.Join(os.TempDir(), "keys")
|
||||||
|
err = os.MkdirAll(keysDir, 0777)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
rsaKeyName := filepath.Join(keysDir, defaultPrivateRSAKeyName)
|
||||||
|
ecdsaKeyName := filepath.Join(keysDir, defaultPrivateECDSAKeyName)
|
||||||
|
nonDefaultKeyName := filepath.Join(keysDir, "akey")
|
||||||
|
c.HostKeys = []string{nonDefaultKeyName, rsaKeyName, ecdsaKeyName}
|
||||||
|
err = c.checkAndLoadHostKeys(configDir, serverConfig)
|
||||||
|
assert.Error(t, err)
|
||||||
|
assert.FileExists(t, rsaKeyName)
|
||||||
|
assert.FileExists(t, ecdsaKeyName)
|
||||||
|
assert.NoFileExists(t, nonDefaultKeyName)
|
||||||
|
err = os.Remove(rsaKeyName)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
err = os.Remove(ecdsaKeyName)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
if runtime.GOOS != osWindows {
|
||||||
|
err = os.Chmod(keysDir, 0551)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
c.HostKeys = nil
|
||||||
|
err = c.checkAndLoadHostKeys(keysDir, serverConfig)
|
||||||
|
assert.Error(t, err)
|
||||||
|
c.HostKeys = []string{rsaKeyName, ecdsaKeyName}
|
||||||
|
err = c.checkAndLoadHostKeys(configDir, serverConfig)
|
||||||
|
assert.Error(t, err)
|
||||||
|
c.HostKeys = []string{ecdsaKeyName, rsaKeyName}
|
||||||
|
err = c.checkAndLoadHostKeys(configDir, serverConfig)
|
||||||
|
assert.Error(t, err)
|
||||||
|
err = os.Chmod(keysDir, 0755)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
}
|
||||||
|
err = os.RemoveAll(keysDir)
|
||||||
|
assert.NoError(t, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestCertCheckerInitErrors(t *testing.T) {
|
func TestCertCheckerInitErrors(t *testing.T) {
|
||||||
@@ -1853,7 +1888,7 @@ func TestUpdateQuotaAfterRenameMissingFile(t *testing.T) {
|
|||||||
request := sftp.NewRequest("Rename", "/testfile")
|
request := sftp.NewRequest("Rename", "/testfile")
|
||||||
request.Filepath = "/dir"
|
request.Filepath = "/dir"
|
||||||
request.Target = path.Join("vdir", "dir")
|
request.Target = path.Join("vdir", "dir")
|
||||||
if runtime.GOOS != "windows" {
|
if runtime.GOOS != osWindows {
|
||||||
testDirPath := filepath.Join(mappedPath, "dir")
|
testDirPath := filepath.Join(mappedPath, "dir")
|
||||||
err := os.MkdirAll(testDirPath, 0777)
|
err := os.MkdirAll(testDirPath, 0777)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|||||||
@@ -513,15 +513,40 @@ func (c *Configuration) checkSSHCommands() {
|
|||||||
c.EnabledSSHCommands = sshCommands
|
c.EnabledSSHCommands = sshCommands
|
||||||
}
|
}
|
||||||
|
|
||||||
// If no host keys are defined we try to use or generate the default ones.
|
func (c *Configuration) checkHostKeyAutoGeneration(configDir string) error {
|
||||||
func (c *Configuration) checkAndLoadHostKeys(configDir string, serverConfig *ssh.ServerConfig) error {
|
for _, k := range c.HostKeys {
|
||||||
|
if filepath.IsAbs(k) {
|
||||||
|
if _, err := os.Stat(k); os.IsNotExist(err) {
|
||||||
|
keyName := filepath.Base(k)
|
||||||
|
switch keyName {
|
||||||
|
case defaultPrivateRSAKeyName:
|
||||||
|
logger.Info(logSender, "", "try to create non-existent host key %#v", k)
|
||||||
|
logger.InfoToConsole("try to create non-existent host key %#v", k)
|
||||||
|
err = utils.GenerateRSAKeys(k)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
case defaultPrivateECDSAKeyName:
|
||||||
|
logger.Info(logSender, "", "try to create non-existent host key %#v", k)
|
||||||
|
logger.InfoToConsole("try to create non-existent host key %#v", k)
|
||||||
|
err = utils.GenerateECDSAKeys(k)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
logger.Warn(logSender, "", "non-existent host key %#v will not be created", k)
|
||||||
|
logger.WarnToConsole("non-existent host key %#v will not be created", k)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
if len(c.HostKeys) == 0 {
|
if len(c.HostKeys) == 0 {
|
||||||
defaultKeys := []string{defaultPrivateRSAKeyName, defaultPrivateECDSAKeyName}
|
defaultKeys := []string{defaultPrivateRSAKeyName, defaultPrivateECDSAKeyName}
|
||||||
for _, k := range defaultKeys {
|
for _, k := range defaultKeys {
|
||||||
autoFile := filepath.Join(configDir, k)
|
autoFile := filepath.Join(configDir, k)
|
||||||
if _, err := os.Stat(autoFile); os.IsNotExist(err) {
|
if _, err := os.Stat(autoFile); os.IsNotExist(err) {
|
||||||
logger.Info(logSender, "", "No host keys configured and %#v does not exist; creating new key for server", autoFile)
|
logger.Info(logSender, "", "No host keys configured and %#v does not exist; try to create a new host key", autoFile)
|
||||||
logger.InfoToConsole("No host keys configured and %#v does not exist; creating new key for server", autoFile)
|
logger.InfoToConsole("No host keys configured and %#v does not exist; try to create a new host key", autoFile)
|
||||||
if k == defaultPrivateRSAKeyName {
|
if k == defaultPrivateRSAKeyName {
|
||||||
err = utils.GenerateRSAKeys(autoFile)
|
err = utils.GenerateRSAKeys(autoFile)
|
||||||
} else {
|
} else {
|
||||||
@@ -534,6 +559,14 @@ func (c *Configuration) checkAndLoadHostKeys(configDir string, serverConfig *ssh
|
|||||||
c.HostKeys = append(c.HostKeys, k)
|
c.HostKeys = append(c.HostKeys, k)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// If no host keys are defined we try to use or generate the default ones.
|
||||||
|
func (c *Configuration) checkAndLoadHostKeys(configDir string, serverConfig *ssh.ServerConfig) error {
|
||||||
|
if err := c.checkHostKeyAutoGeneration(configDir); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
for _, k := range c.HostKeys {
|
for _, k := range c.HostKeys {
|
||||||
hostKey := k
|
hostKey := k
|
||||||
if !utils.IsFileInputValid(hostKey) {
|
if !utils.IsFileInputValid(hostKey) {
|
||||||
|
|||||||
Reference in New Issue
Block a user