Skip to content

Commit 0d7e393

Browse files
authored
Merge pull request #63 from rundeck-plugins/RUN-3556-Hahsicorp-Vault-Revision-of-Authentication-backend-CERT-implementation-fix
Run 3556 hahsicorp vault revision of authentication backend cert implementation fix
2 parents 6999950 + cc64900 commit 0d7e393

File tree

12 files changed

+456
-84
lines changed

12 files changed

+456
-84
lines changed

README.md

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,10 @@ rundeck.storage.provider.[index].config.namespace=namespace
8383
```
8484
rundeck.storage.provider.[index].config.authNamespace=namespace
8585
```
86-
86+
* **certAuthMount**: Cert Auth Mount name, The mount name of the TLS Certificate authentication back end for cert authentication.
87+
```
88+
rundeck.storage.provider.[index].config.certAuthMount=mount-name
89+
```
8790
8891
* **keyStoreFile**: Key store file
8992
A Java keystore, containing a client certificate that's registered with Vault's TLS Certificate auth backend.
@@ -103,6 +106,10 @@ rundeck.storage.provider.[index].config.keyStoreFilePassword=/path/keyStoreFileP
103106
```
104107
rundeck.storage.provider.[index].config.trustStoreFile=/path/trustStoreFile
105108
```
109+
* **trustStoreFilePassword**: Truststore file password. The password needed to access the truststore.
110+
```
111+
rundeck.storage.provider.[index].config.trustStoreFilePassword=trustStorePassword
112+
```
106113
107114
* **pemFile**: PEM file. The path of a file containing an X.509 certificate, in unencrypted PEM format with UTF-8 encoding.
108115
@@ -244,7 +251,23 @@ curl --header "X-Vault-Token: $TOKEN" http://localhost:8200/v1/auth/approle/role
244251
curl --header "X-Vault-Token: $TOKEN" --request POST http://localhost:8200/v1/auth/approle/role/rundeck/secret-id | jq
245252
```
246253
254+
#### **Using CERT authentication**
247255
256+
```
257+
rundeck.storage.provider.1.type=vault-storage
258+
rundeck.storage.provider.1.path=keys
259+
rundeck.storage.provider.1.config.prefix=app
260+
rundeck.storage.provider.1.config.address=$VAULT_URL
261+
rundeck.storage.provider.1.config.authBackend=cert
262+
rundeck.storage.provider.1.config.secretBackend=kv
263+
rundeck.storage.provider.1.config.engineVersion=2
264+
rundeck.storage.provider.1.config.certAuthMount=tls-auth
265+
rundeck.storage.provider.1.config.keyStoreFile=$KEYSTORE_FILE
266+
rundeck.storage.provider.1.config.keyStoreFilePassword=$KEYSTORE_PASSWORD
267+
rundeck.storage.provider.1.config.trustStoreFile=$TRUSTSTORE_FILE
268+
rundeck.storage.provider.1.config.truststoreFilePassword=$TRUSTSTORE_PASSWORD
269+
rundeck.storage.provider.1.config.validateSsl=true
270+
```
248271
## Vault API versions
249272
250273
Since version 1.3.1, this plugin can work with `kV Secrets Engine - Version 2`.

build.gradle

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ dependencies {
4747
// add any third-party jar dependencies you wish to include in the plugin
4848
// using the `pluginLibs` configuration as shown here:
4949

50-
pluginLibs group: 'com.bettercloud', name: 'vault-java-driver', version: '5.1.0', ext: 'jar'
50+
pluginLibs group: 'io.github.jopenlibs', name: 'vault-java-driver', version: '6.2.0', ext: 'jar'
5151

5252

5353
//the compile dependency won't add the rundeck-core jar to the plugin contents

src/main/java/io/github/valfadeev/rundeck/plugin/vault/ConfigOptions.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ class ConfigOptions {
1515
static final String VAULT_KEY_STORE_FILE = "keyStoreFile";
1616
static final String VAULT_KEY_STORE_FILE_PASSWORD = "keyStoreFilePassword";
1717
static final String VAULT_TRUST_STORE_FILE = "trustStoreFile";
18+
static final String VAULT_TRUST_STORE_FILE_PASSWORD = "trustStoreFilePassword";
1819
static final String VAULT_CLIENT_PEM_FILE = "clientPemFile";
1920
static final String VAULT_CLIENT_KEY_PEM_FILE = "clientKeyPemFile";
2021
static final String VAULT_USERPASS_AUTH_MOUNT = "userpassAuthMount";
@@ -28,5 +29,6 @@ class ConfigOptions {
2829
static final String VAULT_STORAGE_BEHAVIOUR = "storageBehaviour";
2930
static final String VAULT_ENGINE_VERSION = "engineVersion";
3031
static final String VAULT_AUTH_NAMESPACE = "authNamespace";
32+
static final String VAULT_CERT_AUTH_MOUNT = "certAuthMount";
3133

3234
}

src/main/java/io/github/valfadeev/rundeck/plugin/vault/KeyObject.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
package io.github.valfadeev.rundeck.plugin.vault;
22

3-
import com.bettercloud.vault.api.Logical;
3+
import io.github.jopenlibs.vault.api.Logical;
44
import com.dtolabs.rundeck.core.storage.ResourceMeta;
55
import org.rundeck.storage.api.Path;
66
import org.rundeck.storage.impl.ResourceBase;

src/main/java/io/github/valfadeev/rundeck/plugin/vault/KeyObjectBuilder.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
package io.github.valfadeev.rundeck.plugin.vault;
22

3-
import com.bettercloud.vault.VaultException;
4-
import com.bettercloud.vault.api.Logical;
5-
import com.bettercloud.vault.response.LogicalResponse;
3+
import io.github.jopenlibs.vault.VaultException;
4+
import io.github.jopenlibs.vault.api.Logical;
5+
import io.github.jopenlibs.vault.response.LogicalResponse;
66
import org.rundeck.storage.api.Path;
77
import org.rundeck.storage.api.PathUtil;
88

src/main/java/io/github/valfadeev/rundeck/plugin/vault/RundeckKey.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
package io.github.valfadeev.rundeck.plugin.vault;
22

3-
import com.bettercloud.vault.VaultException;
4-
import com.bettercloud.vault.api.Logical;
5-
import com.bettercloud.vault.response.LogicalResponse;
3+
import io.github.jopenlibs.vault.VaultException;
4+
import io.github.jopenlibs.vault.api.Logical;
5+
import io.github.jopenlibs.vault.response.LogicalResponse;
66
import com.dtolabs.rundeck.core.storage.ResourceMeta;
77
import com.dtolabs.rundeck.core.storage.ResourceMetaBuilder;
88
import com.dtolabs.rundeck.core.storage.StorageUtil;

src/main/java/io/github/valfadeev/rundeck/plugin/vault/VaultClientProvider.java

Lines changed: 87 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,40 +1,50 @@
11
package io.github.valfadeev.rundeck.plugin.vault;
22

33
import java.io.File;
4+
import java.io.FileInputStream;
5+
import java.security.KeyStore;
46
import java.util.Properties;
57

6-
import com.bettercloud.vault.SslConfig;
7-
import com.bettercloud.vault.Vault;
8-
import com.bettercloud.vault.VaultConfig;
9-
import com.bettercloud.vault.VaultException;
10-
import com.bettercloud.vault.api.Auth;
8+
import io.github.jopenlibs.vault.SslConfig;
9+
import io.github.jopenlibs.vault.Vault;
10+
import io.github.jopenlibs.vault.VaultConfig;
11+
import io.github.jopenlibs.vault.VaultException;
12+
import io.github.jopenlibs.vault.api.Auth;
1113
import com.dtolabs.rundeck.core.plugins.configuration.ConfigurationException;
1214

1315
import static io.github.valfadeev.rundeck.plugin.vault.ConfigOptions.*;
1416
import static io.github.valfadeev.rundeck.plugin.vault.SupportedAuthBackends.*;
1517

16-
class VaultClientProvider {
18+
import org.slf4j.Logger;
19+
import org.slf4j.LoggerFactory;
1720

18-
private Properties configuration;
21+
class VaultClientProvider {
22+
private static final Logger LOG = LoggerFactory.getLogger(VaultClientProvider.class);
23+
private final Properties configuration;
1924

2025
VaultClientProvider(Properties configuration) {
2126
this.configuration = configuration;
2227
}
2328

2429
Vault getVaultClient() throws ConfigurationException {
25-
final Integer vaultMaxRetries = Integer.parseInt(configuration.getProperty(VAULT_MAX_RETRIES));
26-
final Integer vaultRetryIntervalMilliseconds = Integer.parseInt(configuration.getProperty(VAULT_RETRY_INTERVAL_MILLISECONDS));
30+
LOG.debug("[vault] getVaultClient(): start, address='{}'", configuration.getProperty(VAULT_ADDRESS));
31+
final int vaultMaxRetries = Integer.parseInt(configuration.getProperty(VAULT_MAX_RETRIES));
32+
final int vaultRetryIntervalMilliseconds = Integer.parseInt(configuration.getProperty(VAULT_RETRY_INTERVAL_MILLISECONDS));
2733
final Integer vaultEngineVersion = Integer.parseInt(configuration.getProperty(VAULT_ENGINE_VERSION));
2834

2935
VaultConfig vaultConfig = getVaultConfig();
3036

3137
try {
3238
String authToken = getVaultAuthToken();
39+
LOG.debug("[vault] got auth token? {}", (authToken != null && !authToken.isEmpty()));
3340
vaultConfig.token(authToken).build();
34-
return new Vault(vaultConfig, vaultEngineVersion)
35-
.withRetries(vaultMaxRetries,
36-
vaultRetryIntervalMilliseconds);
41+
LOG.debug("[vault] building Vault client with engineVersion={} retries={} interval={}ms",
42+
vaultEngineVersion, vaultMaxRetries, vaultRetryIntervalMilliseconds);
43+
return Vault.create(vaultConfig, vaultEngineVersion).withRetries(vaultMaxRetries,
44+
vaultRetryIntervalMilliseconds);
45+
3746
} catch (VaultException e) {
47+
LOG.debug("[vault] getVaultClient(): VaultException: {}", e.getMessage(), e);
3848
throw new ConfigurationException(String.format("Encountered error while "
3949
+ "building Vault configuration: %s", e.getMessage()));
4050
}
@@ -58,11 +68,13 @@ protected VaultConfig getVaultConfig() throws ConfigurationException {
5868
try {
5969
vaultConfig.nameSpace(nameSpace);
6070
} catch (VaultException e) {
71+
LOG.debug("[vault] error building namespace configuration: {}", e.getMessage(), e);
6172
throw new ConfigurationException(
6273
String.format("Encountered error while building namespace configuration: %s", e.getMessage()));
6374
}
6475
}
6576

77+
LOG.debug("[vault] built VaultConfig for address='{}', namespace='{}'", vaultAddress, nameSpace);
6678
return vaultConfig;
6779
}
6880

@@ -73,19 +85,39 @@ private SslConfig getSslConfig() throws ConfigurationException {
7385
sslConfig.verify(vaultVerifySsl);
7486

7587
final String vaultTrustStoreFile = configuration.getProperty(VAULT_TRUST_STORE_FILE);
88+
final String vaultTrustStoreFilePassword = configuration.getProperty(VAULT_TRUST_STORE_FILE_PASSWORD);
89+
7690
if (vaultTrustStoreFile != null) {
7791
try {
78-
sslConfig.trustStoreFile(new File(vaultTrustStoreFile));
79-
} catch (VaultException e) {
80-
throw new ConfigurationException(String.format("Encountered error while building ssl configuration: %s", e.getMessage()));
92+
LOG.debug("[vault] using trustStoreFile={}", vaultTrustStoreFile);
93+
File trustFile = new File(vaultTrustStoreFile);
94+
LOG.debug("[vault] trustStoreFile exists={}, readable={}, size={}",
95+
trustFile.exists(), trustFile.canRead(), trustFile.length());
96+
97+
// load JKS with password, then provide in-memory KeyStore to the driver
98+
try (FileInputStream in = new FileInputStream(trustFile)) {
99+
KeyStore ts = KeyStore.getInstance("JKS"); // keep default type
100+
if (vaultTrustStoreFilePassword == null) {
101+
throw new ConfigurationException("vault.trustStoreFilePassword is required for JKS truststores");
102+
}
103+
ts.load(in, vaultTrustStoreFilePassword.toCharArray());
104+
sslConfig.trustStore(ts);
105+
}
106+
107+
LOG.debug("[vault] trustStore (JKS) loaded and applied successfully");
108+
} catch (Exception e) {
109+
LOG.debug("[vault] error setting trustStore (JKS): {}", e.getMessage(), e);
110+
throw new ConfigurationException("Encountered error while building ssl configuration: " + e.getMessage());
81111
}
82112
} else {
83113
final String vaultPemFile = configuration.getProperty(VAULT_PEM_FILE);
84114
if (vaultPemFile != null) {
85115
try {
116+
LOG.debug("[vault] using pemFile={}", vaultPemFile);
86117
sslConfig.pemFile(new File(vaultPemFile));
87118
}
88119
catch (VaultException e) {
120+
LOG.debug("[vault] error setting pemFile: {}", e.getMessage(), e);
89121
throw new ConfigurationException(
90122
String.format("Encountered error while building "
91123
+ "ssl configuration: %s",
@@ -109,9 +141,11 @@ private SslConfig getSslConfig() throws ConfigurationException {
109141
final String vaultClientKeyPemFile = configuration.getProperty(VAULT_CLIENT_KEY_PEM_FILE);
110142
if (vaultClientPemFile != null && vaultClientKeyPemFile != null) {
111143
try {
144+
LOG.debug("[vault] using keyStoreFile={}", vaultKeyStoreFile);
112145
sslConfig.clientPemFile(new File(vaultClientPemFile))
113146
.clientKeyPemFile(new File(vaultClientKeyPemFile));
114147
} catch (VaultException e) {
148+
LOG.debug("[vault] error setting client cert/key: {}", e.getMessage(), e);
115149
throw new ConfigurationException(String.format("Encountered error while building ssl configuration: %s", e.getMessage()));
116150
}
117151
}
@@ -121,6 +155,7 @@ private SslConfig getSslConfig() throws ConfigurationException {
121155
try {
122156
sslConfig.build();
123157
} catch (VaultException e) {
158+
LOG.debug("[vault] error building SslConfig: {}", e.getMessage(), e);
124159
throw new ConfigurationException(String.format("Encountered error while building ssl configuration: %s", e.getMessage()));
125160
}
126161

@@ -131,19 +166,26 @@ private String getVaultAuthToken() throws ConfigurationException, VaultException
131166
final String vaultAuthBackend = configuration.getProperty(VAULT_AUTH_BACKEND);
132167
final String vaultAuthNameSpace = configuration.getProperty(VAULT_AUTH_NAMESPACE);
133168

169+
LOG.debug("[vault] getVaultAuthToken(): backend='{}', authNamespace='{}'",
170+
vaultAuthBackend, vaultAuthNameSpace);
171+
172+
134173
final String authToken;
135174
final String msg = "Must specify %s when auth backend is %s";
136175

137176
if (vaultAuthBackend.equals(TOKEN)) {
177+
LOG.debug("[vault] auth=TOKEN");
138178
authToken = configuration.getProperty(VAULT_TOKEN);
139179
if (authToken == null) {
180+
LOG.debug("[vault] missing token for TOKEN backend");
140181
throw new ConfigurationException(
141182
String.format(
142183
msg,
143184
VAULT_TOKEN,
144185
vaultAuthBackend
145186
)
146187
);
188+
147189
}
148190
return authToken;
149191
}
@@ -153,16 +195,18 @@ private String getVaultAuthToken() throws ConfigurationException, VaultException
153195
if(vaultAuthNameSpace!=null && !vaultAuthNameSpace.isEmpty()){
154196
vaultAuthConfig.nameSpace(vaultAuthNameSpace);
155197
}
156-
final Auth vaultAuth = new Vault(vaultAuthConfig).auth();
198+
final Auth vaultAuth = Vault.create(vaultAuthConfig).auth();
157199

158200
switch (vaultAuthBackend) {
159201

160202
case APPROLE:
203+
LOG.debug("[vault] auth=APPROLE");
161204
final String vaultApproleId = configuration.getProperty(VAULT_APPROLE_ID);
162205
final String vaultApproleSecretId = configuration.getProperty(VAULT_APPROLE_SECRET_ID);
163206
final String vaultApproleAuthMount = configuration.getProperty(VAULT_APPROLE_AUTH_MOUNT);
164207

165208
if (vaultApproleId == null || vaultApproleSecretId == null) {
209+
LOG.debug("[vault] missing AppRole id/secret");
166210
throw new ConfigurationException(
167211
String.format(
168212
msg,
@@ -179,17 +223,21 @@ private String getVaultAuthToken() throws ConfigurationException, VaultException
179223
vaultApproleAuthMount,
180224
vaultApproleId,
181225
vaultApproleSecretId).getAuthClientToken();
226+
LOG.debug("[vault] AppRole login success? {}", (authToken != null));
182227

183228
} catch (VaultException e) {
229+
LOG.debug("[vault] AppRole login failed: {}", e.getMessage(), e);
184230
throw new ConfigurationException(
185231
String.format("Encountered error while authenticating with %s: %s", vaultAuthBackend, e.getLocalizedMessage())
186232
);
187233
}
188234
break;
189235

190236
case GITHUB:
237+
LOG.debug("[vault] auth=GITHUB");
191238
final String vaultGithubToken = configuration.getProperty(VAULT_GITHUB_TOKEN);
192239
if (vaultGithubToken == null) {
240+
LOG.debug("[vault] missing github token");
193241
throw new ConfigurationException(
194242
String.format(msg,
195243
VAULT_GITHUB_TOKEN,
@@ -202,8 +250,10 @@ private String getVaultAuthToken() throws ConfigurationException, VaultException
202250
authToken = vaultAuth
203251
.loginByGithub(vaultGithubToken)
204252
.getAuthClientToken();
253+
LOG.debug("[vault] GitHub login success? {}", (authToken != null));
205254

206255
} catch (VaultException e) {
256+
LOG.debug("[vault] GitHub login failed: {}", e.getMessage(), e);
207257
throw new ConfigurationException(
208258
String.format("Encountered error while authenticating with %s",
209259
vaultAuthBackend)
@@ -212,10 +262,12 @@ private String getVaultAuthToken() throws ConfigurationException, VaultException
212262
break;
213263

214264
case USERPASS:
265+
LOG.debug("[vault] auth=USERPASS");
215266
final String vaultUserpassAuthMount = configuration.getProperty(VAULT_USERPASS_AUTH_MOUNT);
216267
final String vaultUsername = configuration.getProperty(VAULT_USERNAME);
217268
final String vaultPassword = configuration.getProperty(VAULT_PASSWORD);
218269
if (vaultUsername == null || vaultPassword == null) {
270+
LOG.debug("[vault] missing user/password");
219271
throw new ConfigurationException(
220272
String.format(msg,
221273
String.join(", ",
@@ -229,16 +281,35 @@ private String getVaultAuthToken() throws ConfigurationException, VaultException
229281
authToken = vaultAuth
230282
.loginByUserPass(vaultUsername, vaultPassword, vaultUserpassAuthMount)
231283
.getAuthClientToken();
284+
LOG.debug("[vault] UserPass login success? {}", (authToken != null));
232285

233286
} catch (VaultException e) {
287+
LOG.debug("[vault] UserPass login failed: {}", e.getMessage(), e);
234288
throw new ConfigurationException(
235289
String.format("Encountered error while authenticating with %s",
236290
vaultAuthBackend)
237291
);
238292
}
239293
break;
240294

295+
case CERT:
296+
LOG.debug("[vault] auth=CERT");
297+
final String configured = configuration.getProperty(VAULT_CERT_AUTH_MOUNT);
298+
final String mount = (configured == null || configured.isEmpty()) ? "cert" : configured;
299+
try {
300+
authToken = vaultAuth.loginByCert(mount).getAuthClientToken();
301+
302+
} catch (VaultException e) {
303+
LOG.debug("[vault] Cert login failed: {}", e.getMessage(), e);
304+
throw new ConfigurationException(
305+
String.format("Encountered error while authenticating with %s at mount '%s': %s",
306+
vaultAuthBackend, mount, e.getLocalizedMessage()));
307+
}
308+
break;
309+
310+
241311
default:
312+
LOG.debug("[vault] unsupported auth backend='{}'", vaultAuthBackend);
242313
throw new ConfigurationException(
243314
String.format("Unsupported auth backend: %s", vaultAuthBackend));
244315

src/main/java/io/github/valfadeev/rundeck/plugin/vault/VaultKey.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
package io.github.valfadeev.rundeck.plugin.vault;
22

3-
import com.bettercloud.vault.VaultException;
4-
import com.bettercloud.vault.api.Logical;
5-
import com.bettercloud.vault.response.LogicalResponse;
3+
import io.github.jopenlibs.vault.VaultException;
4+
import io.github.jopenlibs.vault.api.Logical;
5+
import io.github.jopenlibs.vault.response.LogicalResponse;
66
import com.dtolabs.rundeck.core.storage.ResourceMeta;
77
import com.dtolabs.rundeck.core.storage.ResourceMetaBuilder;
88
import com.dtolabs.rundeck.core.storage.StorageUtil;

0 commit comments

Comments
 (0)