Setting secret on Azure Keyvault using Managed Identites - java

I have an application using Java Springboot and I have already given access from my Managed Identity to the KeyVault.
When I try to set a new secret using the java code I got this error below:
"message":"Failed to set secret - secret-name \nStatus code 401,
"{"error":{"code":"Unauthorized","message":"AKV10032:
Invalid issuer. Expected one of
public void setAzureTokens() {
try{
SecretClient secretClient = new SecretClientBuilder()
.vaultUrl(keyVaultUri)
.credential(new DefaultAzureCredentialBuilder().build())
.buildClient();
secretClient.setSecret(new KeyVaultSecret(key, value));
}
catch (Exception e){
LOG.error("Error during during token update", e);
}
}
Do I need to set any information about Tenant, clientId, or my Managed Identity on Application.properties?

This is a cross-tenant issue as said by #Alex i.e; this problem arises for whom has access to multiple Azure AD tenancies and when library for accessing the Key Vault endpoint cannot decide which credentials to authenticate you with.
The solution is to tell the DefaultCredentialProvider which tenancy to use, and can be done with the Options that you can pass in DefaultAzureCredentialOptions().
var o = new DefaultAzureCredentialOptions();
o.VisualStudioTenantId = preConfig["AzureTenantId"];
configurationBuilder.AddAzureKeyVault(new Uri(preConfig["KeyVaultName"]), new DefaultAzureCredential(o));
Reference
(or)
You can set AZURE_TENANT_ID as environment variables and can be used as here > Azure Key Vault Secret client library for Java | Microsoft Docs

Related

How can I generate a licensed download link in azure storage blob

I can get a link by those code, and it could download a file successfully.
BlobContainerSasPermission blobContainerSasPermission = new BlobContainerSasPermission()
.setReadPermission(true)
.setWritePermission(true)
.setListPermission(true);
BlobServiceSasSignatureValues builder = new BlobServiceSasSignatureValues(OffsetDateTime.now().plusDays(1), blobContainerSasPermission)
.setProtocol(SasProtocol.HTTPS_ONLY);
BlobClient client = new BlobClientBuilder()
.connectionString("connection string")
.blobName("")
.buildClient();
String blobContainerName = "test";
return String.format("https://%s.blob.core.windows.net/%s?%s",client.getAccountName(), blobContainerName, client.generateSas(builder));
but everyone could download the file by this link, I want the file could be downloaded by the one who is authorized. Is there any code or azure's setting(such as AD?) could implement this? Thanks
-------------------------update------------------------
I find a doc,and there gives a reason.
Note: This method call is only valid when using TokenCredential in this object's HttpPipeline.
but there is only BasicAuthenticationCredential which implements TokenCredential in my imported package. This is my mvn.
<dependency>
<groupId>com.azure</groupId>
<artifactId>azure-storage-blob</artifactId>
<version>12.11.0-beta.2</version>
</dependency>
And I tried this
String userName = "userName";
String password = "password";
BasicAuthenticationCredential basicAuthenticationCredential = new BasicAuthenticationCredential(userName,password);
BlobServiceClient blobServiceClient = new BlobServiceClientBuilder().endpoint(endpoint).credential(basicAuthenticationCredential).buildClient();
then I got this Status code 401, (InvalidAuthenticationInfo)
Gods, help me!
A SAS token for access to a container, directory, or blob may be
secured by using either Azure AD credentials or an account key. A SAS
secured with Azure AD credentials is called a user delegation SAS.
Microsoft recommends that you use Azure AD credentials when possible
as a security best practice, rather than using the account key, which
can be more easily compromised. When your application design requires
shared access signatures, use Azure AD credentials to create a user
delegation SAS for superior security.
Generating SAS query parameters with UserDelegationKey
The following sample generates SAS query parameters for an Azure storage container.
BlobSasPermission blobPermission = new BlobSasPermission()
.setReadPermission(true)
.setWritePermission(true);
// We are creating a SAS to a container because only container name is set.
BlobServiceSasSignatureValues builder = new BlobServiceSasSignatureValues()
.setProtocol(SasProtocol.HTTPS_ONLY) // Users MUST use HTTPS (not HTTP).
.setExpiryTime(OffsetDateTime.now().plusDays(2))
.setContainerName("my-container")
.setPermissions(blobPermission);
// Get a user delegation key after signing in with Azure AD
UserDelegationKey credential = new UserDelegationKey();
String account = "my-blob-storage-account";
BlobServiceSasQueryParameters sasQueryParameters = builder.generateSasQueryParameters(credential, account);
Check out Create a user delegation SAS and this for implementation details.
It looks like there is no way to implement this.
This is the answer.

I tried to retrieve keyvault secret by appservice using MSI in my local machine but got the error:java.net.ConnectException:Connection refused:connect

This current code is giving me the error error:java.net.ConnectException:Connection refused:connect
AppServiceMSICredentials msiCredentials = new AppServiceMSICredentials(AzureEnvironment.AZURE,msiEndpoint, msiSecret);
keyVaultClient = new KeyVaultClient(msiCredentials);
SecretBundle secretBundle = keyVaultClient.getSecret(keyvault url, secretName);
I test the sample in my local, and face the same error. But I can't find detailed information if AppServiceMSICredentials supports access key vault locally.
You could try the official tutorial using a managed identity to connect Key Vault to an Azure Spring Cloud app.
The tutorial is based on Azure Identity library, and you could learn more about it with Java: https://learn.microsoft.com/en-us/azure/developer/java/sdk/identity-azure-hosted-auth.
private void getSecretWithManagedIdentity() {
ManagedIdentityCredential managedIdentityCredential = new ManagedIdentityCredentialBuilder()
.maxRetry(1)
.retryTimeout(duration -> Duration.ofMinutes(1))
.build();
secretClient = new SecretClientBuilder()
.vaultUrl(keyVaultUrl)
.credential(managedIdentityCredential)
.buildClient();
KeyVaultSecret secret = secretClient.getSecret(secret-name);
}

Using AWS Educate with Spring

I am using an AWS Educate account and I would like to use S3 in my Java Spring Boot application.
I try to create a bucket using the following code where in place of access key and secret key I use those listed in Account Details from vocareum page:
#Repository
public class S3Repository {
private final AmazonS3 s3Client;
public S3Repository() {
AWSCredentials credentials = new BasicAWSCredentials(
"<AWS accesskey>",
"<AWS secretkey>"
);
this.s3Client = AmazonS3ClientBuilder.standard()
.withCredentials(new AWSStaticCredentialsProvider(credentials))
.withRegion(Regions.US_EAST_1)
.build();
}
public void createBucket(String name) {
s3Client.createBucket(name);
}
}
When I invoke createBucket(String name) get this exception:
com.amazonaws.services.s3.model.AmazonS3Exception: The AWS Access Key Id you provided does not exist in our records.
I tried creating a new user in IAM but it does not create an access key and secret key due to educating account limitations. Every time I sign in to aws educate account it generates new keys and I'm using current ones. Configuration with YAML file and auto wiring s3Client gives the same result.
Is there any additional configuration that I need to include?
I would like to avoid creating a new regular account if there is another solution.
As you may already know, AWS Educate account have limitation across many services.
AWS Educate account's credential (access key, secret key, session token) would always change within 2-3 hours. But you can use this information to implement what you want.
All you need to do is add a session token along with an access key and secret key. Here is the sample code:
BasicSessionCredentials sessionCredentials = new BasicSessionCredentials(
"<type your aws_access_key_id>",
"<type your aws_secret_access_key>",
"<type your aws_session_token>");
final AmazonS3 s3 = AmazonS3ClientBuilder.standard().withCredentials(new AWSStaticCredentialsProvider(sessionCredentials)).withRegion(
Regions.US_EAST_1).build();
// do whatever you want with s3
...
Please note that this program would work for few hours while the credentials are valid. However, when the session expires, you would get the error again. This is the limitation of the AWS educate account.
src: https://docs.aws.amazon.com/sdk-for-java/v1/developer-guide/credentials.html

The AWS Access Key Id you provided does not exist in our records

I had written a library in PHP using the AWS to communicate with the ECS server. Now I am migrating my code to java. In java I am using the same key and secret which I used in PHP.
In php I used the following method:
$s3 = Aws\S3\S3Client::factory(array( 'base_url' => $this->base_url, 'command.params' => array('PathStyle' => true), 'key' => $this->key, 'secret' => $this->secret ));
In java I am using following method
BasicAWSCredentials(String accessKey, String secretKey);
I am getting the following exception:
Exception in thread "main" com.amazonaws.services.s3.model.AmazonS3Exception: The AWS Access Key Id you provided does not exist in our records. (Service: Amazon S3; Status Code: 403; Error Code: InvalidAccessKeyId; Request ID: 3A3000708C8D7883), S3 Extended Request ID: U2rG9KLBBQrAqa6M2rZj65uhaHhOpZpY2VK1rXzqoGrKVd4R/JdR8aeih/skG4fIrecokE4FY3w=
at com.amazonaws.http.AmazonHttpClient.handleErrorResponse(AmazonHttpClient.java:1401)
at com.amazonaws.http.AmazonHttpClient.executeOneRequest(AmazonHttpClient.java:945)
at com.amazonaws.http.AmazonHttpClient.executeHelper(AmazonHttpClient.java:723)
at com.amazonaws.http.AmazonHttpClient.doExecute(AmazonHttpClient.java:475)
at com.amazonaws.http.AmazonHttpClient.executeWithTimer(AmazonHttpClient.java:437)
at com.amazonaws.http.AmazonHttpClient.execute(AmazonHttpClient.java:386)
at com.amazonaws.services.s3.AmazonS3Client.invoke(AmazonS3Client.java:3996)
at com.amazonaws.services.s3.AmazonS3Client.invoke(AmazonS3Client.java:3933)
at com.amazonaws.services.s3.AmazonS3Client.listBuckets(AmazonS3Client.java:851)
at com.amazonaws.services.s3.AmazonS3Client.listBuckets(AmazonS3Client.java:857)
at com.justdial.DocUpload.DocUpload.main(DocUpload.java:22)
Do I need a new key and secret for Java or the previous one can be used.
My questions are:
1 - Is then any prerequisite that we need to follow before using AWS's BasicAwsCredentials() method.
2 - Do we need to create the IAM roles.
3 - If we do then how will it come to know which IP to hit as in case of PHP in Aws\S3\S3Client::factory method I was specifying the base url.
Following code worked for me.
String accesskey = "objuser1";
String secret = "xxxxxxxxxxxxxxxx";
ClientConfiguration config = new ClientConfiguration();
config.setProtocol(Protocol.HTTP);
AmazonS3 s3 = new AmazonS3Client(new BasicAWSCredentials(accesskey, secret), config);
S3ClientOptions options = new S3ClientOptions();
options.setPathStyleAccess(true);
s3.setS3ClientOptions(options);
s3.setEndpoint("1.2.3.4:9020"); //ECS IP Address
System.out.println("Listing buckets");
for (Bucket bucket : s3.listBuckets()) {
System.out.println(" - " + bucket.getName());
}
System.out.println();
AWS access key and secret access keys are independent of SDKs you use (doesn't matter whether you use PHP, Java or Ruby etc), it's more about granting and getting access to services you run/use on AWS.
Root cause of the error is I think you've not set AWS region though you've given access key and secret access key. Please check this to set AWS credentials for Java.
To answer your questions.
As far as I know BasicAwsCredentials() itself acts as prerequisite step before you want to do anything with Amazon services. Only prerequisite for BasicAwsCredentials() is to have the authentication keys.
No, it's not mandatory step. It's an alternative. Please check the definition of IAM role from this link.
You can use the 'endpoint' to do the same job in Java. Check 'endpoint' details from this link.
Please take a look at the examples from here.
Using the same method I'm able to successfully do the 'BasicAwsCredentials()'. And also cross check whether 'access key' and 'secret access key' have necessary permissions to access Amazon services as per your need. Check IAM user, group and ensure they've necessary AWS permissions.

How to generate a kerberos token without user input

I'm trying to generate a Kerberos client token in java, in order to send it in a header for a kerberized service.
For that I created a jaas login conf, and a keytab for my user.
I want to generate the token automatically without any user input. For now I managed to create a token but i'm being
prompted to enter the user's password, which is not what I want.
When I set the 'doNotPrompt' to 'true' (in the login.conf) I get an exception "unable to obtain password from user",
even though i'm specifying a keytab file.
I found very little code samples in the web, though eventually I used them. But I didn't find an answer to what i'm trying to do.
Does anyone know how to accomplish what I want?
My login.conf is:
com.sun.security.jgss.initiate {
com.sun.security.auth.module.Krb5LoginModule required storeKey="true"
principal="HTTP/MyComp#DOMAIN" useKeyTab="true"
keytab="c:\Users\me\Desktop\abc.keytab";
}
And my java code is:
String clientPrincipal = "HTTP/MyComp#DOMAIN";
String serverPrincipal = "HTTP/ServerComp#DOMAIN";
Oid oid = new Oid("1.2.840.113554.1.2.2");
try
{
GSSManager manager = GSSManager.getInstance();
GSSName gssUserName = manager.createName(clientPrincipal, GSSName.NT_USER_NAME, oid);
GSSCredential clientGSSCreds = manager.createCredential(gssUserName.canonicalize(oid),
GSSCredential.INDEFINITE_LIFETIME,
oid,
GSSCredential.INITIATE_ONLY);
GSSName gssServerName = manager.createName(serverPrincipal, GSSName.NT_USER_NAME);
GSSContext clientContext = manager.createContext(
gssServerName.canonicalize(oid),
oid,
clientGSSCreds,
GSSCredential.INITATE_ONLY);
clientContext.requestCredDeleg(true);
byte[] token = clientContext.initSecContext(token, 0, token.length);
}
catch (GSSException e){
e.printStackTrace();
}
A keytab file can contain keys for different users, so you need to tell your login.conf the principal whose key you'll be using. This principal name is arbitrary: it only needs to match the name you provided when you created the keytab (although I recommend using the actual UPN of the user).

Categories