Do I need to escape special characters in application.properties? - spring

I've special characters such as " (doublequotes), #, ~, !, %, &, }, ] in the datasource password field. When I run my springboot app and when it attempts to connect to the database I run into -
com.microsoft.sqlserver.jdbc.SQLServerException: The driver could not establish a secure connection to SQL Server by using Secure Sockets Layer (SSL) encryption. Error: "problem accessing trust store"
Caused by: java.security.KeyStoreException: problem accessing trust store
Caused by: java.io.IOException: Keystore was tampered with, or password was incorrect
Caused by: java.security.UnrecoverableKeyException: Password verification failed
My questions are :
(1) Is there a way to view what password is Spring using to attempt the connection?
(2) Do I need to escape any of the special characters in the password in the application.properties?
I was able to connect to the database using a standalone java program but I had to escape the double quotes in the password. I'm using Springboot-2.0.4, Flyway-5.2.4, jdk 1.8 and MS-SQL server on the backend.
Thanks.

application.properties: No escaping is required for your specified characters.
If you use e.g. a backslash, you'd need to escape it with another backslash, like so: C:\\Development\\mydir
application.yml: Surround value with " and escape embedded " like this \"
Note: application.yml is outside question from OP. Use either application.properties or application.yml, not both.
Tested and verified with this entry in application.properties
myapp.entry-with-special-characters=#~!%&=""
And tested again with this entry in application.yml
myapp:
entry-with-special-characters: "#~!%&=\"\""
Output value to console like this (put code inside e.g. class annotated with #SpringBootApplication, will run when application starts)
#Bean
public CommandLineRunner propertiesTest(#Value("${myapp.entry-with-special-characters}") String myVal) {
return args -> System.out.printf("Test-output for StackOverflow: %s\n", myVal);
}
With Flyway, try this instead
/**
* Override default flyway initializer to do nothing
*/
#Bean
FlywayMigrationInitializer flywayInitializer(Flyway flyway, #Value("${myapp.entry-with-special-characters}") String myVal) {
System.out.printf("Test-output for StackOverflow: %s\n", myVal);
return new FlywayMigrationInitializer(flyway, (f) ->{} );
}
Spring Boot: Hibernate and Flyway boot order
Answer to the question
Is there a way to view what password is Spring using to attempt the
connection?
can be to replace the key myapp.entry-with-special-characters in the above bean with relevant key from application.properties. Please keep in mind that passwords are secrets.
Environment used during testing:
Spring Boot 2.4.1
JDK 14.0.2
Windows 10 Home

Related

Spring Boot 2.7.1 LetsEncrypt PEM keystore throws Resource location must not be null

So I read that Spring Boot now supports PEM since 2.7.0
https://docs.spring.io/spring-boot/docs/2.7.0-SNAPSHOT/reference/htmlsingle/#howto.webserver.configure-ssl 17.3.7. Configure SSL
So I am using PEM generated by certbot.
My application.properties
spring.jpa.generate-ddl=true
spring1.jpa.hibernate.ddl-auto=update
spring.jpa.show-sql=false
spring.jpa.properties.hibernate.format_sql=false
server.port=443
server.ssl.certificate=fullchain1.pem
server.ssl.certificate.certificate-private-key=privkey1.pem
server.ssl.trust-certificate=fullchain1.pem
When I launch I get
org.springframework.context.ApplicationContextException: Unable to start web server; nested exception is org.springframework.boot.web.server.WebServerException: Could not load key store 'null'
Caused by: org.springframework.boot.web.server.WebServerException: Could not load key store 'null'
Caused by: java.lang.IllegalArgumentException: Resource location must not be null
As per the documentation SSL configuration springboot
UPDATE:
Adding the content from link to directly in the answer, as link can
get updates
SSL can be configured declaratively by setting the various
server.ssl.* properties, typically in application.properties or
application.yml. The following example shows setting SSL properties
using a Java KeyStore file:
server.port=8443
server.ssl.key-store=classpath:keystore.jks
server.ssl.key-store-password=secret
server.ssl.key-password=another-secret
The following example shows setting SSL properties using PEM-encoded
certificate and private key files:
server.port=8443
server.ssl.certificate=classpath:my-cert.crt
server.ssl.certificate-private-key=classpath:my-cert.key
server.ssl.trust-certificate=classpath:ca-cert.crt
Your properties are not correctly declared,
server.ssl.certificate.certificate-private-key=privkey1.pem should be changed to server.ssl.certificate-private-key=privkey1.pem
So this workaround works
#Configuration
public class SSLConfig {
#Bean
public ConfigurableServletWebServerFactory webServerFactory() throws Exception {
TomcatServletWebServerFactory factory = new TomcatServletWebServerFactory();
Ssl ssl = new Ssl();
ssl.setEnabled(true);
ssl.setCertificate("cert1.pem");
ssl.setCertificatePrivateKey("privkey1.pem");
ssl.setKeyStoreType("PKCS12");
ssl.setKeyStorePassword(""); // without this decrytption fails
factory.setSsl(ssl);
factory.setPort(443);
return factory;
}
}
server.ssl.key-store=file:///Users/...
Have you tried this way of setting the path? First, make sure your application up with the correct path, then dig into the next step.

Spring Cloud Discovery First Configuration Server Decryption Failed

I am trying discovery first implementation with Spring Cloud. I am getting failure for password decryption. I have .jks in the classpath. The same works when I go for Config First approach.
Following is the bootstrap.properties file for Config first approach.
eureka.instance.hostname=claims-dev
eureka.client.serviceUrl.defaultZone=http://localhost:8761/eureka/
spring.cloud.config.name=claim
spring.config.import=configserver:http://localhost:8888
spring.profiles.active=dev
spring.cloud.config.fail-fast=true
Following is the bootstrap for Discovery First approach.
eureka.instance.hostname=claims-dev
eureka.client.serviceUrl.defaultZone=http://localhost:8761/eureka/
spring.cloud.config.discovery.service-id=configserver
spring.cloud.config.discovery.enabled=true
spring.cloud.config.name=claim
spring.profiles.active=dev
spring.cloud.config.fail-fast=true
For Discovery First I am getting below exception
Caused by: java.lang.UnsupportedOperationException: No decryption for FailsafeTextEncryptor. Did you configure the keystore correctly?
at org.springframework.cloud.bootstrap.encrypt.TextEncryptorUtils$FailsafeTextEncryptor.decrypt(TextEncryptorUtils.java:188) ~[spring-cloud-context-3.1.0.jar:3.1.0]
at org.springframework.cloud.bootstrap.encrypt.AbstractEnvironmentDecrypt.decrypt(AbstractEnvironmentDecrypt.java:144) ~[spring-cloud-context-3.1.0.jar:3.1.0]
... 16 common frames omitted
What I am missing here?
UPDATE: Following are the config server properties
encrypt.keyStore.location=classpath:/store name
encrypt.keyStore.password=store password
encrypt.keyStore.alias=alias
encrypt.key-store.type=jks
spring.cloud.config.server.encrypt.enabled=false
It works if I opt for server side password decryption.

${VAULT_SCHEME} not working in bootstrap.properties

I have configured spring boot application to take properties from my environment but strangely I am facing an error while starting my application.
I have added the properties in my ~/.bash_profile and also did source ~/.bash_profile after adding them to the profile.
This is how my bootstrap.properties look like:
spring.application.name=gamification
spring.cloud.vault.enabled=${VAULT_ENABLE:true}
spring.cloud.vault.fail-fast=false
spring.cloud.vault.token=${VAULT_TOKEN}
spring.cloud.vault.scheme=${VAULT_SCHEME}
spring.cloud.vault.host=${VAULT_HOST}
spring.cloud.vault.port=${VAULT_PORT:8200}
I am getting this error:
Caused by: org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.springframework.cloud.vault.config.VaultReactiveBootstrapConfiguration]: Constructor threw exception; nested exception is java.lang.IllegalArgumentException: Scheme must be http or https
at org.springframework.beans.BeanUtils.instantiateClass(BeanUtils.java:216) ~[spring-beans-5.2.4.RELEASE.jar:5.2.4.RELEASE]
at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:117) ~[spring-beans-5.2.4.RELEASE.jar:5.2.4.RELEASE]
at org.springframework.beans.factory.support.ConstructorResolver.instantiate(ConstructorResolver.java:310) ~[spring-beans-5.2.4.RELEASE.jar:5.2.4.RELEASE]
... 30 common frames omitted
Caused by: java.lang.IllegalArgumentException: Scheme must be http or https
at org.springframework.util.Assert.isTrue(Assert.java:118) ~[spring-core-5.2.4.RELEASE.jar:5.2.4.RELEASE]
at org.springframework.vault.client.VaultEndpoint.setScheme(VaultEndpoint.java:167) ~[spring-vault-core-2.2.0.RELEASE.jar:2.2.0.RELEASE]
at org.springframework.cloud.vault.config.VaultConfigurationUtil.createVaultEndpoint(VaultConfigurationUtil.java:91) ~[spring-cloud-vault-config-2.2.2.RELEASE.jar:2.2.2.RELEASE]
at org.springframework.cloud.vault.config.VaultReactiveBootstrapConfiguration.<init>(VaultReactiveBootstrapConfiguration.java:110) ~[spring-cloud-vault-config-2.2.2.RELEASE.jar:2.2.2.RELEASE]
at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method) ~[na:1.8.0_231]
at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62) ~[na:1.8.0_231]
at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45) ~[na:1.8.0_231]
at java.lang.reflect.Constructor.newInstance(Constructor.java:423) ~[na:1.8.0_231]
at org.springframework.beans.BeanUtils.instantiateClass(BeanUtils.java:203) ~[spring-beans-5.2.4.RELEASE.jar:5.2.4.RELEASE]
... 32 common frames omitted
I added a debug point in Vault Endpoint and found this:
Here as you can see, the VAULT_HOST is being taken as VAULT_HOST instead of the value of that environment variable, and same with the VAULT_SCHEME
[EDIT]
Adding bash_profile vault configuration:
export VAULT_ENABLE=true
export VAULT_SCHEME=http
export VAULT_HOST=vault-1.dev.lokal
export VAULT_PORT=8200
export VAULT_TOKEN=5F97X
[EDIT #2]
Tried out the solution suggested by #Gopinath
I am getting environment as null when trying to autowire it
The root cause of the problem can be found form this error message:
org.springframework.core.convert.ConverterNotFoundException:
No converter found capable of converting
from type [java.lang.String]
to type [org.springframework.cloud.vault.config.VaultProperties$Config]
The above message indicates that the VaultProperties object could not be initialized using the string parameter supplied.
Here is the link to documentation and instructions on configuring VaultProperties:
https://spring.io/guides/gs/vault-config/
Some more information to help understand vault:
References:
Spring Cloud Vault: https://cloud.spring.io/spring-cloud-vault/
Hashicorp Vault: https://www.vaultproject.io
What is a Vault?
A vault is a secure storage space meant for storing secret information.
Hashicorp Vault is one tool that offers vault functionality for cloud applications.
What is Spring Boot Vault?
Spring Boot applications commonly require secret information for those to work.
Some examples of secret information are:
Database password
Private key
API key
Usually, the input parameters are passed to Spring boot application through the
"application.properties" file or "bootstrap.properties" file.
The use of such properties file poses a security risk, if secret data is directly mentioned in the file.
Spring Boot Vault addresses this risk.
It pulls secret information from vault and supplies to the application at the start-up time.
The .properties file will only tell the application the names of parameters that it can expect from Vault.
The actual values of the parameters will be taken from vault.
How to setup Vault?
Step 1: Install and launch HashiCorp Vault from
https://www.vaultproject.io/downloads.html:
Step 2: After installing Vault, test whether it works, by launching
it in a console window.
> vault server --dev --dev-root-token-id="spring-boot-vault-demo"
==> Vault server configuration:
Api Address: http://127.0.0.1:8200
Cgo: disabled
Cluster Address: https://127.0.0.1:8201
Listener 1: tcp (addr: "127.0.0.1:8200", cluster address: "127.0.0.1:8201", max_request_duration: "1m30s", max_request_size: "33554432", tls: "disabled")
Log Level: info
Mlock: supported: false, enabled: false
Recovery Mode: false
Storage: inmem
Version: Vault v1.4.1
WARNING! dev mode is enabled!
.....
You may need to set the following environment variable:
PowerShell:
$env:VAULT_ADDR="http://127.0.0.1:8200"
cmd.exe:
set VAULT_ADDR=http://127.0.0.1:8200
The unseal key and root token are displayed below in case you want to
seal/unseal the Vault or re-authenticate.
Unseal Key: +Dihvgj/oRN2zo6/97ZqpWt086/CFRZEPkuauDu4uQo=
Root Token: spring-boot-vault-demo
Step 3: Store some secret data in the vault,
by running these commands in a separate command window:
> set VAULT_ADDR=http://127.0.0.1:8200
> set VAULT_TOKEN=spring-boot-vault-demo
> vault kv put secret/spring-boot-vault-demo password=££££$$$$%%%%
Key Value
--- -----
created_time 2020-05-02T09:59:41.2233332Z
deletion_time n/a
destroyed false
version 1
I did this:
I made a shell script called setenv.sh and put this under it:
#!/bin/bash
launchctl setenv VAULT_ENABLE true
launchctl setenv VAULT_SCHEME http
launchctl setenv VAULT_HOST vault-1.dev.lokal
launchctl setenv VAULT_PORT 8200
launchctl setenv VAULT_TOKEN 5F97X
And then, before starting the application I ran the shell script with
sudo sh setenv.sh
And the application seems to work fine without any errors. Strangely if I do it with my previous approach of adding the env variables inside the .bash_profile, it doesn't work.

Spring Boot JMS & Batch

Previously everything worked properly. Today I configured Spring Batch together with my Spring Boot application and faced an issue with application.properties.
I have following properties encrypted with Jasypt:
spring.profiles.active=https
ENVIRONMENT=h2
#aws sqs
aws.sqs.account.access.key=ENC(kjsdh456fgkjhdfsgkjhdfg)
#queue message listener
queue.message.listener.task.executor.threads.number=1
queue.message.listener.task.executor.max.concurrent.consumers=1
Now, in order to configure Spring Batch I added
ENVIRONMENT=h2
to application.properties file.
also, I have added batch-h2.properties file:
# Placeholders batch.* for H2 database:
batch.jdbc.driver=org.h2.Driver
batch.jdbc.url=jdbc:h2:~/testdb;CIPHER=AES;AUTO_SERVER=TRUE;DB_CLOSE_ON_EXIT=FALSE
batch.jdbc.user=sa
batch.jdbc.password="sa sa"
batch.jdbc.testWhileIdle=false
batch.jdbc.validationQuery=
batch.drop.script=classpath:/org/springframework/batch/core/schema-drop-h2.sql
batch.schema.script=classpath:/org/springframework/batch/core/schema-h2.sql
batch.business.schema.script=classpath:/business-schema-h2.sql
batch.database.incrementer.class=org.springframework.jdbc.support.incrementer.H2SequenceMaxValueIncrementer
batch.database.incrementer.parent=sequenceIncrementerParent
batch.lob.handler.class=org.springframework.jdbc.support.lob.DefaultLobHandler
batch.grid.size=2
batch.jdbc.pool.size=6
batch.verify.cursor.position=true
batch.isolationlevel=ISOLATION_SERIALIZABLE
batch.table.prefix=BATCH_
and after that I continuously receiving following exception:
Caused by: java.lang.IllegalArgumentException: Could not resolve placeholder 'aws.sqs.account.access.key' in string value "${aws.sqs.account.access.key}"
aws.sqs.account.access.key property now cannot be resolved.
I'm injecting this property into my configuration:
#Configuration
public class SQSConfig {
#Value("${aws.sqs.account.access.key}")
private String accessKey;
How to fix it ?

Connection to Oracle DB with Play Framework failed

I have this exception when I am trying to connect my oracle database to my application on Play! Framework 2.1.5 (the problem concerns the ebean.default="models.*" line) :
Configuration error
path parameter: Invalid path ' - could not find datasource for default': Token not allowed in path expression: '-' (Invalid number: '-') (you can double-quote this token if you really want it here)
In C:\MyApps\oracleCrud2\conf\application.conf at line 54.
51# You can declare as many Ebean servers as you want.
52# By convention, the default server is named `default`
53#
54 ebean.default="models.*"
55
56# Logger
57# ~~~~~
58# You can also configure logback (http://logback.qos.ch/), by providing a logger.xml file in the conf directory .
Some help would be appreciated :)
I am answering my own question. Here is the working part of the application.conf that deals with Oracle connection with Play! :
db.default.driver=oracle.jdbc.driver.OracleDriver
db.default.url="jdbc:oracle:thin:#//127.0.0.1:1521/xe"
db.default.user=play
db.default.password=play
Hope that I helped, have a good one !
-Ramzi

Resources