Skip to content

Commit 6901fe8

Browse files
committed
Add radius auth integration tests
Signed-off-by: Issam El-atif <issam.elatif@gmail.com>
1 parent fb022fc commit 6901fe8

File tree

1 file changed

+165
-0
lines changed

1 file changed

+165
-0
lines changed
Lines changed: 165 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,165 @@
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;
18+
19+
import java.util.Collections;
20+
import java.util.Map;
21+
22+
import org.junit.jupiter.api.BeforeAll;
23+
import org.junit.jupiter.api.Test;
24+
import org.testcontainers.containers.GenericContainer;
25+
import org.testcontainers.containers.Network;
26+
import org.testcontainers.containers.wait.strategy.Wait;
27+
import org.testcontainers.images.builder.Transferable;
28+
import org.testcontainers.junit.jupiter.Container;
29+
import org.testcontainers.junit.jupiter.Testcontainers;
30+
import org.testcontainers.vault.VaultContainer;
31+
32+
import org.springframework.beans.factory.annotation.Value;
33+
import org.springframework.boot.SpringApplication;
34+
import org.springframework.boot.autoconfigure.SpringBootApplication;
35+
import org.springframework.cloud.vault.util.VaultTestContextRunner;
36+
import org.springframework.http.client.SimpleClientHttpRequestFactory;
37+
import org.springframework.vault.authentication.TokenAuthentication;
38+
import org.springframework.vault.client.VaultEndpoint;
39+
import org.springframework.vault.core.VaultTemplate;
40+
import org.springframework.vault.support.VaultMount;
41+
42+
import static org.assertj.core.api.Assertions.assertThat;
43+
44+
/**
45+
* Integration test using config infrastructure with RADIUS authentication.
46+
*
47+
* <p>
48+
* This test uses Testcontainers to run both Vault and FreeRADIUS in a shared Docker
49+
* network. Testcontainers is required here because Vault's RADIUS auth method uses UDP
50+
* protocol for authentication. UDP port forwarding from Docker to the host is unreliable,
51+
* so both containers must be in the same network to communicate directly.
52+
*
53+
* @author Issam El-atif
54+
*/
55+
@Testcontainers
56+
class VaultConfigRadiusTests {
57+
58+
private static final String VAULT_TOKEN = "00000000-0000-0000-0000-000000000000";
59+
60+
private static final String RADIUS_USERNAME = "testuser";
61+
62+
private static final String RADIUS_PASSWORD = "testpass";
63+
64+
private static final String RADIUS_SECRET = "testing123";
65+
66+
private static final String RADIUS_ALIAS = "freeradius";
67+
68+
static Network network = Network.newNetwork();
69+
70+
// FreeRADIUS users config with test user
71+
private static final String USERS_CONFIG = "%s Cleartext-Password := \"%s\"%n".formatted(RADIUS_USERNAME,
72+
RADIUS_PASSWORD);
73+
74+
// Custom FreeRADIUS clients config allowing any client
75+
private static final String CLIENTS_CONFIG = """
76+
client localhost {
77+
ipaddr = 127.0.0.1
78+
secret = testing123
79+
}
80+
client any {
81+
ipaddr = 0.0.0.0/0
82+
secret = %s
83+
}
84+
""".formatted(RADIUS_SECRET);
85+
86+
@Container
87+
static GenericContainer<?> radiusContainer = new GenericContainer<>("freeradius/freeradius-server:latest")
88+
.withNetwork(network)
89+
.withNetworkAliases(RADIUS_ALIAS)
90+
.withCommand("-X")
91+
.withCopyToContainer(Transferable.of(USERS_CONFIG), "/etc/freeradius/mods-config/files/authorize")
92+
.withCopyToContainer(Transferable.of(CLIENTS_CONFIG), "/etc/freeradius/clients.conf")
93+
.waitingFor(Wait.forLogMessage(".*Ready to process requests.*\\n", 1));
94+
95+
@Container
96+
static VaultContainer<?> vaultContainer = new VaultContainer<>("hashicorp/vault:1.13.3").withVaultToken(VAULT_TOKEN)
97+
.withNetwork(network);
98+
99+
VaultTestContextRunner contextRunner = VaultTestContextRunner.of(VaultConfigRadiusTests.class)
100+
.withAuthentication(VaultProperties.AuthenticationMethod.RADIUS)
101+
.withConfiguration(TestApplication.class)
102+
.withProperties("spring.cloud.vault.uri", vaultContainer.getHttpHostAddress())
103+
.withProperties("spring.cloud.vault.radius.username", RADIUS_USERNAME)
104+
.withProperties("spring.cloud.vault.radius.password", RADIUS_PASSWORD)
105+
.withSettings(VaultTestContextRunner.TestSettings::bootstrap);
106+
107+
@BeforeAll
108+
static void beforeClass() {
109+
VaultTemplate vaultTemplate = createVaultTemplate();
110+
configureVaultSecrets(vaultTemplate);
111+
configureRadiusAuth(vaultTemplate);
112+
}
113+
114+
private static VaultTemplate createVaultTemplate() {
115+
VaultEndpoint endpoint = VaultEndpoint.from(vaultContainer.getHttpHostAddress());
116+
return new VaultTemplate(endpoint, new SimpleClientHttpRequestFactory(),
117+
() -> new TokenAuthentication(VAULT_TOKEN).login());
118+
}
119+
120+
private static void configureVaultSecrets(VaultTemplate vaultTemplate) {
121+
vaultTemplate.opsForSys().unmount("secret");
122+
vaultTemplate.opsForSys()
123+
.mount("secret", VaultMount.builder().type("kv").options(Collections.singletonMap("version", "1")).build());
124+
125+
vaultTemplate.write("secret/" + VaultConfigRadiusTests.class.getSimpleName(),
126+
Collections.singletonMap("vault.value", "foo"));
127+
}
128+
129+
private static void configureRadiusAuth(VaultTemplate vaultTemplate) {
130+
vaultTemplate.opsForSys().authMount("radius", VaultMount.create("radius"));
131+
132+
String rules = """
133+
path "*" {
134+
capabilities = ["read", "list"]
135+
}
136+
""";
137+
vaultTemplate.write("sys/policy/testpolicy", Collections.singletonMap("rules", rules));
138+
139+
// Configure RADIUS auth to point to the FreeRADIUS container by network alias
140+
vaultTemplate.write("auth/radius/config", Map.of("host", RADIUS_ALIAS, "secret", RADIUS_SECRET));
141+
142+
vaultTemplate.write("auth/radius/users/" + RADIUS_USERNAME, Map.of("policies", "testpolicy"));
143+
}
144+
145+
@Test
146+
void contextLoads() {
147+
this.contextRunner.run(ctx -> {
148+
TestApplication application = ctx.getBean(TestApplication.class);
149+
assertThat(application.configValue).isEqualTo("foo");
150+
});
151+
}
152+
153+
@SpringBootApplication
154+
public static class TestApplication {
155+
156+
@Value("${vault.value}")
157+
String configValue;
158+
159+
public static void main(String[] args) {
160+
SpringApplication.run(TestApplication.class, args);
161+
}
162+
163+
}
164+
165+
}

0 commit comments

Comments
 (0)