The application
Simple REST API registration service in Spring, after sending proper POST request new user is created in database and Amazon SES sends an email with registration link to verify.
The problem
Locally after setting local variables (AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY and AWS_DEFAULT_REGION) in my OS (Windows) app works just fine, but the problem starts after deploying it. I have an EC2 Instance with Amazon Linux AMI on AWS:
created user at AWS Identity and Access Management (IAM), granted AmazonSESFullAccess role
logging by CMD using shh with the private key file *.pem
Tomcat8 service started
MySQL service started
application *.war file deployed
created environment variables using 'export' command and I checked 'printenv' just to be sure everything is fine
after sending POST request I got exception (below) which means that user has been created but Amazon SES didn't send an email confirmation because couldn't authenticate
{
"timestamp": "2020-04-26T15:44:44.010+0000",
"message": "Unable to load AWS credentials from any provider in the chain: [EnvironmentVariableCredentialsProvider: Unable to load AWS credentials from environment variables (AWS_ACCESS_KEY_ID (or AWS_ACCESS_KEY) and AWS_SECRET_KEY (or AWS_SECRET_ACCESS_KEY)), SystemPropertiesCredentialsProvider: Unable to load AWS credentials from Java system properties (aws.accessKeyId and aws.secretKey), WebIdentityTokenCredentialsProvider: To use assume role profiles the aws-java-sdk-sts module must be on the class path., com.amazonaws.auth.profile.ProfileCredentialsProvider#23fac1a3: profile file cannot be null, com.amazonaws.auth.EC2ContainerCredentialsProviderWrapper#68aa5a98: The requested metadata is not found at http://169.254.169.254/latest/meta-data/iam/security-credentials/]"
}
I checked again local environment variables on my EC2 instance and it was looking fine but to be sure I re-configured it using 'aws configure' command
exception keeps showing, somehow application cannot get environment variables, I'm fighting with that for over 5 hours now so hopefully someone will come here to rescue me...
Piece of code (works fine locally):
AmazonSimpleEmailService client =
AmazonSimpleEmailServiceClientBuilder
.standard()
.withCredentials(DefaultAWSCredentialsProviderChain.getInstance())
.withRegion(Regions.EU_CENTRAL_1)
.build();
I am total Linux noob, having problems with simple commands so please be gentle with solutions requiring some console commands.
If you're running app on EC2, don't use IAM user.
Instead create IAM role with same permissions and assign that role to the instance. If app uses AWS SDK it will be able to pick up credentials without any problems.
In your case problem is probably app's environment being different from yours, if you export credentials in your bash session it will not pass to app if it's loaded under different user or bash session.
The DefaultAWSCredentialsProvider has multiple places it will look for credentials. Instead of setting up your credentials as an environment variable, you can set up a credentials profile. See this documentation: Working with AWS Credentials.
Make sure you have the AWS CLI installed, then you can run the following command to configure your profile: aws configure
Click here for the documentation on the aws configure command.
If you have already configured your aws profile and it still does not work, you have most likely configured the profile for the wrong linux user. For example, if a linux user named tomcat8 is the user who is running your tomcat instance, then you need to set up a credentials profile at /home/tomcat8/.aws/credentials/
Related
I am working on creating some scheduled jobs using the Java SDK for google cloud scheduler. Here is the link for the application code which is already posted as a part of another question. The application basically creates a Cloud Scheduler job, which every time it runs, triggers a custom training job on VertexAI. Now the call from the scheduler to VertexAI to create the custom job is authenticated using the service account. My question is about the authentication of the application code that creates the Cloud Scheduler job itself. I have set this application as a maven project and I create a single executable jar. The application itself runs on my local workstation. The following are my points/questions:
When I create a docker image and copy this jar, and the service account key into the image, and then set the GOOGLE_APPLICATION_CREDENTIALS environment variable to point to the key within the container, then the application runs fine and the Cloud Scheduler job gets created.
When I do the same as above, except I simply execute the jar in powershell (with GOOGLE_APPLICATION_CREDENTIALS environment variable pointing to the service account key), the permission is denied.
Same as 2, except I simply run the application using the eclipse "Run App" button.
How can I authenticate to run the application without having to run in a docker container. And is there a way to authenticate without using the GOOGLE_APPLICATION_CREDENTIALS environment variable, i.e., directly in the application code itself. Links to sample code/examples will be helpful.
EDIT:
For point 2, the problem was a typo in the name of the environment variable. For point 3, you can set environment variables directly in eclipse as mentioned in the answer by #RJC.
I don't have Eclipse on my machine, but I've found a related answer where you can add a specific environment variable within the IDE itself. I suggest that you try to do the following and see if it fixes the problem.
There is another way to authenticate without using GOOGLE_APPLICATION_CREDENTIALS, and that's through explicitly pointing to your service account file in your code. I've created a sample code that retrieves a Job Name without using the GOOGLE_APPLICATION_CREDENTIALS. Authentication is done by specifying a credential setting when initializing the CloudSchedulerClient.
Here's what I've done on my end:
Use the gcloud iam service-accounts keys create serviceaccount.json --iam-account=NAME#PROJECT_ID.iam.gserviceaccount.com that will generate a JSON file for the service account that will be used in CredentialsProvider.
Create a CredentialsProvider object that will call the created JSON file of the service account.
try {
JobName name = JobName.of("[PROJECT]", "[LOCATION]", "[JOB]");
CredentialsProvider credentialsProvider =
FixedCredentialsProvider.create(
ServiceAccountCredentials.fromStream(new FileInputStream("/path/to/serviceaccount.json")));
CloudSchedulerSettings cloudSchedulerSettings = CloudSchedulerSettings.newBuilder().setCredentialsProvider(credentialsProvider).build();
CloudSchedulerClient cloudSchedulerClient = CloudSchedulerClient.create(cloudSchedulerSettings);
System.out.println(cloudSchedulerClient.getJob(name).toString()); // To display the output
cloudSchedulerClient.close();
} catch (IOException e) {
e.printStackTrace();
}
For the additional guidance, here's an API reference to customize credentials.
Note that you are using a service account which can be read by unauthorized person if improperly mishandled. My suggestion is to only set your service accounts with permissions required to perform your task/s. You can also follow this best practices for managing your credentials moving forward.
I have installed google datastore emulator in my local machine along with it written a sample spring boot application .
I can't connection datastore emulator
This is my application.properties config
spring.cloud.gcp.datastore.project-id=project-id
spring.cloud.gcp.datastore.emulator.enabled=true
spring.cloud.gcp.datastore.emulator-host=http://localhost:8081
by this config , I will throw Exception
The Application Default Credentials are not available. They are available if running in Google Compute Engine. Otherwise, the environment variable GOOGLE_APPLICATION_CREDENTIALS must be defined pointing to a file defining the credentials. See https://developers.google.com/accounts/docs/application-default-credentials for more information.
When using the Datastore emulator, you don't need credentials for running the application, so it might be that the library doesn't know that.
However, if you want to try it providing credentials, once you have a service account created, then run in the shell the following:
export GOOGLE_APPLICATION_CREDENTIALS="KEY_PATH"
KEY_PATH you have to replace it with the path of the JSON file that contains your service account key. You can find more information here.
I am trying to connect to Azure KeyVault from my locally running Spring Boot Application. I can't keep those secrets to be saved in keyvault in different properties or yaml during dev, because my application will generate and delete so many secrets and tokens to be saved in keyvault in the run time.
I am aware of the process in which we can create an Azure service principal from your application registration. And use
azure.keyvault.client-id
azure.keyvault.client-key
in application.properties to connect.
But it may not be allowed to be created Azure service principal in our case. So is there any way to connect to key vault using MSI from locally running SpringBoot application.
using MSI_ENDPOINT
and MSI_SECRET
So is there any way to connect to key vault using MSI from locally running SpringBoot application.
using MSI_ENDPOINT and MSI_SECRET
I don't think you can use MSI_ENDPOINT and MSI_SECRET get the token in local, it just works when the web app published in the cloud.
But it may not be allowed to be created Azure service principal in our case.
As you know, you can use the service principal client id and secret(key) to access the keyvault. Actually, when enabling the MSI of the web app, it will create a service principal in your Azure AD tenant automatically. So you can just use the client id and secret of it.
Navigate to the portal -> Azure Active Directory -> Enterprise applications -> search for your web app name(select the Application Type with All Applications), then you get the client id(application id).
Note: Remember to check the object id of the service principal with that in your web app -> Identity, make sure you use the correct one.
For the service principal secret, you could create it via powershell like below(your account need the admin role Application administrator or Global administrator in your AAD tenant).
New-AzureADServicePrincipalPasswordCredential -ObjectId <service principal object id>
Then you will be able to access the keyvault with the client id and secret. For details in java, you can refer to this link.
You can't get it using those variables because locally there is no Azure AD Identity Registered on your local machine and as such Microsoft didn't build any MSI emulator so no variables will be set.
I can recommend what Microsoft did in their .NET library
Run Azure CLI and log in
In code check for variables and if they don't exist then run CLI command
az account get-access-token --resource 'https://vault.azure.net'
In CLI simply log into either principal or your account. Make sure to add this account/your account to KeyVault policy.
I know it's weird but I you can even check it HERE on their GitHub.
I might have an article that will help you in case you want more details
https://marczak.io/posts/2019/07/securing-websites-with-msi/
I set the AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY as environment variables in my local computer and it works fine. When I deploy to Elastic Beanstalk, I set the "Environment properties", but they are passed as -D Java system properties, not set as environment variables, and it generates an error.
https://docs.aws.amazon.com/elasticbeanstalk/latest/dg/environments-cfg-softwaresettings.html?icmpid=docs_elasticbeanstalk_console
while the Tomcat platform sets Java system properties that you retrieve with System.getProperty.
Error
2018-08-03 02:34:37.001 INFO 32073 --- [nio-8080-exec-9] c.s.xxxxx.apis.XxxxxxxApiController : The email was not sent. Error: User 'arn:aws:sts::849611986161:assumed-role/aws-elasticbeanstalk-ec2-role/i-0f447c52c84e1fd93' is not authorized to perform 'ses:SendEmail' on resource 'arn:aws:ses:us-east-1:849611986161:identity/xxxxxx#xxxxx.com' (Service: AmazonSimpleEmailService; Status Code: 403; Error Code: AccessDenied; Request ID: c3835dda-96c5-11e8-8a21-67774160691b)
Amazon SES is expecting environment variables only (or a 'shared credential file' in your home directory). So how do I pass AWS keys to Amazon SES in Elastic Beanstalk for a Java Spring Boot application using the environment?
You can use SSM(Systems Manager Service) parameter store to keep your environment variables and access it through any service which has IAM permissions. In this scenario since you need Access Keys, it doesn't need any of the environment variables to be kept. You can just use your Instance Profile inside elastic beanstalk and it will provide you the access key and secret access key of your assigned IAM for the elastic beanstalk. Please refer this document.
According to https://docs.aws.amazon.com/sdk-for-java/v1/developer-guide/credentials.html#credentials-default
Java system properties–aws.accessKeyId and aws.secretKey. The AWS SDK for Java uses the SystemPropertiesCredentialsProvider to load these credentials.
So instead of AWS_ACCESS_KEY_ID, set aws.accessKeyId, which is then set as a Java system property on the command line, which is picked up by Amazon SES SDK.
I've created my java web application on a tomcat server which will start another instance using the AWS Java SDK, on windows i just place the credentials in my user. Im now trying to host my application on an AWS EC2 Instance and hence i am trying to place my credentials on the Linux EC2 i've follow some steps on the AWS SDK - http://docs.aws.amazon.com/AWSSdkDocsJava/latest/DeveloperGuide/java-dg-setup.html as per the link but im still thrown the same error upon calling the method -
Cannot load the credentials from the credential profiles file. Please
make sure that your credentials file is at the correct location
(~/.aws/credentials), and is in valid format.
I've created a .aws folder in my home directory an placed the credential file within it, i've also added the export codes within the .bashrc file but it doesnt seem to work.
At Wits end here :(
Check what user tomcat runs as on the other machine.
When you store the credential for your user, they are stored at ~/.aws/credentials.
That's ok for you, but Tomcat may not be running as you.
So ensure a copy is present also at /home/{whateveryourtomcatuseris}/.aws/credentials.