config: use --password-command to set config file password if supplied

Before this change, rclone ignored the --password-command on the
rclone config setting except when decrypting an existing config file.

This change allows for offloading the password storage/generation into
external hardware key or other protected password storage.

Fixes #7859
This commit is contained in:
Nick Craig-Wood 2024-07-23 17:13:28 +01:00
parent c9c283533c
commit ffb2e2a6de
4 changed files with 50 additions and 3 deletions

View file

@ -1928,7 +1928,8 @@ The default is `.partial`.
This flag supplies a program which should supply the config password
when run. This is an alternative to rclone prompting for the password
or setting the `RCLONE_CONFIG_PASS` variable.
or setting the `RCLONE_CONFIG_PASS` variable. It is also used when
setting the config password for the first time.
The argument to this should be a command with a space separated list
of arguments. If one of the arguments has a space in then enclose it

View file

@ -10,6 +10,10 @@ import (
"github.com/stretchr/testify/assert"
)
func init() {
configfile.Install()
}
func TestConfigLoad(t *testing.T) {
oldConfigPath := config.GetConfigPath()
assert.NoError(t, config.SetConfigPath("./testdata/plain.conf"))
@ -17,7 +21,6 @@ func TestConfigLoad(t *testing.T) {
assert.NoError(t, config.SetConfigPath(oldConfigPath))
}()
config.ClearConfigPassword()
configfile.Install()
sections := config.Data().GetSectionList()
var expect = []string{"RCLONE_ENCRYPT_V0", "nounc", "unc"}
assert.Equal(t, expect, sections)

View file

@ -310,8 +310,20 @@ func ClearConfigPassword() {
// changeConfigPassword will query the user twice
// for a password. If the same password is entered
// twice the key is updated.
//
// This will use --password-command if configured to read the password.
func changeConfigPassword() {
err := SetConfigPassword(ChangePassword("NEW configuration"))
pass, err := GetPasswordCommand(context.Background())
if err != nil {
fmt.Printf("Failed to read new password with --password-command: %v\n", err)
return
}
if pass == "" {
pass = ChangePassword("NEW configuration")
} else {
fmt.Printf("Read password using --password-command\n")
}
err = SetConfigPassword(pass)
if err != nil {
fmt.Printf("Failed to set config password: %v\n", err)
return

View file

@ -1,8 +1,10 @@
package config
import (
"context"
"testing"
"github.com/rclone/rclone/fs"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
@ -46,3 +48,32 @@ func TestPassword(t *testing.T) {
hashedKeyCompare(t, "abcdef", "ABCDEF", false)
}
func TestChangeConfigPassword(t *testing.T) {
ci := fs.GetConfig(context.Background())
var err error
oldConfigPath := GetConfigPath()
assert.NoError(t, SetConfigPath("./testdata/encrypted.conf"))
defer func() {
assert.NoError(t, SetConfigPath(oldConfigPath))
ClearConfigPassword()
ci.PasswordCommand = nil
}()
// Get rid of any config password
ClearConfigPassword()
// Set correct password using --password command
ci.PasswordCommand = fs.SpaceSepList{"echo", "asdf"}
changeConfigPassword()
err = Data().Load()
require.NoError(t, err)
sections := Data().GetSectionList()
var expect = []string{"nounc", "unc"}
assert.Equal(t, expect, sections)
keys := Data().GetKeyList("nounc")
expect = []string{"type", "nounc"}
assert.Equal(t, expect, keys)
}