From 4f619848b1429c3d0ef638fb174d3e2929bea5d3 Mon Sep 17 00:00:00 2001 From: Rodrigo Navarro Date: Mon, 8 Jul 2024 11:26:54 -0400 Subject: [PATCH 1/6] fix for ansible master password at ansible config level --- .../groovy/com/rundeck/plugins/ansible/ansible/AnsibleVault.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/groovy/com/rundeck/plugins/ansible/ansible/AnsibleVault.java b/src/main/groovy/com/rundeck/plugins/ansible/ansible/AnsibleVault.java index b185ad0..4cda00c 100644 --- a/src/main/groovy/com/rundeck/plugins/ansible/ansible/AnsibleVault.java +++ b/src/main/groovy/com/rundeck/plugins/ansible/ansible/AnsibleVault.java @@ -81,6 +81,7 @@ public String encryptVariable(String key, Map env = new HashMap<>(); env.put("VAULT_ID_SECRET", masterPassword); + env.put("ANSIBLE_VAULT_PASSWORD_FILE", ""); Process proc = null; From 06951c38ff6ad790b275ff1db67ae26b5f13a779 Mon Sep 17 00:00:00 2001 From: Rodrigo Navarro Date: Mon, 8 Jul 2024 11:58:01 -0400 Subject: [PATCH 2/6] it lets the exception to propagate --- .../plugins/ansible/ansible/AnsibleVault.java | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/src/main/groovy/com/rundeck/plugins/ansible/ansible/AnsibleVault.java b/src/main/groovy/com/rundeck/plugins/ansible/ansible/AnsibleVault.java index 4cda00c..c84b52d 100644 --- a/src/main/groovy/com/rundeck/plugins/ansible/ansible/AnsibleVault.java +++ b/src/main/groovy/com/rundeck/plugins/ansible/ansible/AnsibleVault.java @@ -14,6 +14,7 @@ import java.nio.file.attribute.PosixFilePermission; import java.nio.file.attribute.PosixFilePermissions; import java.util.*; +import com.rundeck.plugins.ansible.ansible.AnsibleException; @Data @Builder @@ -56,7 +57,7 @@ public boolean checkAnsibleVault() { } public String encryptVariable(String key, - String content ) throws IOException { + String content ) throws IOException, AnsibleException, InterruptedException { List procArgs = new ArrayList<>(); String ansibleCommand = ANSIBLE_VAULT_COMMAND; @@ -98,10 +99,12 @@ public String encryptVariable(String key, final InputStream stdoutInputStream = proc.getInputStream(); final BufferedReader stdoutReader = new BufferedReader(new InputStreamReader(stdoutInputStream)); + int exitCode = proc.waitFor(); + String line1 = null; boolean capture = false; while ((line1 = stdoutReader.readLine()) != null) { - if (line1.toLowerCase().contains("!vault")) { + if (line1.toLowerCase().contains("!vault") || exitCode != 0) { capture = true; } if (capture) { @@ -109,18 +112,14 @@ public String encryptVariable(String key, } } - int exitCode = proc.waitFor(); - if (exitCode != 0) { - System.err.println("ERROR: encryptFileAnsibleVault:" + procArgs); - return null; + throw new AnsibleException(stringBuilder.toString(), AnsibleException.AnsibleFailureReason.IOFailure); } return stringBuilder.toString(); - } catch (Exception e) { - System.err.println("error encryptFileAnsibleVault file " + e.getMessage()); - return null; - } finally { + } catch(Exception e) { + throw e; + }finally { // Make sure to always cleanup on failure and success if (proc != null) { proc.destroy(); From fa6c820d08fd866da99898f01ccdf6a6c5b70d98 Mon Sep 17 00:00:00 2001 From: Rodrigo Navarro Date: Mon, 8 Jul 2024 14:56:07 -0400 Subject: [PATCH 3/6] add master password at ansible config level --- functional-test/src/test/resources/docker/ansible/ansible.cfg | 1 + functional-test/src/test/resources/docker/ansible/mp.pass | 1 + 2 files changed, 2 insertions(+) create mode 100644 functional-test/src/test/resources/docker/ansible/mp.pass diff --git a/functional-test/src/test/resources/docker/ansible/ansible.cfg b/functional-test/src/test/resources/docker/ansible/ansible.cfg index c8c55b5..1abf9e2 100644 --- a/functional-test/src/test/resources/docker/ansible/ansible.cfg +++ b/functional-test/src/test/resources/docker/ansible/ansible.cfg @@ -1,5 +1,6 @@ [defaults] inventory = /home/rundeck/ansible/inventory.ini +vault_password_file=/home/rundeck/ansible/mp.pass host_key_checking = False interpreter_python=/usr/bin/python3 show_custom_stats = False diff --git a/functional-test/src/test/resources/docker/ansible/mp.pass b/functional-test/src/test/resources/docker/ansible/mp.pass new file mode 100644 index 0000000..4632e06 --- /dev/null +++ b/functional-test/src/test/resources/docker/ansible/mp.pass @@ -0,0 +1 @@ +123456 \ No newline at end of file From c825ca1578b857ad0d62db375bf7dfe1eb06e395 Mon Sep 17 00:00:00 2001 From: Rodrigo Navarro Date: Mon, 8 Jul 2024 19:04:49 -0400 Subject: [PATCH 4/6] add test for node executor with password auth --- .../functional/BasicIntegrationSpec.groovy | 21 +++++++++- .../functional/EncryptedInventorySpec.groovy | 3 +- .../PluginGroupIntegrationSpec.groovy | 3 +- .../base/BaseTestConfiguration.groovy | 11 ++++-- .../resources/docker/ansible/inventory.ini | 1 + .../docker/ansible/rundeckNodes.yaml | 6 +++ .../test/resources/docker/docker-compose.yml | 11 +++--- .../files/acls/node-acl.aclpolicy | 8 ++++ .../files/etc/project.properties | 38 +++++++++++++++++++ ...b-f04f17a9-77cf-4feb-aec1-889a3de0f5ae.xml | 30 +++++++++++++++ 10 files changed, 120 insertions(+), 12 deletions(-) create mode 100644 functional-test/src/test/resources/docker/ansible/rundeckNodes.yaml create mode 100644 functional-test/src/test/resources/project-import/sshPasswordProject/rundeck-sshPasswordProject/files/acls/node-acl.aclpolicy create mode 100644 functional-test/src/test/resources/project-import/sshPasswordProject/rundeck-sshPasswordProject/files/etc/project.properties create mode 100644 functional-test/src/test/resources/project-import/sshPasswordProject/rundeck-sshPasswordProject/jobs/job-f04f17a9-77cf-4feb-aec1-889a3de0f5ae.xml diff --git a/functional-test/src/test/groovy/functional/BasicIntegrationSpec.groovy b/functional-test/src/test/groovy/functional/BasicIntegrationSpec.groovy index b00ecfc..7508cfe 100644 --- a/functional-test/src/test/groovy/functional/BasicIntegrationSpec.groovy +++ b/functional-test/src/test/groovy/functional/BasicIntegrationSpec.groovy @@ -10,10 +10,29 @@ import org.testcontainers.spock.Testcontainers class BasicIntegrationSpec extends BaseTestConfiguration { static String PROJ_NAME = 'ansible-test' + static String DEFAULT_NODE_NAME = "ssh-node" def setupSpec() { startCompose() - configureRundeck(PROJ_NAME) + configureRundeck(PROJ_NAME, DEFAULT_NODE_NAME) + } + + def "ansible node executor with ssh password"(){ + setup: + String ansibleNodeExecutorProjectName = "sshPasswordProject" + String nodeName = "ssh-node-password" + configureRundeck(ansibleNodeExecutorProjectName, nodeName) + when: + def jobId = "f04f17a9-77cf-4feb-aec1-889a3de0f5ae" + JobRun request = new JobRun() + request.loglevel = 'INFO' + def result = client.apiCall {api-> api.runJob(jobId, request)} + def executionState = waitForJob(result.id) + def logs = getLogs(result.id) + Map ansibleNodeExecutionStatus = TestUtil.getAnsibleNodeResult(logs) + then: + executionState!=null + executionState.getExecutionState()=="SUCCEEDED" } def "test simple inline playbook"(){ diff --git a/functional-test/src/test/groovy/functional/EncryptedInventorySpec.groovy b/functional-test/src/test/groovy/functional/EncryptedInventorySpec.groovy index e7cad1e..1b4046b 100644 --- a/functional-test/src/test/groovy/functional/EncryptedInventorySpec.groovy +++ b/functional-test/src/test/groovy/functional/EncryptedInventorySpec.groovy @@ -7,10 +7,11 @@ import org.testcontainers.spock.Testcontainers class EncryptedInventorySpec extends BaseTestConfiguration { static String PROJ_NAME = 'ansible-encrypted-inventory' + static String DEFAULT_NODE_NAME = "ssh-node" def setupSpec() { startCompose() - configureRundeck(PROJ_NAME) + configureRundeck(PROJ_NAME, DEFAULT_NODE_NAME) } def "test encrypted inventory"(){ diff --git a/functional-test/src/test/groovy/functional/PluginGroupIntegrationSpec.groovy b/functional-test/src/test/groovy/functional/PluginGroupIntegrationSpec.groovy index 09ca61c..f3ef63a 100644 --- a/functional-test/src/test/groovy/functional/PluginGroupIntegrationSpec.groovy +++ b/functional-test/src/test/groovy/functional/PluginGroupIntegrationSpec.groovy @@ -10,10 +10,11 @@ import org.testcontainers.spock.Testcontainers class PluginGroupIntegrationSpec extends BaseTestConfiguration { static String PROJ_NAME = 'ansible-plugin-group-test' + static String DEFAULT_NODE_NAME = "ssh-node" def setupSpec() { startCompose() - configureRundeck(PROJ_NAME) + configureRundeck(PROJ_NAME, DEFAULT_NODE_NAME) } def "test simple inline playbook"(){ diff --git a/functional-test/src/test/groovy/functional/base/BaseTestConfiguration.groovy b/functional-test/src/test/groovy/functional/base/BaseTestConfiguration.groovy index 47fd531..4ad0ba5 100644 --- a/functional-test/src/test/groovy/functional/base/BaseTestConfiguration.groovy +++ b/functional-test/src/test/groovy/functional/base/BaseTestConfiguration.groovy @@ -85,7 +85,7 @@ class BaseTestConfiguration extends Specification{ return logs } - def configureRundeck(String projectName){ + def configureRundeck(String projectName, String nodeName){ //add private key RequestBody requestBody = RequestBody.create(new File("src/test/resources/docker/keys/id_rsa"), Client.MEDIA_TYPE_OCTET_STREAM) @@ -125,15 +125,18 @@ class BaseTestConfiguration extends Specification{ api.importProjectArchive(projectName, "preserve", true, true, true, true, true, true, true, [:], body) ) - //wait for node to be available + waitForNodeAvailability(projectName, nodeName) + + } + + def waitForNodeAvailability(String projectName, String nodeName){ def result = client.apiCall {api-> api.listNodes(projectName,".*")} def count =0 - while(result.get("ssh-node")==null && count<5){ + while(result.get(nodeName)==null && count<5){ sleep(2000) result = client.apiCall {api-> api.listNodes(projectName,".*")} count++ } - } } diff --git a/functional-test/src/test/resources/docker/ansible/inventory.ini b/functional-test/src/test/resources/docker/ansible/inventory.ini index 10bf257..f019bb8 100644 --- a/functional-test/src/test/resources/docker/ansible/inventory.ini +++ b/functional-test/src/test/resources/docker/ansible/inventory.ini @@ -1,2 +1,3 @@ [servers] ssh-node ansible_host=ssh-node +ssh-node-password ansible_host=ssh-node ansible_connection=ssh ansible_user=rundeck ansible_ssh_pass=testpassword123 \ No newline at end of file diff --git a/functional-test/src/test/resources/docker/ansible/rundeckNodes.yaml b/functional-test/src/test/resources/docker/ansible/rundeckNodes.yaml new file mode 100644 index 0000000..bd46aec --- /dev/null +++ b/functional-test/src/test/resources/docker/ansible/rundeckNodes.yaml @@ -0,0 +1,6 @@ +ssh-node-password: + nodename: ssh-node-password + hostname: ssh-node + osFamily: Linux + username: rundeck + tags: '' \ No newline at end of file diff --git a/functional-test/src/test/resources/docker/docker-compose.yml b/functional-test/src/test/resources/docker/docker-compose.yml index f2942cd..e31a991 100644 --- a/functional-test/src/test/resources/docker/docker-compose.yml +++ b/functional-test/src/test/resources/docker/docker-compose.yml @@ -5,19 +5,19 @@ services: build: context: node environment: - NODE_USER_PASSWORD: ${NODE_USER_PASSWORD:-rundeck} + NODE_USER_PASSWORD: testpassword123 networks: - rundeck ports: - "2222:22" volumes: - - ${PWD}/keys:/configuration:rw + - ./keys:/configuration:rw rundeck: build: context: rundeck args: - RUNDECK_IMAGE: ${RUNDECK_IMAGE:-rundeck/rundeck:SNAPSHOT} + RUNDECK_IMAGE: rundeck/rundeck:SNAPSHOT image: rundeck-ansible-plugin:latest command: "-Dansible.debug=false" environment: @@ -30,9 +30,10 @@ services: networks: - rundeck ports: - - "4440" + - "4440:4440" volumes: - - ${PWD}/ansible:/home/rundeck/ansible:rw + - ./ansible:/home/rundeck/ansible:rw + - ./ansible/ansible.cfg:/etc/ansible/ansible.cfg:rw volumes: rundeck-data: diff --git a/functional-test/src/test/resources/project-import/sshPasswordProject/rundeck-sshPasswordProject/files/acls/node-acl.aclpolicy b/functional-test/src/test/resources/project-import/sshPasswordProject/rundeck-sshPasswordProject/files/acls/node-acl.aclpolicy new file mode 100644 index 0000000..b052b49 --- /dev/null +++ b/functional-test/src/test/resources/project-import/sshPasswordProject/rundeck-sshPasswordProject/files/acls/node-acl.aclpolicy @@ -0,0 +1,8 @@ +by: + urn: project:sshPasswordProject +for: + storage: + - match: + path: 'keys/project/sshPasswordProject/.*' + allow: [read] +description: Allow access to key storage \ No newline at end of file diff --git a/functional-test/src/test/resources/project-import/sshPasswordProject/rundeck-sshPasswordProject/files/etc/project.properties b/functional-test/src/test/resources/project-import/sshPasswordProject/rundeck-sshPasswordProject/files/etc/project.properties new file mode 100644 index 0000000..ba5c5de --- /dev/null +++ b/functional-test/src/test/resources/project-import/sshPasswordProject/rundeck-sshPasswordProject/files/etc/project.properties @@ -0,0 +1,38 @@ +#Mon Jul 08 21:50:51 GMT 2024 +#edit below +project.ansible-binaries-dir-path=/usr/local/bin/ +project.ansible-config-file-path=/home/rundeck/ansible/ansible.cfg +project.ansible-executable=/bin/bash +project.ansible-generate-inventory=true +project.ansible-ssh-auth-type=password +project.ansible-ssh-passphrase-option=option.password +project.ansible-ssh-password-storage-path=keys/project/sshPasswordProject/ssh-node.pass +project.ansible-ssh-user=rundeck +project.description= +project.disable.executions=false +project.disable.schedule=false +project.execution.history.cleanup.batch=500 +project.execution.history.cleanup.enabled=false +project.execution.history.cleanup.retention.days=60 +project.execution.history.cleanup.retention.minimum=50 +project.execution.history.cleanup.schedule=0 0 0 1/1 * ? * +project.jobs.gui.groupExpandLevel=1 +project.label= +project.later.executions.disable=false +project.later.executions.enable=false +project.later.schedule.disable=false +project.later.schedule.enable=false +project.name=sshPasswordProject +project.nodeCache.enabled=false +project.nodeCache.firstLoadSynch=true +project.output.allowUnsanitized=false +project.retry-counter=3 +project.ssh-authentication=privateKey +resources.source.1.type=local +resources.source.2.config.file=/home/rundeck/ansible/rundeckNodes.yaml +resources.source.2.config.format=resourceyaml +resources.source.2.config.generateFileAutomatically=true +resources.source.2.config.writeable=true +resources.source.2.type=file +service.FileCopier.default.provider=sshj-scp +service.NodeExecutor.default.provider=com.batix.rundeck.plugins.AnsibleNodeExecutor \ No newline at end of file diff --git a/functional-test/src/test/resources/project-import/sshPasswordProject/rundeck-sshPasswordProject/jobs/job-f04f17a9-77cf-4feb-aec1-889a3de0f5ae.xml b/functional-test/src/test/resources/project-import/sshPasswordProject/rundeck-sshPasswordProject/jobs/job-f04f17a9-77cf-4feb-aec1-889a3de0f5ae.xml new file mode 100644 index 0000000..85f104e --- /dev/null +++ b/functional-test/src/test/resources/project-import/sshPasswordProject/rundeck-sshPasswordProject/jobs/job-f04f17a9-77cf-4feb-aec1-889a3de0f5ae.xml @@ -0,0 +1,30 @@ + + + nodes + it targets a single ansible node + + true + false + ascending + false + 1 + + true + f04f17a9-77cf-4feb-aec1-889a3de0f5ae + INFO + simpleCommand + false + + name: ssh-node-password + + true + + true + + + whoami + + + f04f17a9-77cf-4feb-aec1-889a3de0f5ae + + \ No newline at end of file From ecb669f3d0841320a5613483378e63a27cc6a515 Mon Sep 17 00:00:00 2001 From: Rodrigo Navarro Date: Mon, 8 Jul 2024 19:28:19 -0400 Subject: [PATCH 5/6] change encrypt to use --encrypt-vault-id --- .../com/rundeck/plugins/ansible/ansible/AnsibleVault.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/groovy/com/rundeck/plugins/ansible/ansible/AnsibleVault.java b/src/main/groovy/com/rundeck/plugins/ansible/ansible/AnsibleVault.java index c84b52d..05dd9dd 100644 --- a/src/main/groovy/com/rundeck/plugins/ansible/ansible/AnsibleVault.java +++ b/src/main/groovy/com/rundeck/plugins/ansible/ansible/AnsibleVault.java @@ -66,8 +66,8 @@ public String encryptVariable(String key, } procArgs.add(ansibleCommand); procArgs.add("encrypt_string"); - procArgs.add("--vault-id"); - procArgs.add("internal-encrypt@" + vaultPasswordScriptFile.getAbsolutePath()); + procArgs.add("--encrypt-vault-id"); + procArgs.add("internal-encrypt"); if(debug){ System.out.println("encryptVariable " + key + ": " + procArgs); @@ -82,7 +82,7 @@ public String encryptVariable(String key, Map env = new HashMap<>(); env.put("VAULT_ID_SECRET", masterPassword); - env.put("ANSIBLE_VAULT_PASSWORD_FILE", ""); + env.put("ANSIBLE_VAULT_IDENTITY_LIST", "internal-encrypt@" + vaultPasswordScriptFile.getAbsolutePath()); Process proc = null; From 835da7804080cdf3507869c46fdfca0b5c07c368 Mon Sep 17 00:00:00 2001 From: Rodrigo Navarro Date: Tue, 9 Jul 2024 09:48:13 -0400 Subject: [PATCH 6/6] remove unnecesary port mapping --- functional-test/src/test/resources/docker/docker-compose.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/functional-test/src/test/resources/docker/docker-compose.yml b/functional-test/src/test/resources/docker/docker-compose.yml index e31a991..e5ad23a 100644 --- a/functional-test/src/test/resources/docker/docker-compose.yml +++ b/functional-test/src/test/resources/docker/docker-compose.yml @@ -30,7 +30,7 @@ services: networks: - rundeck ports: - - "4440:4440" + - "4440" volumes: - ./ansible:/home/rundeck/ansible:rw - ./ansible/ansible.cfg:/etc/ansible/ansible.cfg:rw