Skip to content

Commit f7deeb8

Browse files
committed
include support for ldap secret engine
Signed-off-by: Drew Mullen <drew.mullen@hashicorp.com>
1 parent b2ce7b6 commit f7deeb8

9 files changed

Lines changed: 663 additions & 0 deletions

File tree

docs/modules/ROOT/pages/secret-backends.adoc

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,78 @@ spring.cloud.vault:
161161

162162
See also: https://www.vaultproject.io/docs/secrets/rabbitmq/index.html[Vault Documentation: Setting up RabbitMQ with Vault]
163163

164+
[[vault.config.backends.ldap]]
165+
== LDAP
166+
167+
Spring Cloud Vault can obtain credentials for LDAP.
168+
169+
The LDAP integration requires the `spring-cloud-vault-config-ldap`
170+
dependency.
171+
172+
.pom.xml
173+
[source,xml,indent=0,subs="verbatim,quotes,attributes"]
174+
----
175+
<dependencies>
176+
<dependency>
177+
<groupId>org.springframework.cloud</groupId>
178+
<artifactId>spring-cloud-vault-config-ldap</artifactId>
179+
<version>{project-version}</version>
180+
</dependency>
181+
</dependencies>
182+
----
183+
184+
The integration can be enabled by setting
185+
`spring.cloud.vault.ldap.enabled=true` (default `false`) and providing the role name with `spring.cloud.vault.ldap.role=…`.
186+
187+
Vault's LDAP secret engine supports both dynamic and static roles:
188+
189+
* **Dynamic roles** generate temporary credentials on-demand
190+
* **Static roles** manage the rotation of existing LDAP user credentials
191+
192+
To use a static role, set `spring.cloud.vault.ldap.static-role=true` (default `false`).
193+
194+
Username and password are stored in `spring.ldap.username`
195+
and `spring.ldap.password` so applications using Spring LDAP will pick up the generated credentials without further configuration.
196+
You can configure the property names by setting `spring.cloud.vault.ldap.username-property` and
197+
`spring.cloud.vault.ldap.password-property`.
198+
199+
Example: Dynamic Role
200+
201+
[source,yaml]
202+
----
203+
spring.cloud.vault:
204+
ldap:
205+
enabled: true
206+
role: my-dynamic-role
207+
static-role: false
208+
backend: ldap
209+
username-property: spring.ldap.username
210+
password-property: spring.ldap.password
211+
----
212+
213+
Example: Static Role
214+
215+
[source,yaml]
216+
----
217+
spring.cloud.vault:
218+
ldap:
219+
enabled: true
220+
role: my-static-role
221+
static-role: true
222+
backend: ldap
223+
username-property: spring.ldap.username
224+
password-property: spring.ldap.password
225+
----
226+
227+
* `enabled` setting this value to `true` enables the LDAP backend config usage
228+
* `role` sets the role name of the LDAP role definition
229+
* `static-role` setting this value to `true` uses static role credentials instead of dynamic
230+
* `backend` sets the path of the LDAP mount to use
231+
* `username-property` sets the property name in which the LDAP username is stored
232+
* `password-property` sets the property name in which the LDAP password is stored
233+
234+
See also: https://www.vaultproject.io/docs/secrets/ldap/index.html[Vault Documentation: Setting up LDAP with Vault]
235+
164236
[[vault.config.backends.aws]]
165237
== AWS
166238

pom.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
<module>spring-cloud-vault-config-consul</module>
2828
<module>spring-cloud-vault-config-rabbitmq</module>
2929
<module>spring-cloud-vault-config-aws</module>
30+
<module>spring-cloud-vault-config-ldap</module>
3031
<module>spring-cloud-starter-vault-config</module>
3132
<module>docs</module>
3233
</modules>
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
3+
xmlns="http://maven.apache.org/POM/4.0.0"
4+
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
5+
<modelVersion>4.0.0</modelVersion>
6+
7+
<parent>
8+
<groupId>org.springframework.cloud</groupId>
9+
<artifactId>spring-cloud-vault-parent</artifactId>
10+
<version>5.0.1-SNAPSHOT</version>
11+
<relativePath>..</relativePath>
12+
</parent>
13+
14+
<artifactId>spring-cloud-vault-config-ldap</artifactId>
15+
<name>Spring Cloud Vault Config LDAP support</name>
16+
<description>Spring Cloud Vault Config LDAP support</description>
17+
18+
<dependencies>
19+
<!-- Compile -->
20+
<dependency>
21+
<groupId>org.springframework.cloud</groupId>
22+
<artifactId>spring-cloud-vault-config</artifactId>
23+
</dependency>
24+
25+
<!-- Annotation processing -->
26+
<dependency>
27+
<groupId>org.springframework.boot</groupId>
28+
<artifactId>spring-boot-autoconfigure-processor</artifactId>
29+
<optional>true</optional>
30+
</dependency>
31+
32+
<dependency>
33+
<groupId>org.springframework.boot</groupId>
34+
<artifactId>spring-boot-configuration-processor</artifactId>
35+
<optional>true</optional>
36+
</dependency>
37+
38+
<!-- Test -->
39+
<dependency>
40+
<groupId>org.springframework.cloud</groupId>
41+
<artifactId>spring-cloud-vault-config</artifactId>
42+
<type>test-jar</type>
43+
<scope>test</scope>
44+
</dependency>
45+
46+
<dependency>
47+
<groupId>org.apache.httpcomponents.client5</groupId>
48+
<artifactId>httpclient5</artifactId>
49+
<scope>test</scope>
50+
</dependency>
51+
52+
</dependencies>
53+
54+
</project>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
/*
2+
* Copyright 2016-present the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.cloud.vault.config.ldap;
18+
19+
import java.util.HashMap;
20+
import java.util.Map;
21+
22+
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
23+
import org.springframework.boot.context.properties.EnableConfigurationProperties;
24+
import org.springframework.cloud.vault.config.PropertyNameTransformer;
25+
import org.springframework.cloud.vault.config.SecretBackendMetadata;
26+
import org.springframework.cloud.vault.config.SecretBackendMetadataFactory;
27+
import org.springframework.cloud.vault.config.VaultSecretBackendDescriptor;
28+
import org.springframework.context.annotation.Bean;
29+
import org.springframework.context.annotation.Configuration;
30+
import org.springframework.core.Ordered;
31+
import org.springframework.core.annotation.Order;
32+
import org.springframework.util.Assert;
33+
import org.springframework.vault.core.util.PropertyTransformer;
34+
35+
/**
36+
* Bootstrap configuration providing support for the LDAP secret engine.
37+
*
38+
* @author Drew Mullen
39+
* @since 5.0.1
40+
*/
41+
@Configuration(proxyBeanMethods = false)
42+
@EnableConfigurationProperties(VaultLdapProperties.class)
43+
@Order(Ordered.LOWEST_PRECEDENCE - 15)
44+
public class VaultConfigLdapBootstrapConfiguration {
45+
46+
@Bean
47+
@ConditionalOnMissingBean
48+
public LdapSecretBackendMetadataFactory ldapSecretBackendMetadataFactory() {
49+
return new LdapSecretBackendMetadataFactory();
50+
}
51+
52+
/**
53+
* {@link SecretBackendMetadataFactory} for LDAP integration using
54+
* {@link VaultLdapProperties}.
55+
*/
56+
public static class LdapSecretBackendMetadataFactory implements SecretBackendMetadataFactory<VaultLdapProperties> {
57+
58+
/**
59+
* Creates a {@link SecretBackendMetadata} for the LDAP secret engine using
60+
* {@link VaultLdapProperties}. This accessor transforms Vault's username/password
61+
* property names to names provided with
62+
* {@link VaultLdapProperties#getUsernameProperty()} and
63+
* {@link VaultLdapProperties#getPasswordProperty()}.
64+
* @param properties must not be {@literal null}.
65+
* @return the {@link SecretBackendMetadata}
66+
*/
67+
static SecretBackendMetadata forLdap(final VaultLdapProperties properties) {
68+
69+
Assert.notNull(properties, "VaultLdapProperties must not be null");
70+
71+
PropertyNameTransformer transformer = new PropertyNameTransformer();
72+
transformer.addKeyTransformation("username", properties.getUsernameProperty());
73+
transformer.addKeyTransformation("password", properties.getPasswordProperty());
74+
75+
return new SecretBackendMetadata() {
76+
77+
private final String credPath = properties.isStaticRole() ? "static-cred" : "creds";
78+
79+
@Override
80+
public String getName() {
81+
return String.format("%s with Role %s", properties.getBackend(), properties.getRole());
82+
}
83+
84+
@Override
85+
public String getPath() {
86+
return String.format("%s/%s/%s", properties.getBackend(), this.credPath, properties.getRole());
87+
}
88+
89+
@Override
90+
public PropertyTransformer getPropertyTransformer() {
91+
return transformer;
92+
}
93+
94+
@Override
95+
public Map<String, String> getVariables() {
96+
97+
Map<String, String> variables = new HashMap<>();
98+
variables.put("backend", properties.getBackend());
99+
variables.put("key", String.format("%s/%s", this.credPath, properties.getRole()));
100+
return variables;
101+
}
102+
};
103+
}
104+
105+
@Override
106+
public SecretBackendMetadata createMetadata(VaultLdapProperties backendDescriptor) {
107+
return forLdap(backendDescriptor);
108+
}
109+
110+
@Override
111+
public boolean supports(VaultSecretBackendDescriptor backendDescriptor) {
112+
return backendDescriptor instanceof VaultLdapProperties;
113+
}
114+
115+
}
116+
117+
}
Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
/*
2+
* Copyright 2016-present the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.cloud.vault.config.ldap;
18+
19+
import org.springframework.boot.context.properties.ConfigurationProperties;
20+
import org.springframework.cloud.vault.config.VaultSecretBackendDescriptor;
21+
import org.springframework.lang.Nullable;
22+
23+
/**
24+
* Configuration properties for Vault using the LDAP secret engine.
25+
*
26+
* @author Drew Mullen
27+
* @since 5.0.1
28+
*/
29+
@ConfigurationProperties("spring.cloud.vault.ldap")
30+
public class VaultLdapProperties implements VaultSecretBackendDescriptor {
31+
32+
/**
33+
* Enable LDAP secret engine usage.
34+
*/
35+
private boolean enabled = false;
36+
37+
/**
38+
* Role name for credentials.
39+
*/
40+
@Nullable
41+
private String role;
42+
43+
/**
44+
* Enable static role usage.
45+
*/
46+
private boolean staticRole = false;
47+
48+
/**
49+
* LDAP secret engine backend path.
50+
*/
51+
private String backend = "ldap";
52+
53+
/**
54+
* Target property for the obtained username.
55+
*/
56+
private String usernameProperty = "spring.ldap.username";
57+
58+
/**
59+
* Target property for the obtained password.
60+
*/
61+
private String passwordProperty = "spring.ldap.password";
62+
63+
@Override
64+
public boolean isEnabled() {
65+
return this.enabled;
66+
}
67+
68+
public void setEnabled(boolean enabled) {
69+
this.enabled = enabled;
70+
}
71+
72+
@Nullable
73+
public String getRole() {
74+
return this.role;
75+
}
76+
77+
public void setRole(@Nullable String role) {
78+
this.role = role;
79+
}
80+
81+
public boolean isStaticRole() {
82+
return this.staticRole;
83+
}
84+
85+
public void setStaticRole(boolean staticRole) {
86+
this.staticRole = staticRole;
87+
}
88+
89+
@Override
90+
public String getBackend() {
91+
return this.backend;
92+
}
93+
94+
public void setBackend(String backend) {
95+
this.backend = backend;
96+
}
97+
98+
public String getUsernameProperty() {
99+
return this.usernameProperty;
100+
}
101+
102+
public void setUsernameProperty(String usernameProperty) {
103+
this.usernameProperty = usernameProperty;
104+
}
105+
106+
public String getPasswordProperty() {
107+
return this.passwordProperty;
108+
}
109+
110+
public void setPasswordProperty(String passwordProperty) {
111+
this.passwordProperty = passwordProperty;
112+
}
113+
114+
}

0 commit comments

Comments
 (0)