From ffb2e2a6decac4e66409c18d28c211ca91ca4447 Mon Sep 17 00:00:00 2001 From: Nick Craig-Wood Date: Tue, 23 Jul 2024 17:13:28 +0100 Subject: [PATCH] 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 --- docs/content/docs.md | 3 ++- fs/config/config_test.go | 5 ++++- fs/config/crypt.go | 14 +++++++++++++- fs/config/crypt_internal_test.go | 31 +++++++++++++++++++++++++++++++ 4 files changed, 50 insertions(+), 3 deletions(-) diff --git a/docs/content/docs.md b/docs/content/docs.md index d5e47e2bd..629bd84e8 100644 --- a/docs/content/docs.md +++ b/docs/content/docs.md @@ -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 diff --git a/fs/config/config_test.go b/fs/config/config_test.go index 612105a6c..7c4a503bf 100644 --- a/fs/config/config_test.go +++ b/fs/config/config_test.go @@ -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) diff --git a/fs/config/crypt.go b/fs/config/crypt.go index ad42640b0..f84b3dda1 100644 --- a/fs/config/crypt.go +++ b/fs/config/crypt.go @@ -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 diff --git a/fs/config/crypt_internal_test.go b/fs/config/crypt_internal_test.go index 05dc71e0f..9aa813cb3 100644 --- a/fs/config/crypt_internal_test.go +++ b/fs/config/crypt_internal_test.go @@ -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) +}