Enhance DataSourceProperties configuration bean with KMS provided secret - spring-boot

I want to set the password for the datasource programatically and still use auto configuration of Spring GCP and Spring Data.
Background
I want to obtain my password as encrypted blob and decrypt it via Google KMS. I read that spring gcp auto configuration mutates the DataSourceProperties bean to apply gcp secific configurations (https://docs.spring.io/spring-cloud-gcp/docs/1.0.0.RELEASE/reference/html/_spring_jdbc.html#_literal_datasource_literal_creation_flow).
Now I would like to mutate the DataSourceProperties Bean exposed by GcpCloudSqlAutoConfiguration once more.
Exposing the DataSourceProperties as follows does not work because GcpCloudSqlAutoConfiguration then doesn't know which Bean to take: the one configured by spring boot auto configuration or mine. But mine should be applied in the very last step of configuration.
#Bean
public DataSourceProperties dataSourceProperties(DataSourceProperties dataSourceProperties) {
dataSourceProperties.setPassword(getDecryptedSecret());
return dataSourceProperties;
}
private String getDecryptedSecret() {
// get encrypted password and decrypt it
return "myDecryptedPassword"
}

You should be able to provide your own bootstrap property source that contains a value for spring.datasource.password.
See: https://cloud.spring.io/spring-cloud-commons/multi/multi__spring_cloud_context_application_context_services.html#customizing-bootstrap-property-sources
Full answer is on project GitHub: https://github.com/spring-cloud/spring-cloud-gcp/issues/2330

Cloud KMS offers encrypting and decrypting data with a symmetric key[1] and/or an asymmetric key[2].
You can see DataSourceProperties[3] for more of the supported options for Spring Boot.
[1]-https://cloud.google.com/kms/docs/encrypt-decrypt
[2]-https://cloud.google.com/kms/docs/encrypt-decrypt-rsa
[3]-https://github.com/spring-projects/spring-boot/blob/v2.1.8.RELEASE/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jdbc/DataSourceProperties.java

Related

OAuth2 Spring Security 5.2.x from Spring Security Oauth 2.x access authenticated oauth/token_key getting 403

When calling
oauth/token_key
To retrieve the OAuth public key for example
{
"alg":"SHA256withRSA",
"value":"-----BEGIN PUBLIC KEY----- nMAABIjANBgkq… -----END PUBLIC KEY-----"
}
How do we pass the credentials when calling this via Spring OAUTH.
Previously it seems these properties were set with
security.oauth2.resource.jwt.key-uri=http://localhost:8080/oauth/token_key
security.oauth2.client.client-id=clientID
security.oauth2.client.client-secret=clientPassword
In the migration guide I cannot see any appropriate migration for this or via the config.
As we now use config like this I believe to set the jwt.key-uri
spring.security.oauth2.client.resourceserver.jwt.jwk-set-uri=
But no equivalent place to set any id or secret.
Thanks
So it is probably an edge case that a public key is protected like this and legacy OAuth server. I could not find any documentation at this time to describe a solution that worked. It does seem like that Spring Security OAuth is in a large transition. I did not want to roll back to deprecated code so I defined my own bean for the NimbusReactiveJWTDecoder.
This allowed me to pass basic auth when getting the public key at start up time of the application.
Sample code in Koltin is:
#Bean
fun jwtDecoderByPublicKey(webclient: WebClient): NimbusReactiveJWTDecorder? {
webclient
.get()
.headers { headers -> headers.setBasicAuth("username","password") }
.retrieve()
.bodyToMono<>()
.map {
KeyFactory.getInstance("RSA")
.generatePublic(X509EncodedKeySpec(getKeySpec(it.value))) as RSAPublicKey
}
.map {
NimbusReactiveJWTDecoder.withPublicKey(it)
.signatureAlgorithm(SignatureAlgorithm.form("RS256")).build()
}.block()
Code inspired by the auto configuration bean if property given.
Current best solution I can find.

Spring boot application properties load process change programatically to improve security

I have spring boot micro-service with database credentials define in the application properties.
spring.datasource.url=<<url>>
spring.datasource.username=<<username>>
spring.datasource.password=<<password>>
We do not use spring data source to create the connection manually. Only Spring create the database connection with JPA.(org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration)
We only provide the application properties, but spring create the connections automatically to use with the database connection pool.
Our requirement to enhance the security without using db properties in clear text. Two possible methods.
Encrypt the database credentials
Use the AWS secret manager. (then get the credential with the application load)
For the option1, jasypt can be used, since we are just providing the properties only and do not want to create the data source manually, how to do to understand by the spring framework is the problem. If better I can get some working sample or methods.
Regarding the option-2,
first we need to define secretName.
use the secertName and get the database credentials from AWS secret manager.
update the application.properties programatically to understand by spring framework. (I need to know this step)
I need to use either option1 and option2. Mentioned the issues with each option.
What you could do is use environment variables for your properties. You can use them like this:
spring.datasource.url=${SECRET_URL}
You could then retrieve these and start your Spring process using a ProcessBuilder. (Or set the variables any other way)
I have found the solution for my problem.
We need to define org.springframework.context.ApplicationListenerin spring.factories file. It should define the required application context listener like below.
org.springframework.context.ApplicationListener=com.sample.PropsLoader
PropsLoader class is like this.
public class PropsLoader implements ApplicationListener<ApplicationEnvironmentPreparedEvent> {
#Override
public void onApplicationEvent(ApplicationEnvironmentPreparedEvent event) {
ConfigurableEnvironment environment = event.getEnvironment();
String appEnv = environment.getProperty("application.env");
//set new properties based on the application environment.
// calling other methods and depends on the enviornment and get the required value set
Properties props = new Properties();
props.put("new_property", "value");
environment.getPropertySources().addFirst(new PropertiesPropertySource("props", props));
}
}
spring.factories file should define under the resources package and META-INF
folder.
This will set the application context with new properties before loading any other beans.

Securing spring cloud config credentials using vault

I’m using spring-cloud-config-server with Git as a backend. I want to secure the git credentials of my private repository (username and password) to be served from the vault server.
I'm thinking to extend the GitCredentialsProviderFactory and create the credentials bean using createFor() with values from the vault. Is this a right approach or there are other recommended ways?
Instead of using the GitCredentialsProviderFactory which I had thought of using. I used MultipleJGitEnvironmentProperties to create the GitPropert bean. And then supplied the credentials via the config values fetched from the vault.
#Bean MultipleJGitEnvironmentProperties createGitProps(){
MultipleJGitEnvironmentProperties
properties = new MultipleJGitEnvironmentProperties();
properties.setUsername(username);
properties.setUri(uri);
properties.setSearchPaths(searchPath);
properties.setPassword(password);
properties.setCloneOnStart(true);
return properties;
}

Spring Boot 2.0.0 + OAuth2

Does Spring Boot 2 + Sping Security OAuth2 still support the #AuthorizationServer annotation? From reading the release notes some things haven't been ported over:
Oauth2 Support
Here is the relevant section of my build.grade:
Auth Server
// security
compile "org.springframework.boot:spring-boot-starter-security:${springBootVersion}"
// oauth
// https://mvnrepository.com/artifact/org.springframework.security.oauth/spring-security-oauth2
compile "org.springframework.security.oauth:spring-security-oauth2:2.2.1.RELEASE"
Client Server
// support for Oauth2 user token services not yet migrated into Spring Boot 2.0
compile "org.springframework.security.oauth.boot:spring-security-oauth2-autoconfigure:2.0.1.BUILD-SNAPSHOT"
And right now my Authorization Server Oauth2 endpoints just return a 401 when i try to pass a client-id and client-secret in as Basic Authentication to /oauth/token. Passing in a username and password gives a different code path. So it looks like the OAuth filters aren't quite lined up.
I also found this as well: Spring Boot 2 OAuth2 starter changes.
Has there been a configuration update or do I need a different set of gradle dependencies to restore the Authorization Server to it's previous state?
Thanks!
UPDATE
I wanted to close the loop on this question. In addition to encrypting the client-secrets. The RedisTokenStore issue has also been resolved as of Spring OAuth 2.3.2: Spring OAuth 2.3.2
Spring Security 5 uses a modernized password storage, see OAuth2 Autoconfig:
If you use your own authorization server configuration to configure the list of valid clients through an instance of ClientDetailsServiceConfigurer as shown below, take note that the passwords you configure here are subject to the modernized password storage that came with Spring Security 5.
To solve your problem, see Spring Security Reference:
Troubleshooting
The following error occurs when one of the passwords that are stored has no id as described in the section called “Password Storage Format”.
java.lang.IllegalArgumentException: There is no PasswordEncoder mapped for the id "null"
at org.springframework.security.crypto.password.DelegatingPasswordEncoder$UnmappedIdPasswordEncoder.matches(DelegatingPasswordEncoder.java:233)
at org.springframework.security.crypto.password.DelegatingPasswordEncoder.matches(DelegatingPasswordEncoder.java:196)
The easiest way to resolve the error is to switch to explicitly provide the PasswordEncoder that you passwords are encoded with. The easiest way to resolve it is to figure out how your passwords are currently being stored and explicitly provide the correct PasswordEncoder. If you are migrating from Spring Security 4.2.x you can revert to the previous behavior by exposing a NoOpPasswordEncoder bean. For example, if you are using Java Configuration, you can create a configuration that looks like:
Reverting to NoOpPasswordEncoder is not considered to be secure. You should instead migrate to using DelegatingPasswordEncoder to support secure password encoding.
#Bean
public static NoOpPasswordEncoder passwordEncoder() {
return NoOpPasswordEncoder.getInstance();
}
if you are using XML configuration, you can expose a PasswordEncoder with the id passwordEncoder:
<b:bean id="passwordEncoder"
class="org.springframework.security.crypto.NoOpPasswordEncoder" factory-method="getInstance"/>
Alternatively, you can prefix all of your passwords with the correct id and continue to use DelegatingPasswordEncoder. For example, if you are using BCrypt, you would migrate your password from something like:
$2a$10$dXJ3SW6G7P50lGmMkkmwe.20cQQubK3.HZWzG3YB1tlRy.fqvM/BG
to
{bcrypt}$2a$10$dXJ3SW6G7P50lGmMkkmwe.20cQQubK3.HZWzG3YB1tlRy.fqvM/BG
OAuth2 AuthorizationServer uses basic authentication.
So, you also need to encode your client secret with delegatedPasswordEncoder in AuthorizationServerConfig to completely solve "There is no PasswordEncoder mapped for the id "null" " exception.
Yao Liu's answer solved my problem.
1) created a bean to auto wire PasswordEncoder;
#Bean
public PasswordEncoder passwordEncoder() {
String idForEncode = "bcrypt";
Map<String, PasswordEncoder> encoderMap = new HashMap<>();
encoderMap.put(idForEncode, new BCryptPasswordEncoder());
return new DelegatingPasswordEncoder(idForEncode, encoderMap);
}
2) Auto wired passwordEncoder in AuthorizationServerConfig class;
#Autowired
private PasswordEncoder passwordEncoder;
3) encoded CLIENT_SECRET with passwordEncoder.
#Override
public void configure(ClientDetailsServiceConfigurer configurer) throws Exception {
configurer
.inMemory()
.withClient(CLIENT_ID)
.secret(passwordEncoder.encode(CLIENT_SECRET))
.authorizedGrantTypes(GRANT_TYPE_FOR_LOGIN, GRANT_TYPE_FOR_REFRESH)
.scopes(SCOPE_READ, SCOPE_WRITE)
.accessTokenValiditySeconds(TOKEN_VALIDITY_SECONDS)
.refreshTokenValiditySeconds(TOKEN_VALIDITY_SECONDS)
.resourceIds(RESOURCES_IDS);
}
That's it.
As #false_memories stated above, with Spring Boot 2 you need to encode your secret. In my project, it looks like:
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
String secretEncoded = passwordEncoder().encode("secret");
clients.inMemory().withClient("some-web-app").secret(secretEncoded).accessTokenValiditySeconds(expiration)
.scopes("read", "write").authorizedGrantTypes("password", "refresh_token").resourceIds("resource");
}
I wanted to close the loop on this question. In addition to encrypting the client-secrets. The RedisTokenStore issue has also been resolved as of Spring OAuth 2.3.2: Spring OAuth 2.3.2

Implement multi-tenanted application with Keycloak and springboot

When we use 'KeycloakSpringBootConfigResolver' for reading the keycloak configuration from Spring Boot properties file instead of keycloak.json.
Now there are guidelines to implement a multi-tenant application using keycloak by overriding 'KeycloakConfigResolver' as specified in http://www.keycloak.org/docs/2.3/securing_apps_guide/topics/oidc/java/multi-tenancy.html.
The steps defined here can only be used with keycloak.json.
How can we adapt this to a Spring Boot application such that keycloak properties are read from the Spring Boot properties file and multi-tenancy is achieved.
You can access the keycloak config you secified in your application.yaml (or application.properties) if you inject org.keycloak.representations.adapters.config.AdapterConfig into your component.
#Component
public class MyKeycloakConfigResolver implements KeycloakConfigResolver {
private final AdapterConfig keycloakConfig;
public MyKeycloakConfigResolver(org.keycloak.representations.adapters.config.AdapterConfig keycloakConfig) {
this.keycloakConfig = keycloakConfig;
}
#Override
public KeycloakDeployment resolve(OIDCHttpFacade.Request request) {
// make a defensive copy before changing the config
AdapterConfig currentConfig = new AdapterConfig();
BeanUtils.copyProperties(keycloakConfig, currentConfig);
// changes stuff here for example compute the realm
return KeycloakDeploymentBuilder.build(currentConfig);
}
}
After several trials, the only feasible option for spring boot is to have
Multiple instances of the spring boot application running with different spring 'profiles'.
Each application instance can have its own keycloak properties (as it is under different profiles) including the realm.
The challenge is to have an upgrade path for all instances for version upgrades/bug fixes, but I guess there are multiple strategies already implemented (not part of this discussion)
there is a ticket regarding this problem: https://issues.jboss.org/browse/KEYCLOAK-4139?_sscc=t
Comments for that ticket also talk about possible workarounds intervening in servlet setup of the service used (Tomcat/Undertow/Jetty), which you could try.
Note that the documentation you linked in your first comment is super outdated!

Resources