13

Where should I store development credentials on a Spring Boot project so that they are not committed to the repository? What's the most standard way?

In other frameworks (Rails, Clojure) I'm used to have a file that I do not commit to the repo where that information goes. Something like a secondary application.properties that gets merged and it's never committed. Does such a thing exist?

I'm deploying to Heroku, which provides credentials in environment variables that Spring can pick up, so, that part is solved. If I deploy somewhere else, it'll be a similar config.

In the Spring Boot Docs, chapter 24, Externalized Configuration, there's a list of all the places where properties are defined. I went through that list trying to find the appropriate place for credentials and I couldn't find it:

Devtools global settings properties on your home directory (~/.spring-boot-devtools.properties when devtools is active).

It's not a devtools thing, as you could want to develop locally disabling devloots.

@TestPropertySource annotations on your tests.

It's not about testing

@SpringBootTest#properties annotation attribute on your tests.

Again, not testing.

Command line arguments.

I'd rather not have credentials in command lines as those tend to be public in the computer, so, another program could pick them up. But besides that, I'm not running a command when development; I'm triggering the app from IntelliJ.

Properties from SPRING_APPLICATION_JSON (inline JSON embedded in an environment variable or system property)

Same as environment variable/system properties bellow.

ServletConfig init parameters. ServletContext init parameters. JNDI attributes from java:comp/env.

Those seem not to apply at all when developing local generating a jar,

Java System properties (System.getProperties()).

I'm not sure what would be the appropriate way of setting them up, but it feels laborious.

OS environment variables.

I either set them on my OS, which is opaque to new developers, or I set them inside the IntelliJ run profile, which makes them part of the repository, which is what I'm trying to avoid.

A RandomValuePropertySource that only has properties in random.*.

They are not random.

Profile-specific application properties outside of your packaged jar (application-{profile}.properties and YAML variants)

This could be, but I'm not sure where that file should reside.

Profile-specific application properties packaged inside your jar (application-{profile}.properties and YAML variants)

I want to be able to commit profile specific application properties to the repo, so, I cannot store credentials in these files.

Application properties outside of your packaged jar (application.properties and YAML variants).

This could be, but I'm not sure where that file should reside.

Application properties packaged inside your jar (application.properties and YAML variants).

I want to commit that file, so, I cannot store credentials there.

@PropertySource annotations on your @Configuration classes. Default properties (specified using SpringApplication.setDefaultProperties).

Using something like this, I could make my own thing, picking properties from a file that's not committed, but I'm interested in following Spring Boot's best and common practices, not making my own; specially as I'm just getting started.

Pablo Fernandez
  • 279,434
  • 135
  • 377
  • 622
  • 1
    Maybe a profile specific configuration could help you: https://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-external-config.html#boot-features-external-config-profile-specific-properties it allow to separate properties in different app.properties, this post has additional information https://stackoverflow.com/questions/32196451/environment-specific-application-properties-file-in-spring-boot-application – Daniel C. Aug 27 '17 at 01:47
  • @DanielC. I'm aware of profiles, but if I use, let's say, the dev profile to store this, it means I can never have a committed dev profile config. I'll update the question accordingly. – Pablo Fernandez Aug 27 '17 at 08:46
  • Put an `application.properties` under `${PROJECT_ROOT}/config/application.properties`. Those properties will override any other defined properties. See https://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-external-config.html#boot-features-external-config-application-property-files. – fateddy Aug 27 '17 at 08:53
  • @fateddy: that works. Is it customary to not commit `${PROJECT_ROOT}/config/application.properties`? – Pablo Fernandez Aug 27 '17 at 09:13
  • That depends. Would recommend to commit an example properties file under `config/application.properties.example`. Everything else under this directory is ignored. That should give every developer a good starting point for his own (unversioned) `config/applicaiton.properties` file. All in all I dont see a problem with that approach, as it will not be packaged with the artifact. – fateddy Aug 27 '17 at 11:53
  • @fateddy: yes, I always do that. I ended up writing a blog post about, explaining why I want to do it that way: https://pupeno.com/2017/08/27/setting-database-credentials-on-a-spring-boot-project-the-right-way/ But if you want to add an answer here, please, go ahead, and I'll select it as the one! – Pablo Fernandez Aug 27 '17 at 12:01
  • @Pablo you can also define the properties file outside the jar and specify the path using the `spring.config.location` property like this https://stackoverflow.com/questions/41754459/spring-boot-how-to-read-properties-file-outside-jar#41755523 – Daniel C. Aug 27 '17 at 12:37
  • @Pablo thank you - added as an answer! – fateddy Aug 31 '17 at 20:38

2 Answers2

19

Spring-boot allows you to externalize your configuration in different places. For the scenario in question, one could place an application.properties file under ${PROJECT_ROOT}/config/. Those properties will override any other defined properties. In this case, it is not packaged within a deployable artifact.
Take a look at Application property files for further details.

Provide a sample configuration file under config/application.properties.example as a good starting point. That file could be version controlled. To avoid unintentional commits ignore everything else.

Using git, an ignore file could contain the following snippet (ignores everything but files ending with .example and a README.md):

.gitignore:

/config/*
!/config/README.md
!/config/*.example
fateddy
  • 6,887
  • 3
  • 22
  • 26
  • A little addition: sometimes it's useful to place this external properties in a special folder: `config/MY_SPECIAL_FOLDER/application.properties`. And yes, Spring Boot find this properties automatically too. – Hermann Schwarz May 29 '22 at 19:46
1

It depends on your configuration. Thanks to Spring's Environment abstraction, you can override properties at runtime from the environment, so depending on your deployment, there are many ways of doing that. However, be warned that, a false sense of security can be a real pain in the neck. You'd want to maintain configuration as code - that can be versioned, reviewed and audited, not as a text file on someone's PC that they use to inject random runtime variables. It is the classic "works on my machine" problem.

You didn't say anything about your deployment so it's almost impossible to say; if you're using Tomcat, you can use the setenv.sh. Your question, as it stands, is pretty vague; I'll update my answer when you update your post.

Edit, based on what OP said in the comments:

You could just provide key-value pairs as JVM variables. For example, if using Gradle to build, you could do gradle clean bootRun -Dmy.secret.key=whatever, and in the build.gradle:

bootRun {
  systemProperties = System.properties
}

You could also keep a application-local.properties that's not committed to Git, and start the application with a -Dspring.profiles.active=default,local profile, in which case Spring would merge application.properties and application-local.properties, with higher preference given to the later. Like I said before, this is a very bad idea, but you're a free man, so...

Spring Boot in itself doesn't have first-class support for encrypting properties, but if you're willing to do that yourself, you could commit everything, and either start the app passing in the key, or somehow manage it in your build environment (like a property in Maven settings.xml).

Edit 2, how to override properties in application.properties using env variables at runtime:

my.secret.key=${MY_SECRET_KEY:default}

If env variable MY_SECRET_KEY is present, my.secret.key will be equal to it's value; else it will be equal to default.

Abhijit Sarkar
  • 21,927
  • 20
  • 110
  • 219
  • I'm deploying to Heroku. Heroku just provides a bunch of environment variables. This shouldn't affect how I set up credentials on development as long as how I do it doesn't affect deployment. – Pablo Fernandez Aug 27 '17 at 08:45
  • @Pablo How do you do development locally? Not by deploying every time to Heroku, I hope. – Abhijit Sarkar Aug 27 '17 at 08:48
  • @Pablo, I'll ask again, because you didn't answer my question. How? Do you currently have a property file that you're trying to get rid of? – Abhijit Sarkar Aug 27 '17 at 08:50
  • no, because I just started the project. But you could assume that yes, I want to find a place to put credentials that is not the application.properties that's committed to the project. – Pablo Fernandez Aug 27 '17 at 09:12
  • In the question I explain why command line arguments are not what I'm after. They are hard to maintain. – Pablo Fernandez Aug 27 '17 at 09:25
  • @Pablo I did suggest a non-cmd line option, didn't I? – Abhijit Sarkar Aug 27 '17 at 09:26
  • Can you have more than one profile active at the same time? I don't want to limit the possibility of having a dev or test profiles. – Pablo Fernandez Aug 27 '17 at 09:30
  • Why is it a bad idea? – Pablo Fernandez Aug 27 '17 at 09:31
  • @Pablo Yes, you can activate multiple profiles, and I *did* show that in my answer by activating both `default` and `local` profiles. and regarding why keeping local files is a bad idea, [here](https://dzone.com/articles/works-on-my-machine) is why. At this point, if you've more questions, I suggest you take some time to learn Spring Boot, because it appears you lack the basics to have an effective discussion. And I don't say that smugly - there's only so much to be discussed in comments. – Abhijit Sarkar Aug 27 '17 at 09:35
  • 1
    Well, I think not keeping credentials in a local file is a very bad idea as it leads to exposed credentials. Keeping credentials on a local file, committing a template of that file for other devs and using environment variables on the server (producting, staging, ci, etc) is a very standard practice on other frameworks. – Pablo Fernandez Aug 27 '17 at 09:37
  • That *template* is called `application.properties`; you can have env variables in it too. See my edit2; hope that helps, because I'm getting my coat. – Abhijit Sarkar Aug 27 '17 at 09:39