Spring Boot is a popular framework used to create (microservice) applications in Java.
It has a flat learning curve and yet is very versatile, which goes a long way explaining its popularity.
As with all Spring frameworks, Spring Boot sports a ‘convention over configuration’ approach to discovering beans, services and also properties. In practice this means working with Spring Boot can largely be done intuitively.
This is also true for the configuration properties
The standard way to provide a Spring Boot application with its properties is to include an application.properties or application.yml file containing key-value pairs in the codebase of the application. When working with different Spring profiles, in an OTAP setting, the property files for all profiles are typically included.
The problem here is that there are production-settings that a company does not wish to be clearly visible to every developer.
Even if the property files are not in the codebase, the fact that the application needs access to them makes it hard to hide production-settings from unauthorized eyes.
Encrypting the properties
One solution to the problem is to encrypt the application properties.
This can be done using the Jasypt library, which uses a secret key to encrypt and decrypt configuration values. The secret key needs to be available to the application at runtime, which makes it hard to hide it from a developer. The library works fine for properties injected into custom made Spring components and services, but it is difficult to use decrypted values in Spring Framework provided beans like datasources etc.
The same limitations apply to any custom encryption/decryption technique.
Spring Cloud Config Server offers an HTTP API for external configuration.
The application properties do not reside in the codebase but are provided by the config server.
Cloud config server also offers the possibility to encrypt the property values.
This encryption can be symmetric (using a shared secret key) or asymmetric using a public/private keypair. One instance of Cloud Config Server can be used to provide multiple client applications with their properties, each encrypted by using their individual public key.
A client application uses its private key to decrypt the values.
Although it is possible to store the application private keys at the Config Server to decrypt there and send unencrypted values to the client, this, imho, defeats the purpose.
A client application should keep its private key private and sending encrypted values over http makes sniffing for secrets much harder.
In the default setup, Cloud Config Server reads the properties it exposes from files stored in a Git repository, but in this instance a database is used to store the encrypted values.
Cloud Config Server using Asymmetric encryption and a database
In figure 1 the setup of the implementation is given.
The standard Spring Boot main application class needs the EnableConfigServer annotation:
In addition to the normal application.yml (and application-<spring_profile>.yml), the server relies on a bootstrap.yml to store the definition of its local keystore.
Server-side decryption of encrypted values is disabled by the somewhat cryptic property
The Server’s context-root and port is defined in the familiar application.yml configuration file, so the server will be available at http://localhost:8888/configserver.
An additional application-jdbc.yml contains the settings for the use of an in-memory H2 database for the storage of the encrypted client configuration values. To activate this, run the server with the JVM argument -Dspring.active.profiles=jdbc.
The sql query to retrieve client properties from the database is also defined in this file.
In the where-clause of this query the field application defines a client application and profile denotes the spring profile the property belongs to. The label field in this setup has no specific meaning.
Liquibase is used to instantiate the properties table and to insert some values.
An example of an encrypted property value:
To acquire an encrypted value, the server exposes an /encrypt endpoint which can be called using a tool like curl:
In response to this request the encrypted value of “Some value” is returned, which can subsequently be inserted in the properties table.
The client configuration to connect to the Config Server
The spring cloud starter dependency needs to be added to the pom.xml:
The client also will need a bootstrap file where the predicates for the server’s query are defined, as well as the basic authentication to connect to the server.
The location of and credentials for the keystore containing the public and private key-pair are also defined in the bootstrap.yml configuration file.
The bean classes of the client need the RefreshScope annotation.
This, together with the information in the bootstrap file, is enough to make the client use the config server as it’s source of property values.
Here, a REST controller is resolving the value of the property app.message by retrieving it from the config server.
For illustration purposes, the decrypted value is returned upon a get request to the client’s /message endpoint.
The complete code for this example can be viewed and downloaded from: