4

I am using Spring JPA to access my database but the username and password for the database is provided to me in the form of two files (username.txt and password.txt). The security guideline is that we are not supposed to paste password or any form of password (encrypted) in the config files.

I am trying to figure out ways to do something like this:

spring.datasource:
  url: <db url>
  username: "file:/path/username.txt"
  password: "file:/path/password.txt"

Does anyone have any idea on how to achieve something very similar to this?

PS. I want to keep the environment-specific credential file paths in this spring properties file. There are ways to pass in the file content as environment variable java -Dusername=${cat /path/username} but I want to avoid putting my config outside the the spring properties file.

EDIT: The username and password are stored as plaintext in separate files.

return 0
  • 4,226
  • 6
  • 47
  • 72
  • so instead of having a config file containing the credentials, you want to create another config file with credentials? or do you want to store the "credentials" part in another file then your normal regular `application.yaml` that you pack in your jar file? – Jens Baitinger Dec 16 '21 at 18:50
  • I have never seen something like this and I am working for 6 years in spring boot. A possible solution might be to have something like `username: DB_USER` where DB_USER will be variable that you will pass when running the application. For example I have it like that and I am passing those values using bitbucket variables. – f.trajkovski Dec 16 '21 at 18:54
  • @JensBaitinger i have to to consume username and password from a local file, this is the constraint i am working with. I need to somehow extract their content either at yaml file OR during runtime (during the datasource is initialized) – return 0 Dec 16 '21 at 18:57
  • You can still read the file in a @Configuration class and then initializing a Datasource(Pool) of your choice, thouth I would rather get rid or that contraint. – Jens Baitinger Dec 16 '21 at 19:02
  • And what is the plan for a production release? Again you will have this constraint to keep it in a file on some location? – f.trajkovski Dec 16 '21 at 19:03
  • @f.trajkovski the plan for production release is exactly the same. Deploy those credential files in the file system and deploy the application that depends on those files. – return 0 Dec 16 '21 at 19:13

5 Answers5

4

Spring Boot 2.4 introduced the ability to read configuration values from files where the file name is the property name and the file content is the property value. For example, given the directory structure

path/
  spring.datasource.username
  spring.datasource.password

and the application.yaml file contains

spring.config.import: configtree:/path/

then the Environment will contain properties named spring.datasource.username and spring.datasource.password.

Chin Huang
  • 12,912
  • 4
  • 46
  • 47
  • This is a good answer for many others who have the same need. Unfortunately it doesn't work for me because all my username/password files are provided with name "username" and "password" so i can't build properties from their file names. – return 0 Dec 18 '21 at 18:14
3

You can’t read the contents of a file from Spring application properties file. But you can certainly configure the data source programmatically, and in code, read the files.

@Configuration
public class DataSourceConfig {
    
    @Bean
    public DataSource getDataSource() {
        DataSourceBuilder dataSourceBuilder = DataSourceBuilder.create();
        dataSourceBuilder.driverClassName("org.h2.Driver");
        dataSourceBuilder.url("jdbc:h2:mem:test");
        dataSourceBuilder.username("SA");
        dataSourceBuilder.password("");
        return dataSourceBuilder.build();
    }
}

See this article for details. I’m assuming you know how to read a file using Spring Boot, if not, you can see this article.

Abhijit Sarkar
  • 21,927
  • 20
  • 110
  • 219
2

When we serach for "spring-boot secrets", still this (2016) article pops up first:

Spring Vault!


Some modern approaches (here) with docker-secrets:


But "pragmatically" and with the "latest rce's${;}", i would propose something like:

@Value("#{T(java.nio.file.Files).readString(T(java.nio.file.Path).of('/path/username.txt'))}")
String username;

// or having the path in a property (named db.password.file):
@Value("#{T(java.nio.file.Files).readString(T(java.nio.file.Path).of('${db.password.file}'))}")
String password;

Thx to:

xerx593
  • 12,237
  • 5
  • 33
  • 64
1

There are multiple ways how you can load another config file containing your secrets (see documentation)

One idea is to create a environment specific application.yaml (e.g. application-prod.yaml containing only the prod config next to your application config (you dont need to pack that in your jar, you can simple put it next to you jar only in your production environment.

Another idea is to load additional config using -Dspring.config.additional-location=...

Jens Baitinger
  • 2,230
  • 14
  • 34
  • The OP didn’t say, but I’m guessing the credential files are not formatted as YAML or key-value pairs, so those aren’t “config” files at all. Your answer doesn’t apply to the problem at hand. – Abhijit Sarkar Dec 16 '21 at 19:12
  • @AbhijitSarkar you are right, those credential files are in the form of plaintext username and password, in separate files. I added this detail in my original post. – return 0 Dec 16 '21 at 19:13
0

Though I can't say, this is a good idea, but you can read file content to field. But you'll have to do 3 steps

  1. Add path to file to application.properties

password.file=/Users/user/work/pass.txt

  1. Implement of find a method, which can load file content into a single string.

For example

public static String readPassword(String filePath) throws IOException {
        return FileUtils.readFileToString(new File(filePath), Charset.defaultCharset());
}
  1. Add SpEL expression to the field, which invokes this method using argument from properties
    @Value("#{T (com.github.TestComponent).readPassword(\"${password.file}\")}")
    private String password;

While startup, Spring will invoke this method and put the result to password field.

Class may look like

package com.github;

import java.io.File;
import java.io.IOException;
import java.nio.charset.Charset;

import org.apache.commons.io.FileUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

@Component
public class TestComponent {

    @Value("#{T (com.github.TestComponent).readPassword(\"${password.file}\")}")
    private String password;
    
    public void showPassword() {
        System.out.println(password);
    }
    
    public static String readPassword(String filePath) throws IOException {
        return FileUtils.readFileToString(new File(filePath), Charset.defaultCharset());
    }
}