diff --git a/README.md b/README.md index 710850d..13cf9d0 100644 --- a/README.md +++ b/README.md @@ -74,6 +74,17 @@ rundeck.storage.provider.[index].config.githubToken=githubToken ``` +* **namespace**: Namespace. Vault namespace (Optional) +``` +rundeck.storage.provider.[index].config.namespace=namespace +``` + +* **authNamespace**: Authentication Namespace. Vault authentication namespace used for authentication(Optional) +``` +rundeck.storage.provider.[index].config.authNamespace=namespace +``` + + * **keyStoreFile**: Key store file A Java keystore, containing a client certificate that's registered with Vault's TLS Certificate auth backend. diff --git a/build.gradle b/build.gradle index 2633633..da0e8cf 100644 --- a/build.gradle +++ b/build.gradle @@ -1,11 +1,15 @@ plugins { id 'pl.allegro.tech.build.axion-release' version '1.11.0' + id 'groovy' + } ext.rundeckVersion='3.0.2-20180803' defaultTasks 'clean','build' apply plugin: 'java' apply plugin: 'idea' apply plugin: 'pl.allegro.tech.build.axion-release' +apply plugin: 'groovy' + sourceCompatibility = 1.8 ext.rundeckPluginVersion= '1.2' @@ -15,6 +19,7 @@ ext.rundeckPluginVersion= '1.2' */ ext.pluginClassNames='io.github.valfadeev.rundeck.plugin.vault.VaultStoragePlugin' + scmVersion { ignoreUncommittedChanges = false tag { @@ -51,7 +56,7 @@ dependencies { // add any third-party jar dependencies you wish to include in the plugin // using the `pluginLibs` configuration as shown here: - pluginLibs group: 'com.bettercloud', name: 'vault-java-driver', version: '4.0.0', ext: 'jar' + pluginLibs group: 'com.bettercloud', name: 'vault-java-driver', version: '5.1.0', ext: 'jar' //the compile dependency won't add the rundeck-core jar to the plugin contents @@ -64,7 +69,11 @@ dependencies { [group: 'net.bytebuddy', name: 'byte-buddy', version: '1.12.1'], [group: 'net.bytebuddy', name: 'byte-buddy-agent', version: '1.12.1'], [group: 'org.objenesis', name: 'objenesis', version: '3.2'] + ) + testCompile "org.codehaus.groovy:groovy-all:2.4.15" + testCompile "org.spockframework:spock-core:1.0-groovy-2.4" + testCompile "cglib:cglib-nodep:2.2.2" } diff --git a/docker/.env-enterprise b/docker/.env-enterprise new file mode 100644 index 0000000..95a7759 --- /dev/null +++ b/docker/.env-enterprise @@ -0,0 +1,8 @@ +VAULT_TOKEN=myroot1 +mem_limit=1200000000 +VAULT_IMAGE=hashicorp/vault-enterprise +VAULT_APPROLE_ID=123 +VAULT_APPROLE_SECRET_ID=123 + +RUNDECK_IMAGE=rundeckpro/enterprise:SNAPSHOT +VAULT_LICENSE=xxxxx \ No newline at end of file diff --git a/docker/.gitignore b/docker/.gitignore new file mode 100644 index 0000000..3303b3e --- /dev/null +++ b/docker/.gitignore @@ -0,0 +1,2 @@ +data/envs.txt +rundeck/plugins/vault-storage-* \ No newline at end of file diff --git a/docker/Makefile b/docker/Makefile index d065672..8055877 100644 --- a/docker/Makefile +++ b/docker/Makefile @@ -1,25 +1,28 @@ all: #env vars -DOCKER_COMPOSE_SPEC="docker-compose.yaml" +DOCKER_COMPOSE_SPEC ?= docker-compose.yaml +ENV_FILE ?= .env + build: set -e # re-build docker env - docker-compose -f $(DOCKER_COMPOSE_SPEC) build + docker-compose -f $(DOCKER_COMPOSE_SPEC) --env-file $(ENV_FILE) build start: # run docker - docker-compose -f $(DOCKER_COMPOSE_SPEC) up -d - + docker-compose -f $(DOCKER_COMPOSE_SPEC) --env-file $(ENV_FILE) up -d + clean: # clean up docker env - docker-compose -f $(DOCKER_COMPOSE_SPEC) down --volumes --remove-orphans + docker-compose -f $(DOCKER_COMPOSE_SPEC) --env-file $(ENV_FILE) down --volumes --remove-orphans + rm data/envs.txt build_and_start: set -e # re-build docker env - docker-compose -f $(DOCKER_COMPOSE_SPEC) build + docker-compose -f $(DOCKER_COMPOSE_SPEC) --env-file $(ENV_FILE) build # clean up docker env - docker-compose -f $(DOCKER_COMPOSE_SPEC) down --volumes --remove-orphans + docker-compose -f $(DOCKER_COMPOSE_SPEC) --env-file $(ENV_FILE) down --volumes --remove-orphans # run docker - docker-compose -f $(DOCKER_COMPOSE_SPEC) up -d \ No newline at end of file + docker-compose -f $(DOCKER_COMPOSE_SPEC) --env-file $(ENV_FILE) up -d diff --git a/docker/README.md b/docker/README.md index 70908c3..43daa14 100644 --- a/docker/README.md +++ b/docker/README.md @@ -6,7 +6,7 @@ This is a docker compose environment wih rundeck, mysql and vault * Copy vault plugin to `docker/rundeck/plugins` -### How to use it +### Simple example with Vault OSS * Build @@ -27,3 +27,28 @@ make start make clean ``` + +### Example with Vault Enterprise using approle + +* Build + +``` +export DOCKER_COMPOSE_SPEC=docker-compose-approle.yaml +export ENV_FILE=.env-enterprise + +make build +``` + +* Start + +``` +make start +``` + + +* Stop + +``` +make clean +``` + diff --git a/docker/data/README.md b/docker/data/README.md new file mode 100644 index 0000000..e69de29 diff --git a/docker/docker-compose-approle.yaml b/docker/docker-compose-approle.yaml new file mode 100644 index 0000000..59bd660 --- /dev/null +++ b/docker/docker-compose-approle.yaml @@ -0,0 +1,66 @@ +version: '2' + +services: + vault: + build: + context: vault-enterprise + args: + IMAGE: ${VAULT_IMAGE} + ports: + - "8200:8200" + cap_add: + - IPC_LOCK + environment: + - VAULT_DEV_ROOT_TOKEN_ID=${VAULT_TOKEN} + - VAULT_DEV_LISTEN_ADDRESS=vault:8200 + - VAULT_ADDR=http://vault:8200 + - VAULT_TOKEN=${VAULT_TOKEN} + - VAULT_LICENSE=${VAULT_LICENSE} + volumes: + - ./data:/data + rundeck: + hostname: rundeck + build: + context: rundeck + args: + IMAGE: ${RUNDECK_IMAGE} + mem_limit: ${mem_limit} + #command: -Dlogging.config=/home/rundeck/server/config/logback.groovy + environment: + - RUNDECK_EXEC_CMD=script/configure.sh + - RUNDECK_GRAILS_URL=http://localhost:4442 + - RUNDECK_DATABASE_DRIVER=org.mariadb.jdbc.Driver + - RUNDECK_DATABASE_USERNAME=rundeck + - RUNDECK_DATABASE_PASSWORD=rundeck + - RUNDECK_DATABASE_URL=jdbc:mariadb://mysql/rundeck?autoReconnect=true&useSSL=false + - RUNDECK_STORAGE_PROVIDER_1_TYPE=vault-storage + - RUNDECK_STORAGE_PROVIDER_1_PATH=keys + - RUNDECK_STORAGE_PROVIDER_1_REMOVEPATHPREFIX=true + - RUNDECK_STORAGE_PROVIDER_1_CONFIG_SECRETBACKEND=secret + - RUNDECK_STORAGE_PROVIDER_1_CONFIG_PREFIX=app + - RUNDECK_STORAGE_PROVIDER_1_CONFIG_ADDRESS=http://vault:8200 + - RUNDECK_STORAGE_PROVIDER_1_CONFIG_BEHAVIOUR=vault + - RUNDECK_STORAGE_PROVIDER_1_CONFIG_ENGINEVERSION=2 + - RUNDECK_STORAGE_PROVIDER_1_CONFIG_MAXRETRIES=10 + - RUNDECK_STORAGE_PROVIDER_1_CONFIG_RETRYINTERVAL=10 + - RUNDECK_STORAGE_PROVIDER_1_CONFIG_OPENTIMEOUT=30 + - RUNDECK_STORAGE_PROVIDER_1_CONFIG_READTIMEOUT=30 + # auth approle + - RUNDECK_STORAGE_PROVIDER_1_CONFIG_AUTHBACKEND=approle + - RUNDECK_STORAGE_PROVIDER_1_CONFIG_AUTHNAMESPACE=rundeck + - RUNDECK_STORAGE_PROVIDER_1_CONFIG_APPROLEID=${VAULT_APPROLE_ID} + - RUNDECK_STORAGE_PROVIDER_1_CONFIG_APPROLESECRETID=${VAULT_APPROLE_SECRET_ID} + - RUNDECK_STORAGE_PROVIDER_1_CONFIG_NAMESPACE=rundeck/demo + ports: + - "4442:4440" + volumes: + - ./data/:/home/rundeck/vault-envs/ + mysql: + image: mysql:5.7 + ports: + - "3306" + environment: + - MYSQL_ROOT_PASSWORD=root + - MYSQL_DATABASE=rundeck + - MYSQL_USER=rundeck + - MYSQL_PASSWORD=rundeck diff --git a/docker/rundeck/Dockerfile b/docker/rundeck/Dockerfile index 5759e5d..d919e4b 100644 --- a/docker/rundeck/Dockerfile +++ b/docker/rundeck/Dockerfile @@ -8,9 +8,7 @@ RUN apt-get update && \ apt-get -y install jq # add cli tool debian repo -RUN echo "deb https://dl.bintray.com/rundeck/rundeck-deb /" | sudo tee -a /etc/apt/sources.list -RUN curl "https://bintray.com/user/downloadSubjectPublicKey?username=bintray" > /tmp/bintray.gpg.key -RUN apt-key add - < /tmp/bintray.gpg.key +RUN curl -s https://packagecloud.io/install/repositories/pagerduty/rundeck/script.deb.sh | os=any dist=any bash RUN apt-get -y update RUN apt-get -y install rundeck-cli @@ -27,6 +25,9 @@ ENV USERNAME=rundeck \ WORKDIR $HOME USER rundeck +COPY --chown=rundeck:root script/ /home/rundeck/script/ +RUN chmod +x /home/rundeck/script/* + COPY --chown=rundeck:root remco /etc/remco COPY --chown=rundeck:root logback.groovy /home/rundeck/server/config diff --git a/docker/rundeck/remco/templates/rundeck-config-storage.properties b/docker/rundeck/remco/templates/rundeck-config-storage.properties index 4b26548..14e1aaf 100644 --- a/docker/rundeck/remco/templates/rundeck-config-storage.properties +++ b/docker/rundeck/remco/templates/rundeck-config-storage.properties @@ -8,7 +8,6 @@ rundeck.storage.provider.{{index}}.removePathPrefix={% set removepathprefix = pr rundeck.storage.provider.{{index}}.config.prefix={% set prefix = printf("%s/config/prefix", provider) %}{{ getv(prefix, "")}} rundeck.storage.provider.{{index}}.config.address={% set address = printf("%s/config/address", provider) %}{{ getv(address, "")}} -rundeck.storage.provider.{{index}}.config.token={% set token = printf("%s/config/token", provider) %}{{ getv(token, "")}} rundeck.storage.provider.{{index}}.config.storageBehaviour={% set behaviour = printf("%s/config/behaviour", provider) %}{{ getv(behaviour, "rundeck")}} rundeck.storage.provider.{{index}}.config.secretBackend={% set secretbackend = printf("%s/config/secretbackend", provider) %}{{ getv(secretbackend, "secret")}} @@ -19,6 +18,33 @@ rundeck.storage.provider.{{index}}.config.readTimeout={% set readtimeout = print rundeck.storage.provider.{{index}}.config.engineVersion={% set engineversion = printf("%s/config/engineversion", provider) %}{{ getv(engineversion, "1")}} +{%- set auth = getv(printf("%s/config/authbackend", provider), "token") %} +{%- set namespace = getv(printf("%s/config/namespace", provider)) %} +{%- set authnamespace = getv(printf("%s/config/authnamespace", provider)) %} + + +{% if namespace %} +rundeck.storage.provider.{{index}}.config.namespace={% set namespace = printf("%s/config/namespace", provider) %}{{ getv(namespace)}} +{% endif %} + +{% if auth == 'token' %} +rundeck.storage.provider.{{index}}.config.token={% set token = printf("%s/config/token", provider) %}{{ getv(token, "")}} + +{% endif %} + + +{% if auth == 'approle' %} +rundeck.storage.provider.{{index}}.config.approleId={% set approleid = printf("%s/config/approleid", provider) %}{{ getv(approleid)}} +rundeck.storage.provider.{{index}}.config.approleSecretId={% set approlesecretid = printf("%s/config/approlesecretid", provider) %}{{ getv(approlesecretid)}} +rundeck.storage.provider.{{index}}.config.approleAuthMount={% set approleauthmount = printf("%s/config/approleauthmount", provider) %}{{ getv(approleauthmount,"approle")}} +rundeck.storage.provider.{{index}}.config.authBackend=approle + +{% endif %} + +{% if authnamespace %} +rundeck.storage.provider.{{index}}.config.authNamespace={% set authnamespace = printf("%s/config/authnamespace", provider) %}{{ getv(authnamespace)}} +{% endif %} + {% endmacro %} diff --git a/docker/rundeck/script/configure.sh b/docker/rundeck/script/configure.sh new file mode 100644 index 0000000..23b7773 --- /dev/null +++ b/docker/rundeck/script/configure.sh @@ -0,0 +1,25 @@ +#!/bin/bash + +ls -lrt /home/rundeck/script + +bash /home/rundeck/script/waitforfile.sh /home/rundeck/vault-envs/envs.txt 30 10 + +export $(xargs < /home/rundeck/vault-envs/envs.txt) + +env | grep VAULT + +sed -i "s/^\(rundeck\.storage\.provider\.1\.config\.approleId\s*=\s*\).*\$/\1$VAULT_ROLE_ID/" server/config/rundeck-config.properties +sed -i "s/^\(rundeck\.storage\.provider\.1\.config\.approleSecretId\s*=\s*\).*\$/\1$VAULT_SECRET_ID/" server/config/rundeck-config.properties + + +cat server/config/rundeck-config.properties + +exec java \ + -XX:+UnlockExperimentalVMOptions \ + -Dlog4j.configurationFile="${HOME}/server/config/log4j2.properties" \ + -Dlogging.config="file:${HOME}/server/config/log4j2.properties" \ + -Dloginmodule.conf.name=jaas-loginmodule.conf \ + -Dloginmodule.name=rundeck \ + -Drundeck.jaaslogin=true \ + "${@}" \ + -jar rundeck.war diff --git a/docker/rundeck/script/waitforfile.sh b/docker/rundeck/script/waitforfile.sh new file mode 100644 index 0000000..1d2a5cb --- /dev/null +++ b/docker/rundeck/script/waitforfile.sh @@ -0,0 +1,30 @@ +#!/bin/bash + +syntax_error() { echo >&2 "SYNTAX: $*"; exit 2; } + +(( $# != 3 )) && { + syntax_error "$0 " +} + +declare -r FILE=$1 INTERVAL=$2 MAXTRY=$3 + +echo "waiting for ${FILE}" + +progress_tic() { if [[ -t 1 ]]; then printf -- "%s" "$@"; else printf -- "%s\n" "$@" ; fi ; } + +echo "Running as user: $(whoami)" + +declare -i attempts=0 +while (( attempts <= MAXTRY )) +do + if ! test -f "$FILE" + then progress_tic "."; # output a progress string. + else break; # file exists + fi + (( attempts += 1 )) ; # increment attempts attemptser. + (( attempts == MAXTRY )) && { + echo "FAIL: Reached max try file exists: $FILE. Exiting." + exit 1 + } + sleep "$INTERVAL"; # wait before trying again. +done \ No newline at end of file diff --git a/docker/vault-enterprise/Dockerfile b/docker/vault-enterprise/Dockerfile new file mode 100644 index 0000000..7870313 --- /dev/null +++ b/docker/vault-enterprise/Dockerfile @@ -0,0 +1,9 @@ +ARG IMAGE +FROM ${IMAGE} + +EXPOSE 8200 +COPY run.sh /usr/local/bin/run.sh +RUN apk --no-cache add curl +RUN apk --no-cache add jq + +CMD ["run.sh"] diff --git a/docker/vault-enterprise/run.sh b/docker/vault-enterprise/run.sh new file mode 100755 index 0000000..f12c590 --- /dev/null +++ b/docker/vault-enterprise/run.sh @@ -0,0 +1,87 @@ +#!/usr/bin/env sh + +#start vault + +vault server -config=/vault/config -dev & > log.out + +version=$(curl -s http://vault:8200/v1/sys/health |jq -r .version) + + +# create namespace +vault namespace create rundeck +vault namespace create -namespace=rundeck demo +vault namespace create -namespace=rundeck other + +export VAULT_NAMESPACE=rundeck + +export VAULT_ROLE_NAME="rundeck" + +#Add AppRole Auth Method +vault auth enable approle +#Create 'rundeck' policy +vault policy write ${VAULT_ROLE_NAME} -< /data/envs.txt << EOL +VAULT_ROLE_ID=$role_id +VAULT_SECRET_ID=$secret_id +EOL + +cat /data/envs.txt + +echo "ROLE ID: $role_id" +echo "SECRET ID: $secret_id" +export VAULT_NAMESPACE=rundeck/demo + + +echo "************ creating test keys" + +if (( $version > 1 )); then + vault secrets enable -version=2 -path=secret kv + + + echo "Vault 1.x" + vault kv put secret/app/simple.secret foo=world + vault kv put secret/app/multiples name=admin password=admin server=rundeck + vault kv put secret/app/folder/another.secret test=hello + vault kv put secret/app/folder/multiple2 name=admin password=admin server=rundeck + +else + echo "Vault 0.x" + vault secrets enable -version=1 -path=secret kv + + vault write secret/app/simple.secret foo=world + vault write secret/app/multiples name=admin password=admin server=rundeck + vault write secret/app/folder/another.secret test=hello + vault write secret/app/folder/multiple2 name=admin password=admin server=rundeck + + echo "************ end" + +fi +sleep 10 + +tail -f log.out diff --git a/src/main/java/io/github/valfadeev/rundeck/plugin/vault/ConfigOptions.java b/src/main/java/io/github/valfadeev/rundeck/plugin/vault/ConfigOptions.java index c3a4428..6f6d59c 100644 --- a/src/main/java/io/github/valfadeev/rundeck/plugin/vault/ConfigOptions.java +++ b/src/main/java/io/github/valfadeev/rundeck/plugin/vault/ConfigOptions.java @@ -27,5 +27,6 @@ class ConfigOptions { static final String VAULT_SECRET_BACKEND = "secretBackend"; static final String VAULT_STORAGE_BEHAVIOUR = "storageBehaviour"; static final String VAULT_ENGINE_VERSION = "engineVersion"; + static final String VAULT_AUTH_NAMESPACE = "authNamespace"; } diff --git a/src/main/java/io/github/valfadeev/rundeck/plugin/vault/DescriptionProvider.java b/src/main/java/io/github/valfadeev/rundeck/plugin/vault/DescriptionProvider.java index 64f118c..b6019c1 100644 --- a/src/main/java/io/github/valfadeev/rundeck/plugin/vault/DescriptionProvider.java +++ b/src/main/java/io/github/valfadeev/rundeck/plugin/vault/DescriptionProvider.java @@ -178,6 +178,11 @@ static Description getDescription() { .values(Arrays.asList("1","2")) .defaultValue("1") ) + .property(PropertyBuilder.builder() + .string(VAULT_AUTH_NAMESPACE) + .title("Authentication Namespace") + .description("The namespace for authentication") + ) .build(); } } diff --git a/src/main/java/io/github/valfadeev/rundeck/plugin/vault/KeyObjectBuilder.java b/src/main/java/io/github/valfadeev/rundeck/plugin/vault/KeyObjectBuilder.java index 382c614..c4a5d52 100644 --- a/src/main/java/io/github/valfadeev/rundeck/plugin/vault/KeyObjectBuilder.java +++ b/src/main/java/io/github/valfadeev/rundeck/plugin/vault/KeyObjectBuilder.java @@ -51,6 +51,10 @@ KeyObject build(){ object = new VaultKey(response,path); } + if(response.getRestResponse().getStatus()!=200){ + object.error = true; + } + } catch (VaultException e) { object = new RundeckKey(path); object.setErrorMessage(e.getMessage()); @@ -87,6 +91,11 @@ public KeyObject getVaultParentObject(Path path){ Path parentPath = PathUtil.parentPath(path); try { response = vault.read(VaultStoragePlugin.getVaultPath(parentPath.getPath(),vaultSecretBackend,vaultPrefix)); + + if(response.getRestResponse().getStatus()!=200){ + return null; + } + parentObject=new VaultKey(response, parentPath); } catch (VaultException e) { diff --git a/src/main/java/io/github/valfadeev/rundeck/plugin/vault/VaultClientProvider.java b/src/main/java/io/github/valfadeev/rundeck/plugin/vault/VaultClientProvider.java index 3637a16..c1e3994 100644 --- a/src/main/java/io/github/valfadeev/rundeck/plugin/vault/VaultClientProvider.java +++ b/src/main/java/io/github/valfadeev/rundeck/plugin/vault/VaultClientProvider.java @@ -127,8 +127,9 @@ private SslConfig getSslConfig() throws ConfigurationException { return sslConfig; } - private String getVaultAuthToken() throws ConfigurationException { + private String getVaultAuthToken() throws ConfigurationException, VaultException { final String vaultAuthBackend = configuration.getProperty(VAULT_AUTH_BACKEND); + final String vaultAuthNameSpace = configuration.getProperty(VAULT_AUTH_NAMESPACE); final String authToken; final String msg = "Must specify %s when auth backend is %s"; @@ -148,6 +149,10 @@ private String getVaultAuthToken() throws ConfigurationException { } final VaultConfig vaultAuthConfig = getVaultConfig(); + + if(vaultAuthNameSpace!=null && !vaultAuthNameSpace.isEmpty()){ + vaultAuthConfig.nameSpace(vaultAuthNameSpace); + } final Auth vaultAuth = new Vault(vaultAuthConfig).auth(); switch (vaultAuthBackend) { diff --git a/src/main/java/io/github/valfadeev/rundeck/plugin/vault/VaultStoragePlugin.java b/src/main/java/io/github/valfadeev/rundeck/plugin/vault/VaultStoragePlugin.java index cf63cab..582dc8d 100644 --- a/src/main/java/io/github/valfadeev/rundeck/plugin/vault/VaultStoragePlugin.java +++ b/src/main/java/io/github/valfadeev/rundeck/plugin/vault/VaultStoragePlugin.java @@ -8,6 +8,7 @@ import com.bettercloud.vault.Vault; import com.bettercloud.vault.VaultException; import com.bettercloud.vault.api.Logical; +import com.bettercloud.vault.response.LogicalResponse; import com.bettercloud.vault.response.LookupResponse; import com.bettercloud.vault.response.VaultResponse; import com.dtolabs.rundeck.core.plugins.Plugin; @@ -130,7 +131,8 @@ private boolean isVaultDir(String key) { try{ lookup(); - if(vault.list(getVaultPath(key,vaultSecretBackend,vaultPrefix)).size() > 0){ + List list = getVaultList(key); + if(list.size() > 0){ return true; }else{ if(!rundeckObject) { @@ -226,7 +228,7 @@ private Set> listResources(Path path, KeyType type) { try { lookup(); - response = vault.list(getVaultPath(path.getPath(),vaultSecretBackend,vaultPrefix)); + response = getVaultList(path); } catch (VaultException e) { throw StorageException.listException( @@ -303,7 +305,9 @@ private Set> listResources(Path path, KeyType type) { public boolean hasPath(Path path) { try { lookup(); - if(vault.list(getVaultPath(path.getPath(),vaultSecretBackend,vaultPrefix)).size() > 0){ + + List list = getVaultList(path); + if(list.size() > 0){ return true; } @@ -344,7 +348,8 @@ public boolean hasResource(String path) { public boolean hasDirectory(Path path) { try { lookup(); - List list=vault.list(getVaultPath(path.getPath(),vaultSecretBackend,vaultPrefix)); + + List list=getVaultList(path); if(list.size() > 0){ return list.size() > 0; @@ -467,5 +472,23 @@ public KeyObject getVaultObject(Path path){ } + private List getVaultList(Path path) throws VaultException { + return getVaultList(path.getPath()); + + } + + private List getVaultList(String path) throws VaultException { + LogicalResponse response = vault.list(getVaultPath(path,vaultSecretBackend,vaultPrefix)); + if (response.getRestResponse().getStatus()==403){ + String body = new String(response.getRestResponse().getBody()); + throw StorageException.listException( + PathUtil.asPath(path), + String.format("Encountered error while reading data from Vault %s", + body)); + } + + return response.getListData(); + + } } \ No newline at end of file diff --git a/src/test/groovy/id/github/valfadeev/rundeck/plugin/vault/VaultStoragePluginSpec.groovy b/src/test/groovy/id/github/valfadeev/rundeck/plugin/vault/VaultStoragePluginSpec.groovy new file mode 100644 index 0000000..50ee637 --- /dev/null +++ b/src/test/groovy/id/github/valfadeev/rundeck/plugin/vault/VaultStoragePluginSpec.groovy @@ -0,0 +1,98 @@ +package id.github.valfadeev.rundeck.plugin.vault + +import com.bettercloud.vault.Vault +import com.bettercloud.vault.api.Auth +import com.bettercloud.vault.api.Logical +import com.bettercloud.vault.response.LogicalResponse +import com.bettercloud.vault.response.LookupResponse +import com.bettercloud.vault.rest.RestResponse +import io.github.valfadeev.rundeck.plugin.vault.VaultStoragePlugin +import spock.lang.Specification + +class VaultStoragePluginSpec extends Specification{ + + def "trigger error when vault returns with permission deny"(){ + given: + + Properties properties = ["address":"http://localhost:8200", + "maxRetries":"1", + "retryIntervalMilliseconds":"200", + "engineVersion":"2", + "openTimeout":"20", + "readTimeout":"20", + "authBackend":"token", + "token":"123456"] + def plugin = new VaultStoragePlugin() + plugin.configure(properties) + + Logical vault = Mock(Logical){ + list(_)>>Mock(LogicalResponse){ + getRestResponse()>>Mock(RestResponse){ + getStatus()>> 403 + } + } + } + + Vault vaultClient = Mock(Vault){ + auth()>>Mock(Auth){ + lookupSelf()>>Mock(LookupResponse){ + getTTL()>>120 + } + } + } + + plugin.vault = vault + plugin.vaultClient = vaultClient + + when: + def result = plugin.hasDirectory("keys/test") + + then: + thrown Exception + + } + + + def "has directories keys"(){ + given: + + Properties properties = ["address":"http://localhost:8200", + "maxRetries":"1", + "retryIntervalMilliseconds":"200", + "engineVersion":"2", + "openTimeout":"20", + "readTimeout":"20", + "authBackend":"token", + "token":"123456"] + def plugin = new VaultStoragePlugin() + plugin.configure(properties) + + Logical vault = Mock(Logical){ + list(_)>>Mock(LogicalResponse){ + getRestResponse()>>Mock(RestResponse){ + getStatus()>> 200 + + } + getListData()>>["key1","key2"] + } + } + + Vault vaultClient = Mock(Vault){ + auth()>>Mock(Auth){ + lookupSelf()>>Mock(LookupResponse){ + getTTL()>>120 + } + } + } + + plugin.vault = vault + plugin.vaultClient = vaultClient + + when: + def result = plugin.hasDirectory("keys/test") + + then: + result + + } +} diff --git a/test/docker/dockers/rundeckvault/Dockerfile b/test/docker/dockers/rundeckvault/Dockerfile index 7616bba..5d656fd 100644 --- a/test/docker/dockers/rundeckvault/Dockerfile +++ b/test/docker/dockers/rundeckvault/Dockerfile @@ -8,9 +8,7 @@ RUN apt-get update && \ # add cli tool debian repo -RUN echo "deb https://dl.bintray.com/rundeck/rundeck-deb /" | sudo tee -a /etc/apt/sources.list -RUN curl "https://bintray.com/user/downloadSubjectPublicKey?username=bintray" > /tmp/bintray.gpg.key -RUN apt-key add - < /tmp/bintray.gpg.key +RUN curl -s https://packagecloud.io/install/repositories/pagerduty/rundeck/script.deb.sh | os=any dist=any bash RUN apt-get -y update RUN apt-get -y install rundeck-cli diff --git a/test/docker/dockers/rundeckvault/tests/existing-vault-eng2/existing-vault-keys-test.sh b/test/docker/dockers/rundeckvault/tests/existing-vault-eng2/existing-vault-keys-test.sh index 37ae031..2cce0d7 100644 --- a/test/docker/dockers/rundeckvault/tests/existing-vault-eng2/existing-vault-keys-test.sh +++ b/test/docker/dockers/rundeckvault/tests/existing-vault-eng2/existing-vault-keys-test.sh @@ -190,8 +190,10 @@ it_check_multiple_key_value_job() { bash -c "rd run -i $JOBID -p $RUNDECK_PROJECT" cmdout=($(bash -c "rd executions follow -e 2 | grep -v '^#' ")) - expout=($(curl -s -H "X-Vault-Token: $VAULT_TOKEN" http://vault:8200/v1/rundeckbackend/app/keys/multiples | jq .data.password)) + expout=($(curl -s -H "X-Vault-Token: $VAULT_TOKEN" http://vault:8200/v1/secret/data/rundeck/keys/multiples | jq .data.data.password)) + echo "${expout}" echo "${cmdout[@]}" + if ! test ${#expout[*]} = ${#cmdout[*]} then echo "FAIL: command output did not contain ${#expout[*]} lines. Contained ${#cmdout[*]}: ${cmdout[*]}"