From d280591bb182a33e22774b090b01556e4991a4c1 Mon Sep 17 00:00:00 2001 From: Sebastian Luksch Date: Tue, 9 Jul 2024 09:32:54 +0200 Subject: [PATCH 1/2] add possibility to use multi line password files for ansible vault --- .../com/rundeck/plugins/ansible/ansible/AnsibleRunner.java | 4 ++-- .../plugins/ansible/plugin/AnsibleResourceModelSource.java | 2 +- .../com/rundeck/plugins/ansible/util/ProcessExecutor.java | 1 + src/main/resources/vault-client.py | 7 ++++++- 4 files changed, 10 insertions(+), 4 deletions(-) diff --git a/src/main/groovy/com/rundeck/plugins/ansible/ansible/AnsibleRunner.java b/src/main/groovy/com/rundeck/plugins/ansible/ansible/AnsibleRunner.java index b8a463f3..49502926 100644 --- a/src/main/groovy/com/rundeck/plugins/ansible/ansible/AnsibleRunner.java +++ b/src/main/groovy/com/rundeck/plugins/ansible/ansible/AnsibleRunner.java @@ -537,7 +537,7 @@ public int run() throws Exception { if (useAnsibleVault) { VaultPrompt vaultPrompt = VaultPrompt.builder() .vaultId("internal-encrypt") - .vaultPassword(ansibleVault.getMasterPassword() + "\n") + .vaultPassword(ansibleVault.getMasterPassword()) .build(); stdinVariables.add(vaultPrompt); @@ -552,7 +552,7 @@ public int run() throws Exception { if (vaultPass != null && !vaultPass.isEmpty()) { VaultPrompt vaultPrompt = VaultPrompt.builder() .vaultId("None") - .vaultPassword(vaultPass + "\n") + .vaultPassword(vaultPass) .build(); stdinVariables.add(vaultPrompt); diff --git a/src/main/groovy/com/rundeck/plugins/ansible/plugin/AnsibleResourceModelSource.java b/src/main/groovy/com/rundeck/plugins/ansible/plugin/AnsibleResourceModelSource.java index db252f8c..d76f01e3 100644 --- a/src/main/groovy/com/rundeck/plugins/ansible/plugin/AnsibleResourceModelSource.java +++ b/src/main/groovy/com/rundeck/plugins/ansible/plugin/AnsibleResourceModelSource.java @@ -719,7 +719,7 @@ public String getNodesFromInventory(AnsibleRunner.AnsibleRunnerBuilder runnerBui if(runner.getVaultPass() != null){ VaultPrompt vaultPrompt = VaultPrompt.builder() .vaultId("None") - .vaultPassword(runner.getVaultPass() + "\n") + .vaultPassword(runner.getVaultPass()) .build(); ansibleInventoryListBuilder.vaultPrompt(vaultPrompt); } diff --git a/src/main/groovy/com/rundeck/plugins/ansible/util/ProcessExecutor.java b/src/main/groovy/com/rundeck/plugins/ansible/util/ProcessExecutor.java index 599e8ef9..d4e2e2f7 100644 --- a/src/main/groovy/com/rundeck/plugins/ansible/util/ProcessExecutor.java +++ b/src/main/groovy/com/rundeck/plugins/ansible/util/ProcessExecutor.java @@ -69,6 +69,7 @@ private void processPrompt(OutputStreamWriter stdinw, final VaultPrompt vaultPro Thread stdinThread = new Thread(() -> { try { stdinw.write(vaultPrompt.getVaultPassword()); + stdinw.write(3); // end of text stdinw.flush(); } catch (IOException e) { throw new RuntimeException(e); diff --git a/src/main/resources/vault-client.py b/src/main/resources/vault-client.py index 3e59c5dc..0e5db613 100755 --- a/src/main/resources/vault-client.py +++ b/src/main/resources/vault-client.py @@ -32,7 +32,12 @@ if sys.stdin.isatty(): secret = getpass.getpass() else: - secret = sys.stdin.readline().rstrip() + secret = '' + while 1: + c = sys.stdin.read(1) + if c == chr(3) or len(c) == 0: # end of text or nothing read + break + secret = secret + c if secret is None: sys.stderr.write('ERROR: secret is not set\n') From f6c1495b2348324e49bdcbb8e351722dfb1acdd8 Mon Sep 17 00:00:00 2001 From: Sebastian Luksch Date: Tue, 9 Jul 2024 12:10:01 +0200 Subject: [PATCH 2/2] add testcase for multiline password file --- .../functional/BasicIntegrationSpec.groovy | 28 ++++++++++++ .../base/BaseTestConfiguration.groovy | 5 +++ ...encrypted-env-vars-multiline-password.yaml | 10 +++++ ...b-198b7deb-2ba2-4ac6-a80e-94f06ab7fb62.xml | 45 +++++++++++++++++++ 4 files changed, 88 insertions(+) create mode 100644 functional-test/src/test/resources/docker/ansible/user-encrypted-env-vars-multiline-password.yaml create mode 100644 functional-test/src/test/resources/project-import/ansible-test/rundeck-ansible-test/jobs/job-198b7deb-2ba2-4ac6-a80e-94f06ab7fb62.xml diff --git a/functional-test/src/test/groovy/functional/BasicIntegrationSpec.groovy b/functional-test/src/test/groovy/functional/BasicIntegrationSpec.groovy index b00ecfca..5d224c6e 100644 --- a/functional-test/src/test/groovy/functional/BasicIntegrationSpec.groovy +++ b/functional-test/src/test/groovy/functional/BasicIntegrationSpec.groovy @@ -287,4 +287,32 @@ class BasicIntegrationSpec extends BaseTestConfiguration { logs.findAll {it.log.contains("\"token\": 13231232312321321321321")}.size() == 1 } + def "test use encrypted user file with multiline password file"(){ + when: + + def jobId = "198b7deb-2ba2-4ac6-a80e-94f06ab7fb62" + + JobRun request = new JobRun() + request.loglevel = 'INFO' + + def result = client.apiCall {api-> api.runJob(jobId, request)} + def executionId = result.id + + def executionState = waitForJob(executionId) + + def logs = getLogs(executionId) + Map ansibleNodeExecutionStatus = TestUtil.getAnsibleNodeResult(logs) + + then: + executionState!=null + executionState.getExecutionState()=="SUCCEEDED" + ansibleNodeExecutionStatus.get("ok")!=0 + ansibleNodeExecutionStatus.get("unreachable")==0 + ansibleNodeExecutionStatus.get("failed")==0 + ansibleNodeExecutionStatus.get("skipped")==0 + ansibleNodeExecutionStatus.get("ignored")==0 + logs.findAll {it.log.contains("\"environmentTest\": \"someOtherTest\"")}.size() == 1 + logs.findAll {it.log.contains("\"token\": \"someOtherToken\"")}.size() == 1 + } + } diff --git a/functional-test/src/test/groovy/functional/base/BaseTestConfiguration.groovy b/functional-test/src/test/groovy/functional/base/BaseTestConfiguration.groovy index 47fd531f..1048be90 100644 --- a/functional-test/src/test/groovy/functional/base/BaseTestConfiguration.groovy +++ b/functional-test/src/test/groovy/functional/base/BaseTestConfiguration.groovy @@ -23,6 +23,7 @@ class BaseTestConfiguration extends Specification{ public static final String NODE_KEY_PASSPHRASE = "testpassphrase123" public static final String USER_VAULT_PASSWORD = "vault123" public static final String ENCRYPTED_INVENTORY_VAULT_PASSWORD = "123456" + public static final String USER_VAULT_PASSWORD_FILE_MULTILINE = "multiline\npassword\n" def startCompose() { if(rundeckEnvironment==null){ @@ -107,6 +108,10 @@ class BaseTestConfiguration extends Specification{ requestBody = RequestBody.create(USER_VAULT_PASSWORD.getBytes(), Client.MEDIA_TYPE_X_RUNDECK_PASSWORD) keyResult = client.apiCall {api-> api.createKeyStorage("project/$projectName/vault-user.pass", requestBody)} + //user vault password + requestBody = RequestBody.create(USER_VAULT_PASSWORD_FILE_MULTILINE.getBytes(), Client.MEDIA_TYPE_X_RUNDECK_PASSWORD) + keyResult = client.apiCall {api-> api.createKeyStorage("project/$projectName/vault-multiline.pass", requestBody)} + //add encrypted inventory password requestBody = RequestBody.create(ENCRYPTED_INVENTORY_VAULT_PASSWORD.getBytes(), Client.MEDIA_TYPE_X_RUNDECK_PASSWORD) keyResult = client.apiCall {api-> api.createKeyStorage("project/$projectName/vault-inventory.password", requestBody)} diff --git a/functional-test/src/test/resources/docker/ansible/user-encrypted-env-vars-multiline-password.yaml b/functional-test/src/test/resources/docker/ansible/user-encrypted-env-vars-multiline-password.yaml new file mode 100644 index 00000000..11f4042f --- /dev/null +++ b/functional-test/src/test/resources/docker/ansible/user-encrypted-env-vars-multiline-password.yaml @@ -0,0 +1,10 @@ +$ANSIBLE_VAULT;1.1;AES256 +39616539663235646532393064306563383930626134643465386664646566616534346366386537 +6361326636333761396565613233356635313465646530390a616332663532653731316263366566 +64383064383736383261646434666330343934313534393062353366613730353865303338613662 +3066623366643033390a356165393935636231346239646562313062663532326461333139656439 +37343636373162643164386663623563346230363830663864313435383561343036333662613131 +30306534336463623266333933393730643838386137376265323565346261346565353031616531 +64663665616430626631363666363834313765623833323830643537633162376632363931356161 +34333966623833396434366531666431626634373530396366356434303764383839353934663833 +6333 diff --git a/functional-test/src/test/resources/project-import/ansible-test/rundeck-ansible-test/jobs/job-198b7deb-2ba2-4ac6-a80e-94f06ab7fb62.xml b/functional-test/src/test/resources/project-import/ansible-test/rundeck-ansible-test/jobs/job-198b7deb-2ba2-4ac6-a80e-94f06ab7fb62.xml new file mode 100644 index 00000000..679b7ac6 --- /dev/null +++ b/functional-test/src/test/resources/project-import/ansible-test/rundeck-ansible-test/jobs/job-198b7deb-2ba2-4ac6-a80e-94f06ab7fb62.xml @@ -0,0 +1,45 @@ + + + nodes + + + true + false + ascending + false + 1 + + true + Ansible + 198b7deb-2ba2-4ac6-a80e-94f06ab7fb62 + INFO + simple-inline-playbook-user-encryption-multiline-password + false + + name: ssh-node + + true + + true + + + + + + + + + + + + + + + + + + + + 198b7deb-2ba2-4ac6-a80e-94f06ab7fb62 + + \ No newline at end of file