Skip to content

Commit

Permalink
Merge pull request #373 from rundeck-plugins/RUN-2566
Browse files Browse the repository at this point in the history
RUN-2566: add a new mechanisms to wait from the prompt of vault-ids
  • Loading branch information
ltamaster authored Jun 10, 2024
2 parents 38636b5 + d027e48 commit cd08bcf
Show file tree
Hide file tree
Showing 8 changed files with 217 additions and 38 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -258,4 +258,33 @@ class BasicIntegrationSpec extends BaseTestConfiguration {
logs.findAll {it.log.contains("\"token\": 13231232312321321321321")}.size() == 1
}

def "test use encrypted user file with password authentication"(){
when:

def jobId = "0ea27de5-ef36-4a2f-b09c-1bd548eb78d4"

JobRun request = new JobRun()
request.loglevel = 'DEBUG'

def result = client.apiCall {api-> api.runJob(jobId, request)}
def executionId = result.id

def executionState = waitForJob(executionId)

def logs = getLogs(executionId)
Map<String, Integer> 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("encryptVariable ansible_ssh_password:")}.size() == 1
logs.findAll {it.log.contains("\"environmentTest\": \"test\"")}.size() == 1
logs.findAll {it.log.contains("\"token\": 13231232312321321321321")}.size() == 1
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ RUN apt-get -y install sshpass && \
apt-get -y install sudo && \
pip3 install --upgrade pip

RUN pip3 install ansible
RUN pip3 install ansible==9.6.0

RUN ln -s /usr/bin/python3 /usr/bin/python

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
<joblist>
<job>
<defaultTab>nodes</defaultTab>
<description></description>
<dispatch>
<excludePrecedence>true</excludePrecedence>
<keepgoing>false</keepgoing>
<rankOrder>ascending</rankOrder>
<successOnEmptyNodeFilter>false</successOnEmptyNodeFilter>
<threadcount>1</threadcount>
</dispatch>
<executionEnabled>true</executionEnabled>
<group>Ansible</group>
<id>0ea27de5-ef36-4a2f-b09c-1bd548eb78d4</id>
<loglevel>INFO</loglevel>
<name>simple-inline-playbook-user-encryption-and-ssh-pass</name>
<nodeFilterEditable>false</nodeFilterEditable>
<nodefilters>
<filter>name: ssh-node </filter>
</nodefilters>
<nodesSelectedByDefault>true</nodesSelectedByDefault>
<plugins />
<scheduleEnabled>true</scheduleEnabled>
<sequence keepgoing='false' strategy='node-first'>
<command>
<node-step-plugin type='com.batix.rundeck.plugins.AnsiblePlaybookInlineWorkflowNodeStep'>
<configuration>
<entry key='ansible-become' value='false' />
<entry key='ansible-encrypt-extra-vars' value='false' />
<entry key='ansible-extra-param' value='--extra-vars=@/home/rundeck/ansible/user-encrypted-env-vars.yaml' />
<entry key='ansible-playbook-inline' value='- hosts: all&#10; gather_facts: false&#10; tasks:&#10;&#10; - name: Hello World!&#10; debug:&#10; msg: "Hello World!"&#10; - name: wait&#10; shell: "sleep 15"&#10; register: sh_output&#10; &#10; - name: Get Disk Space&#10; shell: "df -h"&#10; register: sh_output&#10;&#10;&#10; &#10; - debug: msg={{hostvars[inventory_hostname]}}&#10; - debug: var=sh_output.stdout_lines&#10; - debug: msg="{{ username }}"&#10; - debug: msg="{{ token }}"&#10; - debug: msg="{{ environmentTest }}"&#10;' />
<entry key='ansible-ssh-auth-type' value='password' />
<entry key='ansible-ssh-passphrase-option' value='option.password' />
<entry key='ansible-ssh-password-storage-path' value='keys/project/ansible-test/ssh-node.pass' />
<entry key='ansible-ssh-use-agent' value='false' />
<entry key='ansible-ssh-user' value='rundeck' />
<entry key='ansible-vault-storage-path' value='keys/project/ansible-test/vault-user.pass' />
</configuration>
</node-step-plugin>
</command>
</sequence>
<uuid>0ea27de5-ef36-4a2f-b09c-1bd548eb78d4</uuid>
</job>
</joblist>
Original file line number Diff line number Diff line change
Expand Up @@ -290,6 +290,7 @@ public static AnsibleRunner buildAnsibleRunner(AnsibleRunnerContextBuilder conte
File tempVaultFile ;
File tempSshVarsFile ;
File tempBecameVarsFile ;
File vaultPromptFile;

public void deleteTempDirectory(Path tempDirectory) throws IOException {
Files.walkFileTree(tempDirectory, new SimpleFileVisitor<Path>() {
Expand Down Expand Up @@ -337,7 +338,6 @@ public int run() throws Exception {

if(ansibleVault==null){
tempInternalVaultFile = AnsibleVault.createVaultScriptAuth("ansible-script-vault");

ansibleVault = AnsibleVault.builder()
.baseDirectory(baseDirectory)
.masterPassword(AnsibleUtil.randomString())
Expand Down Expand Up @@ -387,12 +387,7 @@ public int run() throws Exception {

useAnsibleVault = ansibleVault.checkAnsibleVault();

if(useAnsibleVault) {
tempInternalVaultFile = ansibleVault.getVaultPasswordScriptFile();

procArgs.add("--vault-id");
procArgs.add("internal-encrypt@" + tempInternalVaultFile.getAbsolutePath());
}else{
if(!useAnsibleVault) {
System.err.println("WARN: ansible-vault is not installed, extra-vars will not be encrypted.");
}
}
Expand Down Expand Up @@ -431,12 +426,6 @@ public int run() throws Exception {
procArgs.add("--extra-vars" + "=" + "@" + tempVarsFile.getAbsolutePath());
}

if (vaultPass != null && !vaultPass.isEmpty()) {
tempVaultFile = ansibleVault.getVaultPasswordScriptFile();
procArgs.add("--vault-id");
procArgs.add(tempVaultFile.getAbsolutePath());
}

if (sshPrivateKey != null && !sshPrivateKey.isEmpty()) {
String privateKeyData = sshPrivateKey.replaceAll("\r\n", "\n");
tempPkFile = AnsibleUtil.createTemporaryFile("id_rsa", privateKeyData);
Expand Down Expand Up @@ -506,16 +495,14 @@ public int run() throws Exception {
procArgs.addAll(tokenizeCommand(extraParams));
}

if (debug) {
System.out.println(" procArgs: " + procArgs);
}

if(processExecutorBuilder==null){
processExecutorBuilder = ProcessExecutor.builder();
}

//set main process command
processExecutorBuilder.procArgs(procArgs);
if (debug) {
System.out.println(" procArgs: " + procArgs);
processExecutorBuilder.debug(true);
}

if (baseDirectory != null) {
processExecutorBuilder.baseDirectory(baseDirectory.toFile());
Expand All @@ -540,20 +527,51 @@ public int run() throws Exception {
processEnvironment.put("SSH_AUTH_SOCK", this.sshAgent.getSocketPath());
}

processExecutorBuilder.environmentVariables(processEnvironment);

//set STDIN variables
List<String> stdinVariables = new ArrayList<>();
List<VaultPrompt> stdinVariables = new ArrayList<>();

if(useAnsibleVault || vaultPass != null ){
vaultPromptFile = File.createTempFile("vault-prompt", ".log");
}

if (useAnsibleVault) {
stdinVariables.add(ansibleVault.getMasterPassword() + "\n");
VaultPrompt vaultPrompt = VaultPrompt.builder()
.vaultId("internal-encrypt")
.vaultPassword(ansibleVault.getMasterPassword() + "\n")
.build();

stdinVariables.add(vaultPrompt);
processEnvironment.put("LOG_PATH", vaultPromptFile.getAbsolutePath());

tempInternalVaultFile = ansibleVault.getVaultPasswordScriptFile();

procArgs.add("--vault-id");
procArgs.add("internal-encrypt@" + tempInternalVaultFile.getAbsolutePath());
}

if (vaultPass != null && !vaultPass.isEmpty()) {
stdinVariables.add(vaultPass + "\n");
VaultPrompt vaultPrompt = VaultPrompt.builder()
.vaultId("None")
.vaultPassword(vaultPass + "\n")
.build();

stdinVariables.add(vaultPrompt);
processEnvironment.putIfAbsent("LOG_PATH", vaultPromptFile.getAbsolutePath());

tempVaultFile = ansibleVault.getVaultPasswordScriptFile();
procArgs.add("--vault-id");
procArgs.add(tempVaultFile.getAbsolutePath());
}

//set main process command
processExecutorBuilder.procArgs(procArgs);
processExecutorBuilder.stdinVariables(stdinVariables);
processExecutorBuilder.environmentVariables(processEnvironment);

//set vault prompt file
if(vaultPromptFile !=null){
processExecutorBuilder.promptStdinLogFile(vaultPromptFile);
}

proc = processExecutorBuilder.build().run();

Expand Down Expand Up @@ -627,6 +645,10 @@ public int run() throws Exception {
tempInternalVaultFile.deleteOnExit();
}

if(vaultPromptFile != null && !vaultPromptFile.delete()){
vaultPromptFile.deleteOnExit();
}

if (usingTempDirectory && !retainTempDirectory) {
deleteTempDirectory(baseDirectory);
}
Expand Down Expand Up @@ -668,9 +690,13 @@ public boolean registerKeySshAgent(String keyPath) throws Exception {
env.put("SSH_ASKPASS", tempPassVarsFile.getAbsolutePath());
}

List<String> stdinVariables = new ArrayList<>();
List<VaultPrompt> stdinVariables = new ArrayList<>();
if (sshPassphrase != null && !sshPassphrase.isEmpty()) {
stdinVariables.add(sshPassphrase + "\n");
VaultPrompt sshPassPrompt = VaultPrompt.builder()
.vaultPassword(sshPassphrase + "\n")
.build();

stdinVariables.add(sshPassPrompt);
}

ProcessExecutor processExecutor = ProcessExecutor.builder()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import com.rundeck.plugins.ansible.util.AnsibleUtil;
import com.rundeck.plugins.ansible.util.ProcessExecutor;
import com.rundeck.plugins.ansible.util.VaultPrompt;
import lombok.Builder;
import lombok.Data;

Expand Down Expand Up @@ -72,8 +73,11 @@ public String encryptVariable(String key,
}

//send values to STDIN in order
List<String> stdinVariables = new ArrayList<>();
stdinVariables.add(content);
List<VaultPrompt> stdinVariables = new ArrayList<>();
VaultPrompt vaultPrompt = VaultPrompt.builder()
.vaultPassword(content)
.build();
stdinVariables.add(vaultPrompt);

Map<String, String> env = new HashMap<>();
env.put("VAULT_ID_SECRET", masterPassword);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,8 @@

import lombok.Builder;

import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.*;
import java.util.List;
import java.io.File;
import java.util.Map;

@Builder
Expand All @@ -18,12 +15,17 @@ public class ProcessExecutor {

private Map<String, String> environmentVariables;

private List<String> stdinVariables;
private List<VaultPrompt> stdinVariables;

private boolean redirectErrorStream;

private File promptStdinLogFile;

private boolean debug;


public Process run() throws IOException {

ProcessBuilder processBuilder = new ProcessBuilder()
.command(procArgs)
.redirectErrorStream(redirectErrorStream);
Expand All @@ -32,6 +34,7 @@ public Process run() throws IOException {
processBuilder.directory(baseDirectory);
}


if(environmentVariables!=null){
Map<String, String> processEnvironment = processBuilder.environment();

Expand All @@ -47,19 +50,59 @@ public Process run() throws IOException {

if (stdinVariables != null) {
try {

for (String stdinVariable : stdinVariables) {
stdinw.write(stdinVariable);
for (VaultPrompt stdinVariable : stdinVariables) {
processPrompt(stdinw, stdinVariable);
}
stdinw.flush();
} catch (Exception e) {
System.err.println("error encryptFileAnsibleVault file " + e.getMessage());
}
}

stdinw.close();
stdin.close();

return proc;
}

private void processPrompt(OutputStreamWriter stdinw, final VaultPrompt vaultPrompt) throws Exception {
if(promptStdinLogFile!=null){
Thread stdinThread = new Thread(() -> {
try {
stdinw.write(vaultPrompt.getVaultPassword());
stdinw.flush();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
);

//wait for prompt
boolean promptFound = false;
long start = System.currentTimeMillis();
long end = start + 60 * 1000;
BufferedReader reader = new BufferedReader(new FileReader(promptStdinLogFile));

while (!promptFound && System.currentTimeMillis() < end){
String currentLine = reader.readLine();
if(debug){
System.out.println("waiting for vault password prompt ("+vaultPrompt.getVaultId()+")...");
}
if(currentLine!=null && currentLine.contains("Enter Password ("+vaultPrompt.getVaultId()+"):")){
if(debug) {
System.out.println(currentLine);
}
promptFound = true;
//send password / content
stdinThread.start();
}
Thread.sleep(2000);
}
reader.close();

}else{
stdinw.write(vaultPrompt.getVaultPassword());
stdinw.flush();
}
}

}
13 changes: 13 additions & 0 deletions src/main/groovy/com/rundeck/plugins/ansible/util/VaultPrompt.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package com.rundeck.plugins.ansible.util;

import lombok.Builder;
import lombok.Data;

@Builder
@Data
public class VaultPrompt {

private String vaultId;
private String vaultPassword;

}
Loading

0 comments on commit cd08bcf

Please sign in to comment.