Spring Boot & Infisical | Managing your sensitive configuration

Hello everyone! In this article, I’ll show you how to employ Infisical in your Spring Boot application.

Spring Boot & Infisical | Managing your sensitive configuration
Login screen of Infisical

Hello everyone! In this article, I’ll show you how to employ Infisical in your Spring Boot application.

Why Infisical?

Most of my projects utilize HashiCorp Vault to store sensitive data such as database usernames and passwords.

But last year, HashiCorp changed Terraform’s license to non-open source. This could also happen to Vault, or not.

The second reason why I chose Infisical for this article is because IBM acquired Terraform this year.

Introduction

Like the HashiCorp Vault, Infisical is a secret management platform. Let’s start by starting up the Infisical instance locally with the docker-compose file provided by the documentation. First, we need to download it with the following command.

curl -o docker-compose.prod.yml https://raw.githubusercontent.com/Infisical/infisical/main/docker-compose.prod.yml

The second step is to get the .env file. We can do it by command provided by the documentation.

curl -o .env https://raw.githubusercontent.com/Infisical/infisical/main/.env.example

Now we can start an instance of Infisical.

docker-compose -f docker-compose.yml up

After that, you should see this screen at http://localhost:8080

Then, after successfully signing up, you will see a window offering you the ability to download an emergency kit.

Now, we can create a new project.

Let’s create a project with the name medium-secret-service.

Creation of project

After that, the project window appears.

Project overview

Then let’s create two secrets — DATABASE_USERNAME and DATABASE_PASSWORD.

Now it’s time to give access to these secrets to our service. Go to the tab Machine Identities. Create a new one and save clientId; after that, create a clientSecret for identity. Now, you can add identity to the project.

Coding Time

The next step is to configure Maven to access the Infisical SDK.

In order to access it, you have to modify your ~/.m2/settings.xml

<profiles> 
    <profile> 
        <repository> 
          <id>infisical</id> 
          <url>https://maven.pkg.github.com/infisical/sdk</url> 
          <snapshots> 
            <enabled>true</enabled> 
          </snapshots> 
        </repository> 
      </repositories> 
    </profile> 
  </profiles> 
 <servers> 
  <server> 
   <id>infisical</id> 
   <username>YOUR_GITHUB_NAME_HERE</username> 
   <password>YOUR_GITHUB_PAT_HERERE</password> 
  </server> 
 </servers>

After that, we can add a dependency to our pom.xml

<dependency> 
            <groupId>com.infisical</groupId> 
            <artifactId>sdk</artifactId> 
            <version>2.2.3-SNAPSHOT</version> 
        </dependency>

Next, we will implement two services to obtain secrets from properties or the Infisical. Next, we will implement two services to obtain secrets from properties or the Infisical. Before creating the services, we should define our model for secret data.

package io.vrnsky.mediumsecretservice.config.secret; 
 
import lombok.AllArgsConstructor; 
import lombok.Data; 
import lombok.NoArgsConstructor; 
 
@Data 
@NoArgsConstructor 
@AllArgsConstructor 
public class Secret { 
    private String username; 
    private String password; 
}

Then, let’s declare the interface for our services.

package io.vrnsky.mediumsecretservice.config.secret; 
 
public interface SecretService { 
 
    Secret accessSecret(); 
}

Then, let’s declare the interface for our services. The first implementation will be a straightforward approach to get secret configuration from application.properties or application.yml

package io.vrnsky.mediumsecretservice.config.secret; 
 
import org.springframework.beans.factory.annotation.Value; 
import org.springframework.context.annotation.Profile; 
import org.springframework.stereotype.Service; 
 
@Service 
@Profile("!infisical") 
public class PropertySecretService implements SecretService { 
 
    @Value("${spring.datasource.username}") 
    private String username; 
 
    @Value("${spring.datasource.password}") 
    private String password; 
 
    @Override 
    public Secret accessSecret() { 
        return new Secret(username, password); 
    } 
}

The second implementation will work with the Infisical through its SDK.

package io.vrnsky.mediumsecretservice.config.secret; 
 
import com.infisical.sdk.InfisicalClient; 
import com.infisical.sdk.schema.AuthenticationOptions; 
import com.infisical.sdk.schema.ClientSettings; 
import com.infisical.sdk.schema.GetSecretOptions; 
import com.infisical.sdk.schema.GetSecretResponseSecret; 
import com.infisical.sdk.schema.UniversalAuthMethod; 
import org.springframework.beans.factory.annotation.Value; 
import org.springframework.context.annotation.Profile; 
import org.springframework.stereotype.Service; 
 
@Service 
@Profile("infisical") 
public class InfisicalSecretService implements SecretService { 
 
    @Value("${infisical.clientId}") 
    private String clientId; 
 
    @Value("${infisical.clientSecret}") 
    private String clientSecret; 
 
    @Value("${infisical.projectId}") 
    private String projectId; 
 
    @Value("${infisical.siteUrl}") 
    private String siteUrl; 
 
 
    @Override 
    public Secret accessSecret() { 
        ClientSettings settings = new ClientSettings(); 
        settings.setSiteURL(siteUrl); 
        AuthenticationOptions authOptions = new AuthenticationOptions(); 
        UniversalAuthMethod authMethod = new UniversalAuthMethod(); 
 
        authMethod.setClientID(clientId); 
        authMethod.setClientSecret(clientSecret); 
 
        authOptions.setUniversalAuth(authMethod); 
        settings.setAuth(authOptions); 
 
        InfisicalClient infisicalClient = new InfisicalClient(settings); 
 
        GetSecretOptions options = new GetSecretOptions(); 
        options.setSecretName("DATABASE_USERNAME"); 
        options.setEnvironment("dev"); 
        options.setProjectID(projectId); 
        Secret secret = new Secret(); 
        GetSecretResponseSecret databaseUserNameSecret = infisicalClient.getSecret(options); 
        secret.setUsername(databaseUserNameSecret.getSecretValue()); 
        options.setSecretName("DATABASE_PASSWORD"); 
        GetSecretResponseSecret databasePassword = infisicalClient.getSecret(options); 
        secret.setPassword(databasePassword.getSecretValue()); 
        infisicalClient.close(); 
        return secret; 
    } 
}

Okay, the next thing that we should do is configure our Spring Boot service to use data from the Secret Service instance.

package io.vrnsky.mediumsecretservice.config; 
 
import io.vrnsky.mediumsecretservice.config.secret.Secret; 
import io.vrnsky.mediumsecretservice.config.secret.SecretService; 
import lombok.Data; 
import org.springframework.beans.factory.annotation.Value; 
import org.springframework.boot.jdbc.DataSourceBuilder; 
import org.springframework.context.annotation.Bean; 
import org.springframework.context.annotation.Configuration; 
import org.springframework.context.annotation.Primary; 
 
import javax.sql.DataSource; 
 
@Data 
@Configuration 
public class ServiceConfiguration { 
     
    @Value("${spring.datasource.url}") 
    private String url; 
     
    @Bean 
    @Primary 
    public DataSource dataSource(SecretService secretService) { 
        Secret secret = secretService.accessSecret(); 
        return DataSourceBuilder.create() 
                .url(url) 
                .username(secret.getUsername()) 
                .password(secret.getPassword()) 
                .build(); 
                 
    } 
}

After that, we can check if our service works. First, let’s run the service with this configuration and check that all works fine if the fiscal profile is not enabled.

spring: 
  datasource: 
    url: jdbc:postgresql://localhost:5432/medium_service 
    username: postgres 
    password: 55555

Let’s now try to run with only the URL in configuration and secret data from the Infisical.

spring: 
  datasource: 
    url: jdbc:postgresql://localhost:5432/medium_service 
infisical: 
  siteUrl: http://localhost:80 
  projectId: 04608ded-201a-471c-9795-248f8c8b1887 
  clientId: 1ab6f04d-de7f-46cd-9d55-ebf16e5f5c00 
  clientSecret: 75be0b336f4ee245221ef6d49a9a1fe2ef959fbe309c275879a18d9ab1ce3d43

The service should run as usual.

Conclusion

I think it is best to pay attention to how you store your sensitive configuration, as it is a security measure that prevents restricted access to your data.

I hope you enjoyed this article; feel free to comment about your experience with the Infisical.

Subscribe to Egor Voronianskii | Java Development and whatsoever

Sign up now to get access to the library of members-only issues.
Jamie Larson
Subscribe