Skip to content

Commit 22a6ffb

Browse files
committed
Allow TLS certificate to authenticate against the named certificate role
Fixes #874 Signed-off-by: Issam El-atif <[email protected]>
1 parent 2eece46 commit 22a6ffb

File tree

4 files changed

+87
-28
lines changed

4 files changed

+87
-28
lines changed

docs/modules/ROOT/pages/authentication.adoc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -399,6 +399,7 @@ spring.cloud.vault:
399399
key-store-password: changeit
400400
key-store-type: JKS
401401
cert-auth-path: cert
402+
role: my-dev-role
402403
----
403404

404405
See also: https://www.vaultproject.io/docs/auth/cert.html[Vault Documentation: Using the Cert auth backend]

spring-cloud-vault-config/src/main/java/org/springframework/cloud/vault/config/ClientAuthenticationFactory.java

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -378,11 +378,14 @@ private ClientAuthentication pcfAuthentication(VaultProperties vaultProperties)
378378

379379
private ClientAuthentication certificateAuthentication(VaultProperties vaultProperties) {
380380

381-
ClientCertificateAuthenticationOptions options = ClientCertificateAuthenticationOptions.builder()
382-
.path(vaultProperties.getSsl().getCertAuthPath())
383-
.build();
381+
ClientCertificateAuthenticationOptions.ClientCertificateAuthenticationOptionsBuilder optionsBuilder = ClientCertificateAuthenticationOptions
382+
.builder();
383+
optionsBuilder.path(vaultProperties.getSsl().getCertAuthPath());
384+
if (StringUtils.hasText(vaultProperties.getSsl().getRole())) {
385+
optionsBuilder.role(vaultProperties.getSsl().getRole());
386+
}
384387

385-
return new ClientCertificateAuthentication(options, this.restOperations);
388+
return new ClientCertificateAuthentication(optionsBuilder.build(), this.restOperations);
386389
}
387390

388391
private ClientAuthentication tokenAuthentication(VaultProperties vaultProperties) {

spring-cloud-vault-config/src/main/java/org/springframework/cloud/vault/config/VaultProperties.java

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@
4242
* @author Michal Budzyn
4343
* @author Grenville Wilson
4444
* @author Mårten Svantesson
45+
* @author Issam El-atif
4546
*/
4647
@ConfigurationProperties(VaultProperties.PREFIX)
4748
public class VaultProperties implements EnvironmentAware {
@@ -1152,6 +1153,13 @@ public static class Ssl {
11521153
*/
11531154
private String certAuthPath = "cert";
11541155

1156+
/**
1157+
* Name of the role against which the login is being attempted.
1158+
*
1159+
* @since 5.0
1160+
*/
1161+
private String role = "";
1162+
11551163
/**
11561164
* List of enabled SSL/TLS protocol.
11571165
* @since 3.0.2
@@ -1226,6 +1234,14 @@ public void setCertAuthPath(String certAuthPath) {
12261234
this.certAuthPath = certAuthPath;
12271235
}
12281236

1237+
public String getRole() {
1238+
return role;
1239+
}
1240+
1241+
public void setRole(String role) {
1242+
this.role = role;
1243+
}
1244+
12291245
public List<String> getEnabledProtocols() {
12301246
return this.enabledProtocols;
12311247
}

spring-cloud-vault-config/src/test/java/org/springframework/cloud/vault/config/VaultConfigTlsCertAuthenticationTests.java

Lines changed: 63 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -26,15 +26,20 @@
2626
import org.junit.jupiter.api.BeforeAll;
2727
import org.junit.jupiter.api.Test;
2828

29-
import org.springframework.beans.factory.annotation.Value;
29+
import org.junit.jupiter.api.extension.ExtendWith;
3030
import org.springframework.boot.SpringApplication;
31-
import org.springframework.boot.autoconfigure.SpringBootApplication;
32-
import org.springframework.boot.test.context.SpringBootTest;
31+
import org.springframework.boot.WebApplicationType;
32+
import org.springframework.boot.builder.SpringApplicationBuilder;
33+
import org.springframework.boot.test.context.TestConfiguration;
34+
import org.springframework.boot.test.system.CapturedOutput;
35+
import org.springframework.boot.test.system.OutputCaptureExtension;
3336
import org.springframework.cloud.vault.util.Settings;
3437
import org.springframework.cloud.vault.util.VaultRule;
38+
import org.springframework.context.ConfigurableApplicationContext;
3539
import org.springframework.vault.core.VaultOperations;
3640

3741
import static org.assertj.core.api.Assertions.assertThat;
42+
import static org.junit.jupiter.api.Assertions.assertNull;
3843
import static org.springframework.cloud.vault.util.Settings.findWorkDir;
3944

4045
/**
@@ -44,18 +49,19 @@
4449
* referenced with {@code ../work/keystore.jks}.
4550
*
4651
* @author Mark Paluch
52+
* @author Issam El-atif
4753
*/
48-
49-
@SpringBootTest(classes = VaultConfigTlsCertAuthenticationTests.TestApplication.class,
50-
properties = { "spring.cloud.vault.authentication=cert",
51-
"spring.cloud.vault.ssl.key-store=file:../work/client-cert.jks",
52-
"spring.cloud.vault.ssl.key-store-password=changeit",
53-
"spring.cloud.vault.application-name=VaultConfigTlsCertAuthenticationTests",
54-
"spring.cloud.vault.reactive.enabled=false", "spring.cloud.bootstrap.enabled=true" })
54+
@ExtendWith(OutputCaptureExtension.class)
5555
public class VaultConfigTlsCertAuthenticationTests {
5656

57-
@Value("${vault.value}")
58-
String configValue;
57+
SpringApplication app = new SpringApplicationBuilder().sources(TestConfig.class)
58+
.web(WebApplicationType.NONE)
59+
.build();
60+
61+
private static VaultOperations vaultOperations;
62+
63+
private static final String certificate = Files.contentOf(new File(findWorkDir(), "ca/certs/client.cert.pem"),
64+
StandardCharsets.US_ASCII);
5965

6066
@BeforeAll
6167
public static void beforeClass() {
@@ -69,7 +75,7 @@ public static void beforeClass() {
6975
vaultRule.prepare().mountAuth(vaultProperties.getSsl().getCertAuthPath());
7076
}
7177

72-
VaultOperations vaultOperations = vaultRule.prepare().getVaultOperations();
78+
vaultOperations = vaultRule.prepare().getVaultOperations();
7379

7480
String rules = "{ \"name\": \"testpolicy\",\n" //
7581
+ " \"path\": {\n" //
@@ -82,10 +88,6 @@ public static void beforeClass() {
8288
vaultOperations.write("secret/" + VaultConfigTlsCertAuthenticationTests.class.getSimpleName(),
8389
Collections.singletonMap("vault.value", "foo"));
8490

85-
File workDir = findWorkDir();
86-
87-
String certificate = Files.contentOf(new File(workDir, "ca/certs/client.cert.pem"), StandardCharsets.US_ASCII);
88-
8991
Map<String, String> role = new HashMap<>();
9092
role.put("certificate", certificate);
9193
role.put("policies", "testpolicy");
@@ -94,16 +96,53 @@ public static void beforeClass() {
9496
}
9597

9698
@Test
97-
public void contextLoads() {
98-
assertThat(this.configValue).isEqualTo("foo");
99+
void authenticateUsingTlsCertificate() {
100+
ConfigurableApplicationContext context = this.app.run("--spring.cloud.vault.authentication=cert",
101+
"--spring.cloud.vault.ssl.key-store=file:../work/client-cert.jks",
102+
"--spring.cloud.vault.ssl.key-store-password=changeit",
103+
"--spring.cloud.vault.application-name=VaultConfigTlsCertAuthenticationTests",
104+
"--spring.cloud.vault.reactive.enabled=false", "--spring.cloud.bootstrap.enabled=true");
105+
106+
assertThat(context.getEnvironment().getProperty("vault.value")).isEqualTo("foo");
99107
}
100108

101-
@SpringBootApplication
102-
public static class TestApplication {
109+
@Test
110+
void authenticateUsingNamedCertificateRole() {
111+
Map<String, String> anotherRole = new HashMap<>();
112+
anotherRole.put("certificate", certificate);
113+
vaultOperations.write("auth/cert/certs/another-role", anotherRole);
103114

104-
public static void main(String[] args) {
105-
SpringApplication.run(TestApplication.class, args);
106-
}
115+
ConfigurableApplicationContext context = this.app.run("--spring.cloud.vault.authentication=cert",
116+
"--spring.cloud.vault.ssl.key-store=file:../work/client-cert.jks",
117+
"--spring.cloud.vault.ssl.key-store-password=changeit", "--spring.cloud.vault.ssl.role=my-role",
118+
"--spring.cloud.vault.application-name=VaultConfigTlsCertAuthenticationTests",
119+
"--spring.cloud.vault.reactive.enabled=false", "--spring.cloud.bootstrap.enabled=true");
120+
121+
assertThat(context.getEnvironment().getProperty("vault.value")).isEqualTo("foo");
122+
123+
vaultOperations.delete("auth/cert/certs/another-role");
124+
}
125+
126+
@Test
127+
void authenticationFailsWhenMultipleCertsAndNoNamedRole(CapturedOutput capturedOutput) {
128+
Map<String, String> anotherRole = new HashMap<>();
129+
anotherRole.put("certificate", certificate);
130+
vaultOperations.write("auth/cert/certs/another-role", anotherRole);
131+
132+
ConfigurableApplicationContext context = this.app.run("--spring.cloud.vault.authentication=cert",
133+
"--spring.cloud.vault.ssl.key-store=file:../work/client-cert.jks",
134+
"--spring.cloud.vault.ssl.key-store-password=changeit",
135+
"--spring.cloud.vault.application-name=VaultConfigTlsCertAuthenticationTests",
136+
"--spring.cloud.vault.reactive.enabled=false", "--spring.cloud.bootstrap.enabled=true");
137+
138+
assertThat(capturedOutput.getOut()).contains("org.springframework.vault.VaultException: Status 403 Forbidden");
139+
assertNull(context.getEnvironment().getProperty("vault.value"));
140+
141+
vaultOperations.delete("auth/cert/certs/another-role");
142+
}
143+
144+
@TestConfiguration
145+
public static class TestConfig {
107146

108147
}
109148

0 commit comments

Comments
 (0)