Spring boot JDBC with password lease/renew (as in Vault) - spring-boot

Hashicorp's Vault can be set up to provide database passwords on demand; each password can be used for a certain "lease" period (say 1 hour) before being renewed, and a maximum use period can be set after which the password has to be trashed and a new one obtained.
In Spring Boot, the JDBC connection is configured at application start, and it is assumed that the JDBC password is coded in the application.properties file (or, alternatively, obtained at application bootstrap time via Spring Cloud Config or equivalent) and used forever.
QUESTION: How might I implement a way in Spring Boot to reset the JDBC password by accessing Vault when a connection attempt fails due to an expired password?
Is there a way to set some sort of handler gets invoked when the connection fails due to an old password, and resets it to a new value?

Check out this open source project available on GitHub; I think it might just be what you are looking for. Note: From the looks of it, this is currently a Spring Cloud Incubator project (has the potential of becoming an official Spring endorsed open source library in the future), and there are only three contributors. You would have to see if it is "reliable enough" to suit your needs.
https://github.com/spring-cloud-incubator/spring-cloud-vault-config
--- Here's a quick summary of useful information ---
Add the following dependency to pom.xml:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-vault-starter-config</artifactId>
<version>x.y.z</version>
</dependency>
Create a standard Spring Boot application - provided example is just a main application class:
#SpringBootApplication
#RestController
public class Application {
#RequestMapping("/")
public String home() {
return "Hello World!";
}
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
When it runs it will pick up the external configuration from the
default local Vault server on port 8200 if it is running. To modify
the startup behavior you can change the location of the Vault server
using bootstrap.properties (like application.properties but for the
bootstrap phase of an application context), e.g.
bootstrap.yml:
spring.cloud.vault:
host: localhost
port: 8200
scheme: http
connection-timeout: 5000
read-timeout: 15000
host sets the hostname of the Vault host. The host name will be used for SSL certificate validation
port sets the Vault port
scheme setting the scheme to http will use plain HTTP. Supported schemes are http and https.
connection-timeout sets the connection timeout in milliseconds
read-timeout sets the read timeout in milliseconds

Related

Spring-boot LDAP - Property 'userDn' not set

I am running a Spring-boot application which authenticates users via our internal LDAP with spring-security-ldap.
By default it binds with LDAP anonymously.
Property 'userDn' not set - anonymous context will be used for read-write operations
But I want the first bind to be with current username.
Where should I specify the userDn attribute?
Thank you for your advice
When using spring ldap maybe you started from one many tutorials on the web but main of them uses embedded ldap server; embdedded server uses ldif file and doesn't need the manager credetials.
When connecting to an external ldap server you need to specify userDn setting it via managerDn method. Here the snippet of code
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.ldapAuthentication().contextSource().managerDn("uid=admin,ou=system")
.managerPassword("secret")
.......
}
Obviously you need to provide also all the other infos like url, port, etc (and userSearchBase like mvreijn told).
I am not the most knowledgeable person regarding Spring-boot, more so regarding LDAP.
That said, your LDAP configuration properties should be mentioned in your application.properties file and are named spring.ldap.*.
They are mentioned in the documentation here.
When initializing your authentication provider, you can pass important properties like the Base DN (root to search from) and the filter using:
.userSearchBase("ou=<your users container>").userSearchFilter("(uid={0})")
Most likely, your search filter will be uid={0} or cn={0}.

Spring Cloud Config client doesn't get values from server

I have been following this tutorial from Baeldung in setting up a Spring Cloud Config server and client. I believe I have set up the server correctly since going to http://localhost:8888/service-config/development/master will seemingly correctly show the json with the user.role defined:
{"name":"service-config","profiles":["development"],"label":"master","version":"d30a6015a6b8cb1925f6e61cee22721f331e5783","state":null,"propertySources":[{"name":"file:///C:.../git/service-config-data/service-config-development.properties","source":{"user.role":"Developer"}},{"name":"file:///C:.../git/service-config-data/service-config.properties","source":{"user.role":"Developer"}}]}
However, I cannot seem to correctly connect the Spring Cloud Config client to the server - the client fails to run with cannot resolve placeholder errors on the #Value("${user.role}") annotation. I've tried adding a default value to the annotation and then manually refreshing the values (using the #refreshscope annotation), but to no avail.
I've looked at the debug and trace logs by running the tools with --debug --trace flags but I cannot find when/if the client is making the call to the server and which config-data .properties file the server is actually looking for. I am a bit lost on how to proceed.
Here is my server application.properties:
spring.application.name=service-config
server.port=8888
spring.cloud.config.server.git.uri=file:///${user.home}/git/service-config-data
spring.cloud.config.server.git.clone-on-start=false
spring.security.user.name=root
spring.security.user.password=toor
And inside the service-config-data folder, there are files with this inside:
user.role=Developer
I have tried files called service-config.properties, service-config-client.properties, service-config-development.properties, and service-config-client-development.properties, and none of them seem to pull through the user.role.
Here is my client:
#SpringBootApplication
#RestController
#RefreshScope
public class ServiceConfigClient {
#Value("${user.role:unknown}")
private String role;
#RequestMapping(
value = "/whoami/{username}",
method = RequestMethod.GET,
produces = MediaType.TEXT_PLAIN_VALUE)
public String whoami(#PathVariable("username") String username) {
return String.format("Hello! You're %s and you'll become a(n) %s...\n", username, role);
}
}
And my client bootstrap.properties:
spring.application.name=service-config-client
spring.profiles.active=development
spring.cloud.config.uri=http://localhost:8888
spring.cloud.config.username=root
spring.cloud.config.password=toor
management.endpoints.web.exposure.include=*
How do I correctly connect the client to the server so that it can successfully pull the config data? Thank you.
My problem was fixed by adding spring.cloud.config.name=service-config to the bootstrap.properties of the service-config-client as well as adding spring-cloud-config-client to and removing spring-cloud-config-server from my pom.xml
There is confusion about client service name and corresponding property sources in config server.
You need to do one of the following:
Change client service name:
//client bootstrap.properties
spring.application.name=service-config
Change folder name in git repo from service-config to service-config-client

Spring Config-Client doesn't refresh if Config-Server is down during initial startup

I am running a test with a barebones Spring cloud config-server and a client-application. I executed a refresh scenario (by calling /refresh endpoint on the client-application)
after config-server was down initially. Here is what I found
Client starts up with locally packaged properties when config-server is not reachable on startup. (I have the properties in application.yml that is bundled with client-application)
Git backend has different values for the properties compared to locally packaged version. Config-server is aware of the changes in git (Confirmed by connecting directly to config-server)
I bring up config-server and do a POST to /refresh endpoint on the client-application.
Client-application is not aware of the new properties from config-server.
In the second usecase
Client-application starts up and connects to config-server successfully. I see that the values from config-server have been fetched by the client-application successfully
I make a change in Git and call the /refresh endpoint on the client-application. Properties are refreshed successfully.
At this point it looks like /refresh doesn't work if the client-application comes up initially without being able to successfully connect to config-server. I am doing this to test
a fallback strategy for the client-application if config-server is not reachable when the client-application is starting up. (The fallback strategy is to have locally packaged properties
that will be used if config-server is not available on startup. If the config-server is available then the local properties are overriden). Any pointers to why this is not working and
what I could do differently? Thanks in advance.
Edit
Server-Code
#EnableConfigServer
#SpringBootApplication
public class ConfigServerApplication {
public static void main(String[] args) {
SpringApplication.run(ConfigServerApplication.class, args);
}
}
Client-Code
#RestController
#RefreshScope
#Component
public class Greeter {
#Value("${message.greeting}")
String greeting;
#RequestMapping(value = "/",produces = "application/json")
public List<String> index(){
List<String> env = Arrays.asList("message.greeting: " + greeting);
return env;
}
}
bootstrap.yml (On config-client application)
spring:
application:
name: configclient
cloud:
config:
uri: http://localhost:8888
management:
security:
enabled: false
logging:
config: classpath:logback.xml
server:
port: 8000
application.yml
message:
greeting: Hello from Local!
Config in Git (Served through config-server)
message:
greeting: Hello from Git-Edited!
According to spring-cloud-config documentation -
If you expect that the config server may occasionally be unavailable
when your app starts, you can ask it to keep trying after a failure.
First you need to set spring.cloud.config.failFast=true, and then you
need to add spring-retry and spring-boot-starter-aop to your
classpath. The default behaviour is to retry 6 times with an initial
backoff interval of 1000ms and an exponential multiplier of 1.1 for
subsequent backoffs. You can configure these properties (and others)
using spring.cloud.config.retry.* configuration properties.
Reference -> http://cloud.spring.io/spring-cloud-static/spring-cloud-config/1.3.1.RELEASE/

Port binding for Spring RESTful Web Service

I've used the Building a RESTful Web Service tutorial to build a WebService for my purpose. It was quite easy, but now I'm struggeling to configure the port the WebService should be binded through. There is no config.xml or anything to configure in this project. Can anyone give me a hint about how to configure the WebService's port?
As these details might be helpful. I'm starting the server with the code below, containing the #EnableAutoConfiguration tag. The configuration is done by Spring Boot.
#ComponentScan
#EnableAutoConfiguration
public class ServerStarter{
public static void main(String[] args) {
SpringApplication.run(ServerStarter.class, args);
}
}
To configure the ports you can set the server.port and management.port properties by writing the following in the "application.properties" file located under "src/main/resources":
server.port = 9000
management.port = 9001
Similarly if you need to specify the management address:
management.address = 127.0.0.1
There are more properties you can set for the server and management server. See spring-boot's documentation: http://docs.spring.io/spring-boot/docs/current-SNAPSHOT/reference/htmlsingle/
For a quick and dirty solution you can employ the command line option arguments (source):
--server.port=9000

How can I specify my .keystore file with Spring Boot and Tomcat?

I'm trying to set up Spring Security to work with Spring Boot's embedded Tomcat instance. There are quite a few basic samples that do this but I'm stuck where they leave off -- they do basic authentication over HTTP (not HTTPS).
I could probably make it work if I had access to the Tomcat configuration files (server.xml) but since Spring Boot uses an embedded Tomcat instance (which is otherwise a huge convenience), I dont have access to the Tomcat configuration files (at least, not to my knowledge).
There may be an application.properties setting for this but I haven't been able to track it down. I've seen references to a server.contextPath field in application.properties that I suspect may have something to do with replacement Tomcat config files. Even if it is related, I wouldn't know where to begin anyway -- all of the Tomcat SSL instructions I've seen start with editing an existing server.xml file, not building one from scratch.
Can this be done with Spring Boot (either by somehow specifying a snippet of server.xml or through other means)? If not, what would be the simplest way to do this? I understand that I may need to exclude the Tomcat component of Spring Boot but I'd prefer to avoid that if possible.
Starting with Spring Boot 1.2, you can configure SSL using application.properties or application.yml. Here's an example for application.properties:
server.port = 8443
server.ssl.key-store = classpath:keystore.jks
server.ssl.key-store-password = secret
server.ssl.key-password = another-secret
Same thing with application.yml:
server:
port: 8443
ssl:
key-store: classpath:keystore.jks
key-store-password: secret
key-password: another-secret
Here's a link to the current reference documentation.
For external keystores, prefix with "file:"
server.ssl.key-store=file:config/keystore
It turns out that there is a way to do this, although I'm not sure I've found the 'proper' way since this required hours of reading source code from multiple projects. In other words, this might be a lot of dumb work (but it works).
First, there is no way to get at the server.xml in the embedded Tomcat, either to augment it or replace it. This must be done programmatically.
Second, the 'require_https' setting doesn't help since you can't set cert info that way. It does set up forwarding from http to https, but it doesn't give you a way to make https work so the forwarding isnt helpful. However, use it with the stuff below, which does make https work.
To begin, you need to provide an EmbeddedServletContainerFactory as explained in the Embedded Servlet Container Support docs. The docs are for Java but the Groovy would look pretty much the same. Note that I haven't been able to get it to recognize the #Value annotation used in their example but its not needed. For groovy, simply put this in a new .groovy file and include that file on the command line when you launch spring boot.
Now, the instructions say that you can customize the TomcatEmbeddedServletContainerFactory class that you created in that code so that you can alter web.xml behavior, and this is true, but for our purposes its important to know that you can also use it to tailor server.xml behavior. Indeed, reading the source for the class and comparing it with the Embedded Tomcat docs, you see that this is the only place to do that. The interesting function is TomcatEmbeddedServletContainerFactory.addConnectorCustomizers(), which may not look like much from the Javadocs but actually gives you the Embedded Tomcat object to customize yourself. Simply pass your own implementation of TomcatConnectorCustomizer and set the things you want on the given Connector in the void customize(Connector con) function. Now, there are about a billion things you can do with the Connector and I couldn't find useful docs for it but the createConnector() function in this this guys personal Spring-embedded-Tomcat project is a very practical guide. My implementation ended up looking like this:
package com.deepdownstudios.server
import org.springframework.boot.context.embedded.tomcat.TomcatConnectorCustomizer
import org.springframework.boot.context.embedded.EmbeddedServletContainerFactory
import org.springframework.boot.context.embedded.tomcat.TomcatEmbeddedServletContainerFactory
import org.apache.catalina.connector.Connector;
import org.apache.coyote.http11.Http11NioProtocol;
import org.springframework.boot.*
import org.springframework.stereotype.*
#Configuration
class MyConfiguration {
#Bean
public EmbeddedServletContainerFactory servletContainer() {
final int port = 8443;
final String keystoreFile = "/path/to/keystore"
final String keystorePass = "keystore-password"
final String keystoreType = "pkcs12"
final String keystoreProvider = "SunJSSE"
final String keystoreAlias = "tomcat"
TomcatEmbeddedServletContainerFactory factory =
new TomcatEmbeddedServletContainerFactory(this.port);
factory.addConnectorCustomizers( new TomcatConnectorCustomizer() {
void customize(Connector con) {
Http11NioProtocol proto = (Http11NioProtocol) con.getProtocolHandler();
proto.setSSLEnabled(true);
con.setScheme("https");
con.setSecure(true);
proto.setKeystoreFile(keystoreFile);
proto.setKeystorePass(keystorePass);
proto.setKeystoreType(keystoreType);
proto.setProperty("keystoreProvider", keystoreProvider);
proto.setKeyAlias(keystoreAlias);
}
});
return factory;
}
}
The Autowiring will pick up this implementation an run with it. Once I fixed my busted keystore file (make sure you call keytool with -storetype pkcs12, not -storepass pkcs12 as reported elsewhere), this worked. Also, it would be far better to provide the parameters (port, password, etc) as configuration settings for testing and such... I'm sure its possible if you can get the #Value annotation to work with Groovy.
If you don't want to implement your connector customizer, you can build and import the library (https://github.com/ycavatars/spring-boot-https-kit) which provides predefined connector customizer. According to the README, you only have to create your keystore, configure connector.https.*, import the library and add #ComponentScan("org.ycavatars.sboot.kit"). Then you'll have HTTPS connection.
And here's an example of the customizer implemented in Groovy:
https://github.com/UniconLabs/orville/blob/master/web/src/main/groovy/org/apereo/openregistry/config/TomcatSslConfiguration.groovy

Resources