Using IAM to connect to AWS s3 without credentials in Java - amazon-web-services

I am new to AWS. I am trying to connect to AmazonS3 using IAM role. I am not using secret keys here. Changes have been done at AWS side regarding IAM.
I have to create AmazonS3 client object. I tried to create object by following code:
public AmazonS3 AmazonS3Provider() {
AmazonS3ClientBuilder.defaultClient();
}
But it is returning :
"status":500,"exception":"AmazonS3Exception","message":"Access Denied (Service: Amazon S3; Status Code: 403; Error Code: AccessDenied;
Next, I tried to connect AWS s3 using AWSSecretsManagerClient using the following code:
public AmazonS3 AmazonS3Provider() {     
return (AmazonS3) AWSSecretsManagerClientBuilder.defaultClient();
}
But it was throwing error that AWSSecretsManagerClient object cannot be cast to AmazonS3.
Also, I tried to build client object using the following code:
AWSCredentials credentials;
try {
credentials = new ProfileCredentialsProvider().getCredentials();
} catch (Exception e) {
throw new AmazonClientException(
"Cannot load the credentials from the credential profiles file.", e);
}
return AmazonS3ClientBuilder.standard()
.withCredentials(new AWSStaticCredentialsProvider(credentials))
.withRegion(s3EndPoint)
.build();
But is throwing AmazonClientException.
Can someone guide me how to build client object using IAM role.

IAM access key and secret key (AWS credential) path locations are stored in environment variable. I was able to create client object using WebIdentityTokenCredentialsProvider with the following code:
AmazonS3 client = AmazonS3ClientBuilder.standard()
.withCredentials(WebIdentityTokenCredentialsProvider.create())
.withRegion("Region-name")
.build();

Related

AWS SDK 2.x issues in PUTOBJECT request. Unable to add headers

We recently migrated from AWS SDK 1.x to 2.x for Java and put operation for AWS S3 is not happening. S3 bucket is encryption enabled ( using AWS KMS key).
Below is the code I am trying to use but I am getting error mentioned below that
Server Side Encryption with AWS KMS managed key requires HTTP header x-amz-server-side-encryption : aws:kms (Service: S3, Status Code: 400, Request ID: xxx, Extended Request ID: xxx/rY9ydIzxi3NROPiM=)
Update : I figured it out myself. So anyone who wants to benefit to connect to AWS S3 bucket using KMS key and AWS SDK 2.x for Java use the below code it should work
Map<String, String> metadata = new HashMap<>();
metadata.put("x-amz-server-side-encryption", "aws:kms");
PutObjectRequest request = PutObjectRequest.builder()
//.bucketKeyEnabled(true)
.bucket(bucketName)
.key(Key)
.metadata(metadata)
.serverSideEncryption(ServerSideEncryption.AWS_KMS)
.ssekmsKeyId("arn:aws:kms:xxxxx")
.build();
File outputFile = new File("filename");
try (PrintWriter pw = new PrintWriter(outputFile)) {
data.stream()
.forEach(pw::println);
}
awsS3Client.putObject(request, RequestBody.fromBytes(String.join(System.lineSeparator(),
data).getBytes(StandardCharsets.UTF_8)));

AWS Role vs. Keys

AWS Roles are meant for services which requires access to AWS Services e.g. S3 etc. using temporary credentials. These are done using STS. This is useful when a user/application from one account needs access to a different account-owned resources on a temporary-basis.
However, STS will only issue a temporary credentials when the credentials are passed using Profile properties. At least that's what the code provided by AWS implies anyway
import com.amazonaws.AmazonServiceException;
import com.amazonaws.SdkClientException;
import com.amazonaws.auth.AWSStaticCredentialsProvider;
import com.amazonaws.auth.BasicSessionCredentials;
import com.amazonaws.auth.profile.ProfileCredentialsProvider;
import com.amazonaws.services.s3.AmazonS3;
import com.amazonaws.services.s3.AmazonS3ClientBuilder;
import com.amazonaws.services.s3.model.ObjectListing;
import com.amazonaws.services.securitytoken.AWSSecurityTokenService;
import com.amazonaws.services.securitytoken.AWSSecurityTokenServiceClientBuilder;
import com.amazonaws.services.securitytoken.model.AssumeRoleRequest;
import com.amazonaws.services.securitytoken.model.AssumeRoleResult;
import com.amazonaws.services.securitytoken.model.Credentials;
public class MakingRequestsWithIAMTempCredentials {
public static void main(String[] args) {
String clientRegion = "*** Client region ***";
String roleARN = "*** ARN for role to be assumed ***";
String roleSessionName = "*** Role session name ***";
String bucketName = "*** Bucket name ***";
try {
// Creating the STS client is part of your trusted code. It has
// the security credentials you use to obtain temporary security credentials.
AWSSecurityTokenService stsClient = AWSSecurityTokenServiceClientBuilder.standard()
.withCredentials(new ProfileCredentialsProvider())
.withRegion(clientRegion)
.build();
// Obtain credentials for the IAM role. Note that you cannot assume the role of an AWS root account;
// Amazon S3 will deny access. You must use credentials for an IAM user or an IAM role.
AssumeRoleRequest roleRequest = new AssumeRoleRequest()
.withRoleArn(roleARN)
.withRoleSessionName(roleSessionName);
AssumeRoleResult roleResponse = stsClient.assumeRole(roleRequest);
Credentials sessionCredentials = roleResponse.getCredentials();
// Create a BasicSessionCredentials object that contains the credentials you just retrieved.
BasicSessionCredentials awsCredentials = new BasicSessionCredentials(
sessionCredentials.getAccessKeyId(),
sessionCredentials.getSecretAccessKey(),
sessionCredentials.getSessionToken());
// Provide temporary security credentials so that the Amazon S3 client
// can send authenticated requests to Amazon S3. You create the client
// using the sessionCredentials object.
AmazonS3 s3Client = AmazonS3ClientBuilder.standard()
.withCredentials(new AWSStaticCredentialsProvider(awsCredentials))
.withRegion(clientRegion)
.build();
// Verify that assuming the role worked and the permissions are set correctly
// by getting a set of object keys from the bucket.
ObjectListing objects = s3Client.listObjects(bucketName);
System.out.println("No. of Objects: " + objects.getObjectSummaries().size());
}
catch(AmazonServiceException e) {
// The call was transmitted successfully, but Amazon S3 couldn't process
// it, so it returned an error response.
e.printStackTrace();
}
catch(SdkClientException e) {
// Amazon S3 couldn't be contacted for a response, or the client
// couldn't parse the response from Amazon S3.
e.printStackTrace();
}
}
}
The above code will only work without providing some credentials. So my question is, how is Role useful here when I can just simply use access/secret key ?
Exactly the point you mentioned that the credentials are temporary is one of the many reasons why IAM roles are the recommended approach.
A role can be applied to AWS services as well as resources, for example an EC2 instance can have a role attached with AWS automatically rotating these. Additionally you can use STS to assume a role as role, this can be assumed from an IAM user, a role or a federated user.
You should try to avoid using IAM users where possible, there are some usecases such as signed URLs (where you would like it to last more than a few hours) as well as in an on-premise location. If you must use an IAM key you should make sure to rotate the key frequently.
For more information take a look at the IAM Identities (users, groups, and roles) and Security best practices in IAM pages.

Unable to access Aws KMS service from amazon EKS pods

I have a kubernates pod which is trying to access the aws kms service using the aws java sdk to decrypt the password and I have the valid I am role attached to the pod but the request is failing with the below error,
{
"message": "Service Unavailable: 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), com.amazonaws.auth.profile.ProfileCredentialsProvider#219aa2a6: profile file cannot be null,
com.amazonaws.auth.EC2ContainerCredentialsProviderWrapper#399fc8ea: Internal Server Error (Service: null; Status Code: 500; Error Code: null; Request ID: null)]"
}
below is the code part which supposes to invoke the kms
private final boolean kmsEnabled;
private final AWSKMS kmsClient;
public KmsKeyManager(#Value("${kms.enabled}") final boolean kmsEnabled,
#Value("${kms.endpoint}") final String kmwEndPoint,
#Value("${aws.region}") final String awsRegion) {
AwsClientBuilder.EndpointConfiguration endpointConfig = new AwsClientBuilder.EndpointConfiguration(kmwEndPoint, awsRegion);
kmsClient = AWSKMSClientBuilder.standard()
.withCredentials(new DefaultAWSCredentialsProviderChain())
.withEndpointConfiguration(endpointConfig)
.build();
this.kmsEnabled = kmsEnabled;
}
You may have to create a VPC endpoint since EKS pods may not have an external IP and thus will not be able to connect to KMS
The error message here contains the hint what is missing:
Unable to load AWS credentials from any provider in the chain …
See the docs how to provide the necessary credentials.

DynamoDBLocal Fails on Credentials

DynamoDBLocal is rejecting my credentials in spite of the documentation indicating that valid credentials are unnecessary:
The AWS SDKs for DynamoDB require that your application configuration specify an access key value and an AWS region value...these do not have to be valid AWS values in order to run locally.
In this case, I've set up my credentials ~/.aws/credentials as:
[default]
aws_access_key_id = BogusAwsAccessKeyId
aws_secret_access_key = BogusAwsSecretAccessKey
run DynamoDBLocal using:
java -Djava.library.path=./DynamoDBLoc_lib -jar DynamoDBLocal.jar
checked that it's working by hitting http://localhost:8000/shell/
then run my test Java app:
DefaultAWSCredentialsProviderChain credentialProvider = new DefaultAWSCredentialsProviderChain();
AWSCredentials awsCredentials = credentialProvider.getCredentials();
log.info("creds \"{}\", \"{}\"", awsCredentials.getAWSAccessKeyId(), awsCredentials.getAWSSecretKey());
AmazonDynamoDBClient client = new AmazonDynamoDBClient(credentialProvider);
client.withEndpoint("http://localhost:8000");
client.withRegion(Regions.US_WEST_2);
dynamoDB = new DynamoDB(client);
try {
TableCollection<ListTablesResult> tables = dynamoDB.listTables();
while (tables.iterator().hasNext()) { // <-- exception thrown here
log.info(tables.iterator().next().getTableName());
}
} catch (Exception e) {
log.error("", e);
}
which results in this output:
creds "BogusAwsAccessKeyId", "BogusAwsSecretAccessKey"
com.amazonaws.AmazonServiceException: The security token included in the request is invalid. (Service: AmazonDynamoDBv2; Status Code: 400; Error Code: UnrecognizedClientException
Any thoughts on why it is concerned with the validity of the credentials?
In your code, you are calling withRegion() after calling setEndpoint(). The call to withRegion() is setting the endpoint to DynamoDB's us-west-2 endpoint and that's why your authentication is failing (because it's actually going to the DynamoDB us-west-2 region). Remove the withRegion() line.

AmazonCognitoIdentity AccessDeniedException

I am developing an app and I need use Amazon Cognito for access to AS3 services.
I created an IdentityPool and I have my IdentityPoolId and I created my Developer provider so, I have my Developer provider name. But when I execute getOpenIdTokenForDeveloperIdentity, the response is null:
(Service: AmazonCognitoIdentity; Status Code: 400; Error Code: AccessDeniedException;
try{
AmazonCognitoIdentity identityClient = new AmazonCognitoIdentityClient(
new BasicAWSCredentials(accessKeyId, secretAccessKey)
);
GetOpenIdTokenForDeveloperIdentityRequest tokenRequest = new GetOpenIdTokenForDeveloperIdentityRequest();
tokenRequest.setIdentityPoolId("us-east-1:xxxxxx-xxxx-xxxxx-xxxx-xxxxxxxxx");
HashMap<String, String> providerTokens = new HashMap<String, String>();
providerTokens.put(<developer provider name>,<some identifier backend>);
String token = result.getToken();
System.out.println(token);
}catch (Exception e) {
System.out.println(e.getMessage());
}
And it always fails in catch.
I only have to attach the AmazonCognitoDeveloperAuthenticatedIdentities policy to the user with whom I connect to Amazon and that's it.
amazon Identity and Access Management console
AccessDenied in this case points to that the policies on the IAM user/role do not allow the action you are trying to do. If you have any other questions please refer our developer guide or feel free to post on our forum.