I have a record:
#ConfigurationProperties("service.api")
#ConstructorBinding
public record ServiceApiConfig(String clientId, String clientSecret) {}
and an application.properties file:
service.api.client_id=client_id
service.api.client_secret=client_secret
and the binding works when I'm doing local development.
What I want to do is to use Azure services (Azure Spring Apps, Key Vault, etc.) and my question is whether I can, inside application.properties file, reference secrets stored in the Key Vault. Something like this:
service.api.client_id=<name-of-the-id-kept-in-the-vault>
service.api.client_secret=<name-of-the-secret-kept-in-the-vault>
Tutorials I've seen make me think that the only way to do the binding is by using #Value("${nameOfTheSecret}") annotation on fields in java classes. Is it possible to do it in a way that I want?
You can reference Azure Key Vault secrets inside the application.properties file by adding the below properties of azure key vault which you want to connect from the spring boot application:
spring.cloud.azure.keyvault.secret.property-sources[0].credential.client-id=<your client ID>
spring.cloud.azure.keyvault.secret.property-sources[0].credential.client-secret=<your client key>
spring.cloud.azure.keyvault.secret.property-sources[0].endpoint=https://contosokv.vault.azure.net/
spring.cloud.azure.keyvault.secret.property-sources[0].profile.tenant-id=<your tenant ID>
Related
I have some java services which use environment variables for config values.
I'd like to migrate them to use Spring Cloud config instead of environment variables.
Currently, my config is all in application.yml files, as the following:
someKey: ${SOME_KEY_ENV_VAR}
If I were to migrate to using Spring Cloud Config, how would I modify the above line to load its value from the cloud config server, instead of environment variables? (Assuming I've separately setup the maven dependencies & other configuration, to hook them up)
All examples of cloud config clients only show java code, e.g:
#Value("${someKey}")
private String someKey
Is that enough, or will I also need to make any changes to the yaml?
What about things like datasource URLs which don't have a corresponding #Value but are only defined in yaml?
Our application needs to connect to confluent kafka and thus we have the following setups inside application.yaml file
kafka:
properties:
sasl:
mechanism: PLAIN
jaas:
config: org.apache.kafka.common.security.plain.PlainLoginModule required username={userName} password={passWord};
The {userName} and {passWord} need to be replaced by value fetching from AWS secret manager. These are what I have done so far.
Step 1: Use the following maven dependency
<dependency>
<groupId>com.amazonaws</groupId>
<artifactId>aws-java-sdk-secretsmanager</artifactId>
</dependency>
Step 2: Create a configuration class and create a method annotated with #Bean to init a AWSSecretsManager client object.And we can get some key value pairs by using AWSSecretsManager object.
// Create a Secrets Manager client
AWSSecretsManager client = AWSSecretsManagerClientBuilder.standard()
.withRegion(region)
.withCredentials(new AWSStaticCredentialsProvider(new BasicAWSCredentials(accessKey, secretKey)))
.build();
I have the following questions to ask:
How can we inject the value we get from secret manager and replace the placeholder in the application.yml file?
To access AWSSecretsManager we need to pass AWS accessKey and seretKey. What is a good practice to provide those two values?
Some more info:
our application will be running on AWS ECS
I wouldn't recommend doing this via Java code at all. I would totally remove the aws-java-sdk-secretsmanager dependency, and use the ECS support for injecting SecretsManager values as environment variables.
My answer here will focus on the Secrets Manager API part of your question
I recommend that you move from AWS SDK for Java V1 to AWS SDK for Java V2. You can find V2 Java Secret Manager examples here.
https://github.com/awsdocs/aws-doc-sdk-examples/tree/main/javav2/example_code/secretsmanager
Here is the Service Client for V2.
SecretsManagerClient secretsClient = SecretsManagerClient.builder()
.region(region)
.credentialsProvider(ProfileCredentialsProvider.create())
.build();
In this example, I am using a ProfileCredentialsProvider that reads creds from .aws/Credentials. You can learn more about how V2 handles creds in the AWS Java V2 DEV Guide.
Using credentials
You cannot use ProfileCredentialsProvider in an app deployed to a container as this file structure not part of the container. So you can use Amazon ECS container credentials:
The SDK uses the ContainerCredentialsProvider class to load credentials from the AWS_CONTAINER_CREDENTIALS_RELATIVE_URI system environment variable.
See point 5 in the above Doc.
I am working on Java Springboot with MongoDB using Kubernetes. Currently I just hard coded the URI in application properties and I would like to know
how can I access to the MongoDB credentials on Kubernetes with Java?
The recommended way of passing credentials to Kubernetes pods is to use secrets and to expose them to the application either as environment variables, or as a volume. The link above describes in detail how each approach works.
If I properly understood the question, it is specifically about Java Spring Boot applications running on Kubernetes.
Few options come to my mind...some not that secure or exclusive to running on Kubernetes but still mentioned here:
Environment variables with values in the deployment/pod configuration. Everyone with access to the configuration will be able to see them.
Use ${<env-var>} / ${<end-var>:<default-value>} to access the environment variables in Spring Boot's application.properties/.yaml file. For example, if DB_USERNAME and DB_PASSWORD are two such environment variables:
spring.data.mongodb.username = ${DB_USERNAME}
spring.data.mongodb.password = ${DB_PASSWORD}
...or
spring.data.mongodb.uri = mongodb://${DB_USERNAME}:${DB_PASSWORD}#<host>:<port>/<dbname>
This will work regardless whether the application uses spring.data.mongodb.* properties or properties with custom names injected in a #Configuration class with #Value.
Based on how the Java application is started in the container, startup arguments can be defined in the deployment/pod configuration, similarly to the bullet point above.
Environment variables with values populated from secret(s). Access the environment variables from SpringBoot as above.
Secrets as files - the secrets will "appear" in a file dynamically added to the container at some location/directory; it would require you to define your own #Configuration class that loads the user name and password from the file using #PropertySource.
The whole application.properties could be put in a ConfigMap. Notice that the properties will be in clear text. Then populate a Volume with the ConfigMap so that application.properties will be added to the container at some location/directory. Point Spring Boot to that location using spring.config.location as env. var, system property, or program argument.
Spring Cloud Vault
Some other external vault-type of secure storage - an init container can fetch the db credentials and make them available to the Java application in a file on a shared volume in the same pod.
Spring Cloud Config...even though it is unlikely you'd want to put db credentials in its default implementation of the server storage backend - git.
Due to some new security requirments the api I'm developing now is required to store several urls, azure account names etc. in the azure key vault, rather than in the application.yml config file.
The issue is that I'm having trouble authenticating / accessing the key vault client in a Local environment. I have very limited access to the azure functions / key vault itself so testing the new code I'm writing is near impossible at current:
public String getSecretFromKeyVault(String key) {
/**
* Breaks in the constructor call, as the system.env variables for MSI_ENDPOINT and MSI_SECRET are null.
**/
AppServiceMSICredentials credentials = new AppServiceMSICredentials(AzureEnvironment.AZURE);
KeyVaultClient client = new KeyVaultClient(credentials);
SecretBundle secret = client.getSecret("url-for-key-vault", key);
return secret.value();
}
I'm aware that the variables will be set in the cloud server, but my question is how can I best verify that the vault calls have been implemented properly(unit, integration, e2e local tests), and how would I manage to use key vault calls during local development / runtime?
The alternative to MSI would be to enter the client id and key manually, following authentication against the active directory. This could be a solution for local development, but Would still require the declaration of confidential information in the source code.
Ive also tried logging in to azure using az login before running the server but that didn't work either.
Does anyone have any suggestions on how I might resolve this issue, or what my best options are going forward?
Notes on application:
Java version: 8
Spring boot
Azure / vsts development and deployment environment
Since you're using spring-boot you may be better off using Microsoft's property source implementation that maps the keyvault properties into Spring properties and for local development and testing you set equivalent properties in property files.
Use Spring profiles. let's say you have azure and local profiles. In your application-azure.yml file configure your app to use keyvault:
# endpoint on the azure internal network for getting the identity access token
MSI_ENDPOINT: http://169.254.169.254/metadata/identity/oauth2/token
MSI_SECRET: unused
# this property triggers the use of keyvault for properties
azure.keyvault:
uri: https://<your-keyvault-name>.vault.azure.net/
Now you can inject secret properties from the spring context into your variables and they will be read from keyvault:
#Value("${superSecretValue}")
String secretValue;
To make this work locally for testing, in your application-local.yml file you can set the secret property to something appropriate:
superSecretValue: dummy-for-testing-locally
The Azure dependency you need to add to build.gradle is:
implementation "com.microsoft.azure:azure-keyvault-secrets-spring-boot-starter:2.1.6"
Run your spring-boot jar with azure as the active profile when deployed, and local when testing and developing away from azure. This is tested and working with azure java containers.
Recently I have gone through Jasypt API to secure the property file entries. As per Jasypt, in order to decrypt the entry in the property file that was enclosed with ENC(..), we need to use a secure password, a secret key, as shown below (Not a web application):
encryptor.setPassword("jasypt"); // could be got from web, env variable..
Of course we can configure such password using
org.jasypt.encryption.pbe.config.SimplePBEConfig setPassword()
But my question, if we extract the jar file, 3rd party could be able to find out the secret key. How could we ensure security in such cases?
Thanks in advance,
JK
secret key should be stored in environment variable outside the application.
For example, in your spring configuration file:
<bean id="environmentConfig" class=
"org.jasypt.encryption.pbe.config.EnvironmentStringPBEConfig"
p:passwordEnvName="APP_ENCRYPTION_PASSWORD" ...
Now, add APP_ENCRYPTION_PASSWORD in the env variable either in OS or app server.