Spring Cloud - Externalized Configuration, Setting up and Securing

Ömer Kurular
6 min readAug 14, 2021

--

In this article, we are going to talk about why we should use config server, how to set it up and secure it.

One of the important patterns of microservice architecture is externalized configuration. Imagine we have a lot of services each of which uses different configurations to handle some operations such as database startup, setting up messaging infarastructure and so on. If we set those necessary configuration directly on those services, it would eventually be a lot of work to handle for future as those services may run on different locations. In this case, externalization of configuration comes into the stage to help us easily manage configs and we can handle all the configuration in one place.

1- Why should we use config server?

There are some benefits of using standalone configuration server.

Firstly, when we use such structure, we centralize all the configuration properties that services use in one place. And this makes it easy to manage them. Another benefit is that you can change values of the properties in config server and services may be reflected those changes at runtime without needing to be stopped and run again.

2- Setting up config server

Above you can see a diagram that represent services and config server. We have different ways to store our properties like

1- File system

2- Git

3- Database

4- Vault

In this tutorial, we are going to use git as our config storage and keep our configs at github. It is very simple create a config server using related Spring Cloud module “spring-cloud-config”. Include the following module in your project. (Use spring-boot-starter-parent and it will deduce the version, you can find github repo at the end of the article)

<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-config-server</artifactId>
</dependency>

Then, simply annotate the class contatining your main class as follows.

@EnableConfigServer
@SpringBootApplication
public class ConfigServerApplication {

public static void main(String[] args) {
SpringApplication.run(ConfigServerApplication.class, args);
}
}

We also have some configuration to set in our application.properties (or yaml if you want). Include the following properties

server.port=8090 // port your server runs at
spring.cloud.config.server.git.uri=https://github.com/kurular4/config-repo // repo that you create to store configs
spring.cloud.config.server.git.username=abc // username to access repo
spring.cloud.config.server.git.password=abc // access token to access repo, we will change it to use secure version of it.

If your config repo is public, you will not need username and password properties but that is definitely not the way you want to go for. Also, we will change it so we will not use password directly in properties as it creates a security issue. Let’s also have some configuration file to use later in our client application. Create a three file in git repo named,

config-client-git-properties will be default config

config-client-git-development.properties will be development profile config

config-client-git-production.properties will production profile config

The pattern will be {client_app_name}-{profile_name}.{properties|yaml}

You can see possible patterns belows

/{application}/{profile}[/{label}]
/{application}-{profile}.yml
/{label}/{application}-{profile}.yml
/{application}-{profile}.properties
/{label}/{application}-{profile}.properties

The initial part of the properties file will be our client application’s name, second part will be profile name. You can include any config property inside them. I just added the following

common_header=someheader123

Now, let’s create a client app and see we can fetch configuration from server. Create another Spring Boot app, include the following dependency besides others

<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-config</artifactId>
</dependency>

then, create bootstrap.properties file under resources. Here, we will set some property to access config server.

spring.profiles.active=development
spring.application.name=config-client-git
spring.cloud.config.uri=http://localhost:8090

Config server provide properties according to the profile of our client application. Name property is crucial as the config server will fetch config according to it. Finally, uri is the address of the config server. Now, have a controller to test if you can properly fetch configuration.

@RestController
public class TestController {

@Value(value = "${common_header}")
private String commonHeader;

@GetMapping("/common_header")
public String getCommonHeader() {
return commonHeader;
}
}

http://localhost:8080/common_header will give you someheader123 or whatever you put inside.

There are some more points that helped me for my own work.

More on profile usage:

You can put your dev application.yml file under resources and put your production yml on config server. When you set your profile “dev”, you can keep working on your development environment without config server. It will still try to connect to the config server but when it fails to do so, it will configure your app with your dev application.yml under resources.

Retrying to fetch configs when running client and config server at the same time:

I had a problem on my local environment where I run both config server and client application at the same time. Config server may take some time to fully ready to serve configs. So, when both apps are opened at the same time, client may not able to fetch configs from config server and it keeps running without configs. When this occurs, client app will not function properly and you possibly see a lot of exception due to unconfigured properties. To prevent this we have some properties for config. I suggest that you do not put this config under bootstrap.yml but bootstrap-prod.yml or other bootstrap.yml with a profile different than development specific one to prevent abortions on your local development when working without config server.

spring:
cloud:
config:
fail-fast: true
retry:
max-interval: 3000
max-attempts: 20

Fail-fast property will let your client abort its execution when it is not able to access config server. It is especially helpful for production environment as because config server mostly will be available on prod but if it is not, you no longer want your app to run without noticing background exceptions and malfunctioning service. It is safer to fail fast and notice there is something wrong. Retry properties will be more helpful with the situation I mentioned above for development environment. When you opened them both at the same time and client gets opened first, it will keep retrying with an interval of value “max-interval” until config server becomes available or “max-attempts” of retries occurs.

3- Securing config server

To secure our config server, we will need two extra dependencies, Jasypt spring boot starter and spring starter security.

<dependency>
<groupId>com.github.ulisesbocchio</groupId>
<artifactId>jasypt-spring-boot-starter</artifactId>
<version>3.0.3</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>

We will use Jasypt to encrypt our properties in application.yaml file as it is not a good practice to use plain text for secrets. Let’s see how our application.yaml look like (I used application.properties previously in this article, and yaml now, you can use whatever you want).

server:
port: 8090 #port at which our config server runs

spring:
cloud:
config:
server:
git: # github credentials
uri: https://github.com/kurular4/config-repo # repo uri
username: github_username
password: ENC(encrypted_access_token_here)
default-label: main
security: # credentials to access config server from services
user:
name: config-server-username
password: ENC(encrypted_password_here)

jasypt:
encryptor:
algorithm: PBEWithMD5AndDES
iv-generator-classname: org.jasypt.iv.NoIvGenerator

As you can see, we will run our application at port 8090 (it’s up to you). Then, we have cloud config properties. Under spring.cloud.config.server.git, we have uri that points to our repository address ,and we have username and password fields. Important point here is that we will not use plain text for password here. We will first encrypt our github access token with our secret and then put it in ENC(..) wrapper. When you start your application by providing your secret key, Jasypt will automatically decrypt it for it to be usable. I also set default label (which is master) to be main as Github recently changed their default branch name from master to main. And finally, we set our encryptor algorithm to PBEWithMD5AndDES because the Jasypt default encryption algorithm changed after version 3.0.0 and the tool we use uses PBEWithMD5AndDES.

You can encrypt/decrypt your secret by using the following tool or directly via jasypt jar.

We will also limit access to our config server by setting up basic security with username and password. We applied the said encryption method for this password field, too.

Now, we can start our application by providing our secret as follows. (Or if you are using IDE possibly Intellij or Eclipse, you add the property at VM options under run configuration)

java -jar app.jar -Djasypt.encryptor.password=your_private_key

In this article, I talked about why we should embrace externalized configuration, how to set it up and how to secure it. I hope you enjoy it.

--

--

Ömer Kurular

I am Ömer and currently working as a full-time software engineer. I will share my knowledge with you I gained through years of professional and self working.