How to retrieve DB credentials from external file for Liquibase config - java

I have a Spring Boot application which runs in Azure Kubernetes. The application uses Liquibase (Liquibase-Core 4.4.3) to create and amend database tables for the application on start up. For security reasons the application has different DB credentials to the Liquibase user and the Liquibase user credentials are added via the application.properties file as per below using environment variables:
spring.liquibase.url=${env_db_connection_url}
spring.liquibase.user=${env_db_lb_username}
spring.liquibase.password=${env_db_lb_password}
We add the credentials using this method with environment variables as the credentials are different depending on the environment (e.g. UAT, production) the app is being deployed to and this allows the pipeline to handle the differences without any changes being necessary to the code.
Our security team want us to stop using environment variables and so we are moving to using csi drivers for any sensitive information. This effectively creates something similar to an external properties file which sits outside the application but within the same container.
However, I am now having trouble resolving the db credentials for Liquibase and I don't know the best method to achieve what I need. I need a solution where we can still tokenize the value within the code but it is able to retrieve the secret values (db password etc) from the external properties file.
As you can imagine hard-coding these values or even just encrypting them wouldn't solve the issue due to the changing values between environments. We are able to retrieve the values within the application however I feel this is done too late in the process as Liquibase will have already attempted to start (and therefore fail) before the application code is run.
Any help or suggestions would be appreciated.

I used a method like this for a similar problem.
I'm running things on Jenkins server. But it's not necessary
I do not keep database connection information in files. After I send the files to the server, I add the database connection information to these files with bash script. The DB connection information is on the server, not the version control system.
Thus, I hide some db connection information from unauthorized people (only those who have access to the server can access it).
A- File example; liquibase.prod.properties
there is no db data
changeLogFile=./master.xml
logFile: liquibase.prod.log
logLevel: INFO
liquibase.hub.mode=off
B- Example of file where I keep database connections on server
spring.liquibase.url = my_env_db_connection_url
spring.liquibase.user= my_env_db_lb_username
spring.liquibase.password= my_env_db_lb_password
C- I am adding lines to this file with Jenkins and cmd bash
type E:\file1.cfg >> file2.cfg

This was the solution we took in the end. Effectively we worked out that the spring properties file could look directly in an external properties file (the csi driver in this case) to get the value.
Added to Spring properties file spring.config.import=optional:configtree:${env_var_for_external_property_file_location:#{null}}/liquibase/
For clarity, below is the secret provider class yaml which stores the liquibase password. The object alias value matching what Liquibase config expects for db password.
apiVersion: secrets-store.csi.x-k8s.io/v1
kind: SecretProviderClass
metadata:
name: name
spec:
provider: azure
parameters:
keyvaultName: ****
tenantId: ****
objects: |
array:
- |
objectName: lqb
objectAlias: liquibase/spring.liquibase.password
objectType: secret

Related

Why Google Cloud Storage isn't taking my GOOGLE_APPLICATION_CREDENTIALS environment variable [HEROKU]?

I'm developing a POC website with Spring Boot to learn about GCP products, in specific Google Cloud Storage. The context is I am trying to save a profile picture when a user is registered.
From Google documentation, I can use
StorageOptions.getDefaultInstance().getService();
to validate my credentials if I have GOOGLE_APPLICATION_CREDENTIALS on my environment variables. The fact is, I do have GOOGLE_APPLICATION_CREDENTIALS as an environment variable (I'm using Linux Mint) pointing to my .json file (that is valid because using the path method it works), but it always returns 401 UNAUTHORIZED from GCP lib.
After spending HOURS searching, just to make sure I run this command on terminal:
gcloud auth application-default login
The response from this command is:
The environment variable [GOOGLE_APPLICATION_CREDENTIALS] is set to:
[/home/<myuser>/<some_folder>/<myapplication>-<id>.json]
Credentials will still be generated to the default location:
[/home/<myuser>/.config/gcloud/application_default_credentials.json]
To use these credentials, unset this environment variable before
running your application.
Do you want to continue (Y/n)?
After confirming and login with my browser, to my surprise it worked using StorageOptions.getDefaultInstance().getService();
So I'm assuming that GCP takes credentials from application_default_credentials file instead of the GOOGLE_APPLICATION_CREDENTIALS env var.
So on my local environment, it's working fine. The problem is: This website is deployed on Heroku and I can't use the path method because I don't want to put this credentials on GitHub.
I already put the environment variables at Heroku and didn't work.
Already tried to use these buildpacks too:
https://github.com/elishaterada/heroku-google-application-credentials-buildpack
https://github.com/gerywahyunugraha/heroku-google-application-credentials-buildpack
but I can't get it to work when on Heroku Cloud. Someone knows a way I can make this run using the 12factor rules, please?
In order to pass credentials you can do it via environment variables, that in fact is the one you are mentioning. To do it in Linux you should use the below command
export GOOGLE_APPLICATION_CREDENTIALS="[PATH]"
Nevertheless, something to keep in mind is that this variable only applies to your current shell session, so if you open a new session, set the variable again.
On the other hand, another option for perform this task is passing credentials using code, and as you can see in the shared link you should use something like below.
GoogleCredentials credentials = GoogleCredentials.fromStream(new FileInputStream(jsonPath))
.createScoped(Lists.newArrayList("https://www.googleapis.com/auth/cloud-platform"));
Storage storage = StorageOptions.newBuilder().setCredentials(credentials).build().getService();
Finally, in case you do not want to point the credentials inside of your applications's code, you can use Cloud Key Management Service. I think that KMS is a good option for your case but at the end, you should make the decision.

Spring cloud config can't read ENCRYPT_KEY environment variable

I'm currently working on a an application with a microservice architecture. I have two services for now: product service and configuration service.
The configuration service plays the role of spring cloud config server, while the product service plays the role of spring cloud config client.
To launch my application, the configuration service should be run first, and then product service, this way product service can ask its configuration (application.properties) file to set up its connection to database etc. What I want now is to encrypt some sensitive properties like database credentials. Why? 'cause if you reach the endpoint of the cloud config service http://localhost:8888/productservice/env you can find these informations as plain text, which is not good.
For the time being I thought to use symetric encryption so following the documentation if I set a variable environement in my OS as ENCRYPT_KEY, my spring cloud config server should encrypt and decrypt my data.
I'm using windows 10 and using the set command I can see that the ENCRYPT_KEY is set properly with the correct secret word.
However when I try to reach the /encrypt endpoint with a POST method of my cloud config server, it responds with a 404 not found:
{
"description": "No key was installed for encryption service",
"status": "NO_KEY"
}
I'm using Edgware.RELEASE version of spring-cloud.
Thank you
I think the problem is that you set the environment variable with the set command.
When you set a variable with the set command that variable is available only in that CMD session.
What you need is to make the variable visible in your App. To achieve that you can set the environment variable through the System Properties option (tap Windows key and type edit the system environment variables).
Also make sure your app can read the variable value by doing something like System.getenv("ENCRYPT_KEY") in your Application main method.
By the way, if you are using Eclipse you can set environment variables in Run, Run Configurations..., Environment tab.

Java - JNDI / Active Directory / Kerberos / WebLogic Server - Password Configuration

I want to fetch data from an Active Directory using Java and JNDI from my EJB.
Doing this search I need to define a user and a password.
I was thinking of creating a service account (for my server) in the AD.
I will also be using Kerberos protocol and WebLogic Server.
As I understand it now, I need to create a keytab file that will contain this service account's credentials. This keytab file will then be configured in the WebLogic Server?
So, this means that I will have to state the username/password both for the keytab file and in my EJB (to fetch the data from the AD using JNDI). What is the best approach for this? Can the keytab file be defined dynamically? Which would simplify changing the password and only doing this at one place.
Have your machine join the domain, start you WebLogic server with the machine account or give access to the machine keytab, create a JDNI resource with the DirContextSourceFactory and you are able to access the AD as you wish.

Database password storing issue, Java Web App

I'm part of a Java Spring Web app which should be very secure. So far, on test environment we're loading database username & password from a property file which lies on classpath. The password is encrypted with a key which we load from local file system.
My job is to find a better way(more secure one) using software tools only. I was thinking about supplying the db username and password on startup of webapp or smth like that(But still does not seem ok because the DB admin should be present on startup). Other than that I'm stuck.
What is the best way to deal with this issue?
You need to lock down the database so that only the app can talk to it with minimal creds as possible.
One way is restrict it so that only app servers private IP is only accepted. Make it so the database only listens to private (internal) network connections.
The password is a red herring.

When using Jaspyt to encrypt Spring properties files, is an environment variable really a secure place to store the master password?

I'm using Jaspyt and Spring 3 in my Java project. I currently store the database connection properties in a properties file. The user name and password are plain text, so I've been looking at using Jaspyt's EncryptablePropertyPlaceholderConfigurer.
The documentation and tutorials suggest storing the master password used for decryption in an environment variable. Is that really any more secure than storing plain text values in the properties file? If someone compromises the box, wouldn't the master password be either visible in (1) the environment variables or (2) the server start-up script? I suppose you could manually set the environment variable and unset it after server start-up, but the manual process of that seems unmanageable.
Am I just being paranoid? Is there an approach that you've used to secure your connection user names and passwords?
Storeing clear text passwords is never a safe procedure. An attacker who takes over the server has access to your passwords in all cases.
If manual entry is not an option (as usual) you can only hide the password only for a part of your team. If you not want to disclose the database passwords, use a JNDI database connection. This makes the passwords only visible to the application server administrators.

Categories