Reputation: 85
I installed Vault locally. I was able to start local dev server and write/read some secrets into Vault kv based on this official tutorial https://learn.hashicorp.com/vault/
Then I wanted to create some very basic Java/Spring Boot demo client that would connect to my local Vault dev server in order to write/read secrets. I read Baeldung tutorial for inspiration https://www.baeldung.com/spring-vault.
This is my vault-config.properties:
vault.uri=http://127.0.0.1:8200
vault.token=s.EXg6MQwUuB63Z7Xra4zybOut (token generated after the latest start of server)
Then service class:
@Service
public class CredentialsService {
@Autowired
private VaultTemplate vaultTemplate;
public void secureCredentials(Credentials credentials) throws URISyntaxException {
vaultTemplate.write("credentials/myapp", credentials);
}
public Credentials accessCredentials() throws URISyntaxException {
VaultResponseSupport<Credentials> response = vaultTemplate.read("credentials/myapp", Credentials.class);
return response.getData();
}
}
Configuration class:
@Configuration
public class VaultConfig extends AbstractVaultConfiguration {
@Override
public ClientAuthentication clientAuthentication() {
return new TokenAuthentication("s.EXg6MQwUuB63Z7Xra4zybOut");
}
@Override
public VaultEndpoint vaultEndpoint() {
return VaultEndpoint.create("host", 8200);
}
}
and this:
@Configuration
@PropertySource(value = { "vault-config.properties" })
@Import(value = EnvironmentVaultConfiguration.class)
public class VaultEnvironmentConfig {
}
One domain object:
public class Credentials {
private String username;
private String password;
public Credentials() {
}
public Credentials(String username, String password) {
this.username = username;
this.password = password;
}
public String getUsername() {
return username;
}
public String getPassword() {
return password;
}
@Override
public String toString() {
return "Credential [username=" + username + ", password=" + password + "]";
}
}
And finally my main Spring Boot class:
@RestController
@ComponentScan
@SpringBootApplication
public class SpringVaultTutorial {
@Autowired
CredentialsService credentialsService;
@RequestMapping("/")
String home() throws URISyntaxException {
Credentials credentials = new Credentials("oliver","exxeta123");
credentialsService.secureCredentials(credentials);
return credentialsService.accessCredentials().getUsername().toString();
}
public static void main(String[] args) {
SpringApplication.run(SpringVaultTutorial.class, args);
}
}
Main class should write secret and then immediately read it and print username. But I am getting this error message:
There was an unexpected error (type=Internal Server Error, status=500). I/O error on POST request for "https://host:8200/v1/credentials/myapp": host; nested exception is java.net.UnknownHostException: host
Does somebody have a clue what can be wrong?
EDIT: Based on advice from Arun I followed this tutorial https://drissamri.be/blog/java/enable-https-in-spring-boot/
I have been trying both approaches. 1) Modify application.properties:
server.port: 8443
server.ssl.key-store: keystore.p12
server.ssl.key-store-password: oliver
server.ssl.keyStoreType: PKCS12
server.ssl.keyAlias: tomcat
security.require-ssl=true
After modification, when I call https://localhost:8443, I am getting Exception: javax.net.ssl.SSLException: Unrecognized SSL message, plaintext connection? at sun.security.ssl.InputRecord.handleUnknownRecord(InputRecord.java:710) ~[na:1.8.0_121] at sun.security.ssl.InputRecord.read(InputRecord.java:527) ~[na:1.8.0_121] at sun.security.ssl.SSLSocketImpl.readRecord(SSLSocketImpl.java:973) ~[na:1.8.0_121] at sun.security.ssl.SSLSocketImpl.performInitialHandshake(SSLSocketImpl.java:1375) ~[na:1.8.0_121] at sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1403) ~[na:1.8.0_121] at sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1387) ~[na:1.8.0_121]
2) Second approach based on tutorial is about adding ConnectorConfig class:
@Configuration
public class ConnectorConfig {
@Bean
public ServletWebServerFactory servletContainer() {
TomcatServletWebServerFactory tomcat =
new TomcatServletWebServerFactory() {
@Override
protected void postProcessContext(Context context) {
SecurityConstraint securityConstraint = new SecurityConstraint();
securityConstraint.setUserConstraint("CONFIDENTIAL");
SecurityCollection collection = new SecurityCollection();
collection.addPattern("/*");
securityConstraint.addCollection(collection);
context.addConstraint(securityConstraint);
}
};
tomcat.addAdditionalTomcatConnectors(redirectConnector());
return tomcat;
}
private Connector redirectConnector() {
Connector connector = new Connector("org.apache.coyote.http11.Http11NioProtocol");
connector.setScheme("http");
connector.setPort(8090);
connector.setSecure(false);
connector.setRedirectPort(8443);
return connector;
}
}
But after calling localhost:8090 that redirects me to https://localhost:8443, I am getting the same error: javax.net.ssl.SSLException: Unrecognized SSL message, plaintext connection? at sun.security.ssl.InputRecord.handleUnknownRecord(InputRecord.java:710) ~
Now the question is: Do I have to configure something also on the Vault side regarding certificate? Or do you think there could be some certificate problem on Java client side? But I thing if there was Java certificate problem, exception would be thrown already during startup.
Upvotes: 3
Views: 6912
Reputation: 85
Problem solved. Now I am able to connect to local Vault from Java client. I paste code here if someone in the future wants to run simple Java Client - Vault demo.
Controller:
@RestController
@RequestMapping(Paths.ROOT)
@Api(value = Paths.ROOT, description = "Endpoint for core testing")
public class Controller {
@Autowired
CredentialsService credentialsService;
@GetMapping("/")
String home() throws URISyntaxException {
Credentials credentials = new Credentials("oliver", "exxeta123");
credentialsService.secureCredentials(credentials);
return credentialsService.accessCredentials().toString();
}
@GetMapping("/test")
public String test() throws IOException {
// http://127.0.0.1:8200/v1/sys/internal/ui/mounts/secret/mysecrets
VaultConfig vc = new VaultConfig();
String bearerToken = vc.clientAuthentication().login().getToken();
System.out.println(bearerToken);
// credentialsService.accessCredentials()
// Sending get request
//URL url = new URL("http://127.0.0.1:8200/v1/sys/internal/ui/mounts/secret/mysecrets");
// URL updated to match readme.adoc
URL url = new URL("http://127.0.0.1:8200/v1/kv/my-secret");
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setRequestProperty("Authorization", "Bearer " + bearerToken);
conn.setRequestProperty("Content-Type", "application/json");
conn.setRequestMethod("GET");
BufferedReader in = new BufferedReader(new InputStreamReader(conn.getInputStream()));
String output;
StringBuffer response = new StringBuffer();
while ((output = in.readLine()) != null) {
response.append(output);
}
in.close();
// printing result from response
return "Response: - " + response.toString();
}
@GetMapping(value = { "/add/{name}/{username}/{password}" })
public ResponseEntity<String> addKey(@PathVariable(value = "name", required = false, name = "name") String name,
@PathVariable(value = "username", required = false, name = "username") String username,
@PathVariable(value = "password", required = false, name = "password") String password) throws URISyntaxException {
Credentials credentials = new Credentials(username, password);
credentialsService.secureCredentials(name, credentials);
return new ResponseEntity<>("Add success: " + credentialsService.accessCredentials(name).getUsername(), HttpStatus.OK);
}
@GetMapping(value = {"/get", "/get/{name}"})
public ResponseEntity<Credentials> getKey(@PathVariable(value = "name", required = false, name = "name") String name) {
return new ResponseEntity<>(credentialsService.accessCredentials(name), HttpStatus.OK);
}
@GetMapping(value= {"/delete", "/delete/{name}"})
public String removeKey(@PathVariable(value = "name", required = false, name = "name") String name) {
return "Delete success: " + credentialsService.deleteCredentials(name);
}
}
Service:
@Service
public class CredentialsService {
private VaultTemplate vaultTemplate;
/**
* To Secure Credentials
*
* @param credentials
* @return VaultResponse
* @throws URISyntaxException
*/
public void secureCredentials(Credentials credentials) throws URISyntaxException {
//vaultTemplate.write("credentials/myapp", credentials);
initVaultTemplate();
vaultTemplate.write("kv/myapp", credentials);
}
public void secureCredentials(String storagePlace, Credentials credentials) {
initVaultTemplate();
vaultTemplate.write("kv/" + storagePlace, credentials);
}
/**
* To Retrieve Credentials
*
* @return Credentials
* @throws URISyntaxException
*/
public Credentials accessCredentials() throws URISyntaxException {
//VaultResponseSupport<Credentials> response = vaultTemplate.read("credentials/myapp", Credentials.class);
initVaultTemplate();
VaultResponseSupport<Credentials> response = vaultTemplate.read("kv/myapp", Credentials.class);
return response.getData();
// TODO special case when there are no values
}
/**
* @param nameOfsecrets key name
* @return if is presented or empty object
*/
public Credentials accessCredentials(String nameOfsecrets) {
initVaultTemplate();
VaultResponseSupport<Credentials> response = vaultTemplate.read("kv/" + nameOfsecrets, Credentials.class);
if (response != null) {
return response.getData();
} else {
return new Credentials();
}
}
public boolean deleteCredentials(String name) {
initVaultTemplate();
vaultTemplate.delete("kv/" + name);
return true;
}
}
private void initVaultTemplate() {
VaultEndpoint endpoint = new VaultEndpoint();
endpoint.setHost("localhost");
endpoint.setPort(8200);
endpoint.setScheme("http");
vaultTemplate = new VaultTemplate(endpoint, new VaultConfig().clientAuthentication());
}
VaultConfig:
@Configuration
public class VaultConfig extends AbstractVaultConfiguration {
@Override
public ClientAuthentication clientAuthentication() {
return new TokenAuthentication("00000000-0000-0000-0000-000000000000");
}
@Override
public VaultEndpoint vaultEndpoint() {
return VaultEndpoint.create("localhost", 8200);
}
}
VaultEnvironmentConfig:
@Configuration
@PropertySource(value = { "vault-config.properties" })
@Import(value = EnvironmentVaultConfiguration.class)
public class VaultEnvironmentConfig {
}
Main class:
@SpringBootApplication
@EnableSwagger2
public class SpringVaultTutorial {
public static void main(String[] args) {
SpringApplication.run(SpringVaultTutorial.class, args);
}
//SWAGGER DOCUMENTATION BEANS
// default group contains all endpoints
@Bean
public Docket defaultApi() {
return new Docket(DocumentationType.SWAGGER_2)
.select()
.apis(RequestHandlerSelectors.any())
.paths(PathSelectors.any())//all
.build().apiInfo(apiInfo());
}
// Management group contains Spring Actuator endpoints
@Bean
public Docket swaggerAdminEndpoints() {
return new Docket(DocumentationType.SWAGGER_2)
.groupName(Paths.ROOT)
.apiInfo(apiInfo())
.select()
.paths(PathSelectors.regex("/v1/.*"))
.build()
.forCodeGeneration(true);
}
private ApiInfo apiInfo() {
return new ApiInfoBuilder()
.title("Vault Demo Application")
.description("Demo Application using vault")
.version("1.0")
.build();
}
}
vault-config.properties:
vault.uri=http://127.0.0.1:8200 vault.token=00000000-0000-0000-0000-000000000000
application.properties:
server.port=8443
server.ssl.key-alias=selfsigned_localhost_sslserver
server.ssl.key-password=changeit
server.ssl.key-store=classpath:ssl-server.jks
server.ssl.key-store-provider=SUN
server.ssl.key-store-type=JKS
Paths:
public class Paths {
public static final String ROOT = "/v1";
}
Credentials:
public class Credentials {
private String username;
private String password;
public Credentials() {
}
public Credentials(String username, String password) {
this.username = username;
this.password = password;
}
public String getUsername() {
return username;
}
public String getPassword() {
return password;
}
@Override
public String toString() {
return "Credential [username=" + username + ", password=" + password + "]";
}
}
Upvotes: 2
Reputation: 346
UnknownHostException is due to no server is available with the name 'host'. you can either add an entry in hosts file map to localhost. or try changing the host name while creating vault as
@Override
public VaultEndpoint vaultEndpoint() {
return VaultEndpoint.create("localhost", 8200);
}
Upvotes: 1