An application needs to be configurable in order to allow internal setup (like CDI) but also to allow externalized configuration of a deployed package (e.g. integration into runtime environment). Using Spring Boot (must read: Spring Boot reference) we rely on a comprehensive configuration approach following a "convention over configuration" pattern. This guide adds on to this by detailed instructions and best-practices how to deal with configurations.
In general we distinguish the following kinds of configuration that are explained in the following sections:
-
Internal Application configuration maintained by developers
-
Externalized Environment configuration maintained by operators
-
Externalized Business configuration maintained by business administrators
The application configuration contains all internal settings and wirings of the application (bean wiring, database mappings, etc.) and is maintained by the application developers at development time. There usually is a main configuration registered with main Spring Boot App, but differing configurations to support automated test of the application can be defined using profiles (not detailed in this guide).
The devonfw recommends using spring-boot to build web applications. For a complete documentation see the Spring Boot Reference Guide.
With spring-boot you provide a simple main class (also called starter class) like this: com.devonfw.mtsj.application
@SpringBootApplication(exclude = { EndpointAutoConfiguration.class })
@EntityScan(basePackages = { "com.devonfw.mtsj.application" }, basePackageClasses = { AdvancedRevisionEntity.class })
@EnableGlobalMethodSecurity(jsr250Enabled = true)
@ComponentScan(basePackages = { "com.devonfw.mtsj.application.general", "com.devonfw.mtsj.application" })
public class SpringBootApp {
/**
* Entry point for spring-boot based app
*
* @param args - arguments
*/
public static void main(String[] args) {
SpringApplication.run(SpringBootApp.class, args);
}
}
In an devonfw application this main class is always located in the <basepackage>
of the application package namespace (see package-conventions). This is because a spring boot application will automatically do a classpath scan for components (spring-beans) and entities in the package where the application main class is located including all sub-packages. You can use the @ComponentScan
and @EntityScan
annotations to customize this behaviour.
If you want to map spring configuration properties into your custom code please see configuration mapping.
For basic bean configuration we rely on spring boot using mainly configuration classes and only occasionally XML configuration files. Some key principle to understand Spring Boot auto-configuration features:
-
Spring Boot auto-configuration attempts to automatically configure your Spring application based on the jar dependencies and annotated components found in your source code.
-
Auto-configuration is non-invasive, at any point you can start to define your own configuration to replace specific parts of the auto-configuration by redefining your identically named bean (see also
exclude
attribute of@SpringBootApplication
in example code above).
Beans are configured via annotations in your java code (see dependency-injection).
For technical configuration you will typically write additional spring config classes annotated with @Configuration
that provide bean implementations via methods annotated with @Bean
. See spring @Bean documentation for further details. Like in XML you can also use @Import
to make a @Configuration
class include other configurations.
More specific configuration files (as required) reside in an adequately named subfolder of:
src/main/resources/app
In case you are still using dozer, you will find further details in bean-mapper configuration.
The abstract base class BaseWebSecurityConfig
should be extended to configure web application security thoroughly.
A basic and secure configuration is provided which can be overridden or extended by subclasses.
Subclasses must use the @Profile
annotation to further discriminate between beans used in production and testing scenarios. See the following example:
BaseWebSecurityConfig
for Production and Test@Configuration
@EnableWebSecurity
@Profile(SpringProfileConstants.JUNIT)
public class TestWebSecurityConfig extends BaseWebSecurityConfig {...}
@Configuration
@EnableWebSecurity
@Profile(SpringProfileConstants.NOT_JUNIT)
public class WebSecurityConfig extends BaseWebSecurityConfig {...}
See WebSecurityConfig.
A websocket endpoint is configured within the business package as a Spring configuration class. The annotation @EnableWebSocketMessageBroker
makes Spring Boot registering this endpoint.
package your.path.to.the.websocket.config;
...
@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig extends AbstractWebSocketMessageBrokerConfigurer {
...
Externalized configuration is a configuration that is provided separately to a deployment package and can be maintained undisturbed by re-deployments.
The environment configuration contains configuration parameters (typically port numbers, host names, passwords, logins, timeouts, certificates, etc.) specific for the different environments. These are under the control of the operators responsible for the application.
The environment configuration is maintained in application.properties
files, defining various properties (see common application properties for a list of properties defined by the spring framework).
These properties are explained in the corresponding configuration sections of the guides for each topic:
For a general understanding how spring-boot is loading and boostrapping your application.properties
see spring-boot external configuration.
The following properties files are used in every devonfw application:
-
src/main/resources/application.properties
providing a default configuration - bundled and deployed with the application package. It further acts as a template to derive a tailored minimal environment-specific configuration. -
src/main/resources/config/application.properties
providing additional properties only used at development time (for all local deployment scenarios). This property file is excluded from all packaging. -
src/test/resources/config/application.properties
providing additional properties only used for testing (JUnits based on spring test).
For other environments where the software gets deployed such as test
, acceptance
and production
you need to provide a tailored copy of application.properties
. The location depends on the deployment strategy:
-
standalone run-able Spring Boot App using embedded tomcat:
config/application.properties
under the installation directory of the spring boot application. -
dedicated tomcat (one tomcat per app):
$CATALINA_BASE/lib/config/application.properties
-
tomcat serving a number of apps (requires expanding the wars):
$CATALINA_BASE/webapps/<app>/WEB-INF/classes/config
In this application.properties
you only define the minimum properties that are environment specific and inherit everything else from the bundled src/main/resources/application.properties
. In any case, make very sure that the classloader will find the file.
Make sure your properties are thoroughly documented by providing a comment to each property. This inline documentation is most valuable for your operating department.
Often applications do not need business configuration. In case they do it should typically be editable by administrators via the GUI. The business configuration values should therefore be stored in the database in key/value pairs.
Therefore we suggest to create a dedicated table with (at least) the following columns:
-
ID
-
Property name
-
Property type (Boolean, Integer, String)
-
Property value
-
Description
According to the entries in this table, an administrative GUI may show a generic form to modify business configuration. Boolean values should be shown as checkboxes, integer and string values as text fields. The values should be validated according to their type so an error is raised if you try to save a string in an integer property for example.
We recommend the following base layout for the hierarchical business configuration:
component.[subcomponent].[subcomponent].propertyname
Often you need to have passwords (for databases, third-party services, etc.) as part of your configuration. These are typically environment specific (see above). However, with DevOps and continuous-deployment you might be tempted to commit such configurations into your version-control (e.g. git
). Doing that with plain text passwords is a severe problem especially for production systems. Never do that! Instead we offer some suggestions how to deal with sensible configurations:
A simple but reasonable approach is to configure the passwords encrypted with a master-password. The master-password should be a strong secret that is specific for each environment. It must never be committed to version-control.
In order to support encrypted passwords in spring-boot application.properties
all you need to do is to add jasypt-spring-boot as dependency in your pom.xml
(please check for recent version):
<dependency>
<groupId>com.github.ulisesbocchio</groupId>
<artifactId>jasypt-spring-boot-starter</artifactId>
<version>1.17</version>
</dependency>
This will smoothly integrate jasypt into your spring-boot application. Read this HOWTO to learn how to encrypt and decrypt passwords using jasypt. Here is a simple example output of an encrypted password (of course you have to use strong passwords instead of secret
and postgres
- this is only an example):
----ARGUMENTS-------------------
input: postgres
password: secret
----OUTPUT----------------------
jd5ZREpBqxuN9ok0IhnXabgw7V3EoG2p
The master-password can be configured on your target environment via the property jasypt.encryptor.password
. As system properties given on the command-line are visible in the process list, we recommend to use an config/application.yml
file only for this purpose (as we recommended to use application.properties
for regular configs):
jasypt:
encryptor:
password: secret
(of course you will replace secret
with a strong password). In case you happen to have multiple apps on the same machine, you can symlink the application.yml
from a central place.
Now you are able to put encrypted passwords into your application.properties
spring.datasource.password=ENC(jd5ZREpBqxuN9ok0IhnXabgw7V3EoG2p)
To prevent jasypt to throw an exception in dev or test scenarios simply put this in your local config (src/main/config/application.properties
and same for test
, see above for details):
jasypt.encryptor.password=none
Is this Security by Obscurity?
-
Yes, from the point of view to protect the passwords on the target environment this is nothing but security by obscurity. If an attacker somehow got full access to the machine this will only cause him to spend some more time.
-
No, if someone only gets the configuration file. So all your developers might have access to the version-control where the config is stored. Others might have access to the software releases that include this configs. But without the master-password that should only be known to specific operators none else can decrypt the password (except with brute-force what will take a very long time, see jasypt for details).