Skip to content

Commit

Permalink
add key storage support for resource model
Browse files Browse the repository at this point in the history
add  ProxySecretBundleCreator to resource model
  • Loading branch information
ltamaster committed Jul 14, 2023
1 parent 2ec0714 commit a2237d5
Show file tree
Hide file tree
Showing 2 changed files with 139 additions and 5 deletions.
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
package com.rundeck.plugins.ansible.plugin;

import com.dtolabs.rundeck.core.execution.proxy.DefaultSecretBundle;
import com.dtolabs.rundeck.core.execution.proxy.ProxySecretBundleCreator;
import com.dtolabs.rundeck.core.execution.proxy.SecretBundle;
import com.dtolabs.rundeck.core.storage.ResourceMeta;
import com.dtolabs.rundeck.core.storage.StorageTree;
import com.dtolabs.rundeck.core.storage.keys.KeyStorageTree;
import com.rundeck.plugins.ansible.ansible.AnsibleDescribable;
import com.rundeck.plugins.ansible.ansible.AnsibleDescribable.AuthenticationType;
import com.rundeck.plugins.ansible.ansible.AnsibleException;
Expand All @@ -18,8 +24,12 @@
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import com.google.gson.JsonPrimitive;
import org.rundeck.app.spi.Services;
import org.rundeck.storage.api.PathUtil;
import org.rundeck.storage.api.StorageException;

import java.io.BufferedReader;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.nio.charset.Charset;
Expand All @@ -28,10 +38,12 @@
import java.util.*;
import java.util.Map.Entry;

public class AnsibleResourceModelSource implements ResourceModelSource {
public class AnsibleResourceModelSource implements ResourceModelSource, ProxySecretBundleCreator {

private Framework framework;

private Services services;

private String project;
private String sshAuthType;

Expand All @@ -55,6 +67,9 @@ public class AnsibleResourceModelSource implements ResourceModelSource {
protected Boolean sshUsePassword;
protected String sshPassword;
protected String sshPrivateKeyFile;

protected String sshPasswordPath;
protected String sshPrivateKeyPath;
protected String sshPass;
protected Integer sshTimeout;

Expand Down Expand Up @@ -99,6 +114,10 @@ private static Boolean skipVar(final String hostVar, final List<String> varList)
return false;
}

public void setServices(Services services) {
this.services = services;
}

public void configure(Properties configuration) throws ConfigurationException {

project = configuration.getProperty("project");
Expand Down Expand Up @@ -156,6 +175,10 @@ public void configure(Properties configuration) throws ConfigurationException {

extraParameters = (String) resolveProperty(AnsibleDescribable.ANSIBLE_EXTRA_PARAM,null,configuration,executionDataContext);

sshPasswordPath = (String) resolveProperty(AnsibleDescribable.ANSIBLE_SSH_PASSWORD_STORAGE_PATH,null,configuration,executionDataContext);
sshPrivateKeyPath = (String) resolveProperty(AnsibleDescribable.ANSIBLE_SSH_KEYPATH_STORAGE_PATH,null,configuration,executionDataContext);


}

public AnsibleRunner buildAnsibleRunner() throws ResourceModelSourceException{
Expand All @@ -172,6 +195,9 @@ public AnsibleRunner buildAnsibleRunner() throws ResourceModelSourceException{
runner.limit(limitList);
}

StorageTree storageTree = services.getService(KeyStorageTree.class);


if ( sshAuthType.equalsIgnoreCase(AuthenticationType.privateKey.name()) ) {
if (sshPrivateKeyFile != null) {
String sshPrivateKey;
Expand All @@ -182,10 +208,29 @@ public AnsibleRunner buildAnsibleRunner() throws ResourceModelSourceException{
}
runner = runner.sshPrivateKey(sshPrivateKey);
}

if(sshPrivateKeyPath !=null && !sshPrivateKeyPath.isEmpty()){
try {
String sshPrivateKey = getStorageContentString(sshPrivateKeyPath, storageTree);
runner = runner.sshPrivateKey(sshPrivateKey);
} catch (ConfigurationException e) {
throw new ResourceModelSourceException("Could not read password from storage path " + sshPasswordPath,e);
}
}

} else if ( sshAuthType.equalsIgnoreCase(AuthenticationType.password.name()) ) {
if (sshPassword != null) {
runner = runner.sshUsePassword(Boolean.TRUE).sshPass(sshPassword);
}

if(sshPasswordPath !=null && !sshPasswordPath.isEmpty()){
try {
sshPassword = getStorageContentString(sshPasswordPath, storageTree);
runner = runner.sshUsePassword(Boolean.TRUE).sshPass(sshPassword);
} catch (ConfigurationException e) {
throw new ResourceModelSourceException("Could not read password from storage path " + sshPasswordPath,e);
}
}
}


Expand Down Expand Up @@ -249,6 +294,9 @@ public AnsibleRunner buildAnsibleRunner() throws ResourceModelSourceException{
runner.extraParams(extraParameters);
}




return runner;
}

Expand Down Expand Up @@ -534,4 +582,79 @@ public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOEx

return nodes;
}



private String getStorageContentString(String storagePath, StorageTree storageTree) throws ConfigurationException {
return new String(this.getStorageContent(storagePath, storageTree));
}

private byte[] getStorageContent(String storagePath, StorageTree storageTree) throws ConfigurationException {
org.rundeck.storage.api.Path path = PathUtil.asPath(storagePath);
try {
ResourceMeta contents = storageTree.getResource(path).getContents();
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
contents.writeContent(byteArrayOutputStream);
return byteArrayOutputStream.toByteArray();
} catch (StorageException e) {
throw new ConfigurationException("Failed to read the ssh private key for " +
"storage path: " + storagePath + ": " + e.getMessage());
} catch (IOException e) {
throw new ConfigurationException("Failed to read the ssh private key for " +
"storage path: " + storagePath + ": " + e.getMessage());
}
}


@Override
List<String> listSecretsPathResourceModel(Map<String, Object> configuration){
List<String> keys = new ArrayList<>();

String passwordStoragePath = (String) configuration.get(AnsibleDescribable.ANSIBLE_SSH_PASSWORD_STORAGE_PATH);
String privateKeyStoragePath = (String) configuration.get(AnsibleDescribable.ANSIBLE_SSH_KEYPATH_STORAGE_PATH);

if(passwordStoragePath!=null){
keys.add(passwordStoragePath);
}

if(privateKeyStoragePath!=null){
keys.add(privateKeyStoragePath);
}

return null;

}

@Override
SecretBundle prepareSecretBundleResourceModel(Services services, Map<String, Object> configuration){
DefaultSecretBundle secretBundle = new DefaultSecretBundle();

try {
StorageTree storageTree = services.getService(KeyStorageTree.class);

String passwordStoragePath = (String) configuration.get(AnsibleDescribable.ANSIBLE_SSH_PASSWORD_STORAGE_PATH);
String privateKeyStoragePath = (String) configuration.get(AnsibleDescribable.ANSIBLE_SSH_KEYPATH_STORAGE_PATH);

if(passwordStoragePath!=null){
secretBundle.addSecret(
passwordStoragePath,
getStorageContent(passwordStoragePath,storageTree )
);
}

if(privateKeyStoragePath!=null){
secretBundle.addSecret(
privateKeyStoragePath,
getStorageContent(privateKeyStoragePath,storageTree )
);
}

return secretBundle;

} catch (Exception e) {
return null;
}
}


}
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import com.dtolabs.rundeck.core.resources.ResourceModelSourceFactory;
import com.dtolabs.rundeck.plugins.ServiceNameConstants;
import com.dtolabs.rundeck.plugins.util.DescriptionBuilder;
import org.rundeck.app.spi.Services;

import java.util.Properties;

Expand Down Expand Up @@ -53,6 +54,10 @@ public AnsibleResourceModelSourceFactory(final Framework framework) {
builder.property(BECOME_PASSWORD_PROP);
builder.property(VAULT_KEY_FILE_PROP);
builder.property(VAULT_PASSWORD_PROP);

builder.property(SSH_PASSWORD_STORAGE_PROP);
builder.property(SSH_KEY_STORAGE_PROP);

builder.mapping(ANSIBLE_INVENTORY,PROJ_PROP_PREFIX + ANSIBLE_INVENTORY);
builder.frameworkMapping(ANSIBLE_INVENTORY,FWK_PROP_PREFIX + ANSIBLE_INVENTORY);
builder.mapping(ANSIBLE_CONFIG_FILE_PATH,PROJ_PROP_PREFIX + ANSIBLE_CONFIG_FILE_PATH);
Expand All @@ -68,12 +73,18 @@ public AnsibleResourceModelSourceFactory(final Framework framework) {

@Override
public ResourceModelSource createResourceModelSource(Properties configuration) throws ConfigurationException {
AnsibleResourceModelSource ansibleResourceModelSource = new AnsibleResourceModelSource(framework);
ansibleResourceModelSource.configure(configuration);
return ansibleResourceModelSource;
return null;
}

@Override
@Override
public ResourceModelSource createResourceModelSource(Services services, Properties configuration) throws ConfigurationException {
AnsibleResourceModelSource ansibleResourceModelSource = new AnsibleResourceModelSource(framework);
ansibleResourceModelSource.configure(configuration);
ansibleResourceModelSource.setServices(services);
return ansibleResourceModelSource;
}

@Override
public Description getDescription() {
return DESC;
}
Expand Down

0 comments on commit a2237d5

Please sign in to comment.