Using Managed Identities for containers running in PCF to access Azure Blob Storage - azure

I would like to know if it is possible to access a blob storage by a container running in Pivotal Cloud Foundry in Azure using a Managed Identity , say system assigned managed identity, or i need to use a Service Principal Object. Any help is appreciated.
Earlier we were using SAS by coding the URL in the code, but want to use Azure AD to do authentication of our API app running inside container. So what is the best way to achieve this

In Pivotal Cloud Foundry, you could not use the managed identity(MSI) to auth, when using MSI to auth, it essentially makes an API call to azure instance metadata endpoint to get the access token, then use the token to auth, it is just available in an Azure environment support MSI.
In this case, the best practice is to use a service principal to auth as you mentioned, please follow the steps below.
1.Register an application with Azure AD and create a service principal.
2.Get values for signing in and create a new application secret.
3.Navigate to the storage account in the portal -> Access Control (IAM) -> assign a Storage Blob Data Contributor/Storage Blob Data Owner role to the service principal like below.
4.The use the java code below, to do a quick test, in my sample, I list all the blobs in a container, just do other things depends on your requirement, replace the values with yours got from step 2.
pom.xml:
<dependency>
<groupId>com.azure</groupId>
<artifactId>azure-identity</artifactId>
<version>1.2.0</version>
</dependency>
<dependency>
<groupId>com.azure</groupId>
<artifactId>azure-storage-blob</artifactId>
<version>12.11.0-beta.1</version>
</dependency>
Code:
import com.azure.identity.ClientSecretCredential;
import com.azure.identity.ClientSecretCredentialBuilder;
import com.azure.storage.blob.BlobContainerClient;
import com.azure.storage.blob.BlobServiceClient;
import com.azure.storage.blob.BlobServiceClientBuilder;
import com.azure.storage.blob.models.BlobItem;
public class vacate {
public static void main(String[] args) {
String clientId="xxxxxx";
String clientSecret="xxxxxx";
String tenantId="xxxxxx";
ClientSecretCredential credential1 = new ClientSecretCredentialBuilder()
.tenantId(tenantId)
.clientId(clientId)
.clientSecret(clientSecret)
.build();
BlobServiceClient storageClient = new BlobServiceClientBuilder()
.endpoint("https://joystoragev2.blob.core.windows.net")
.credential(credential1)
.buildClient();
BlobContainerClient containerClient = storageClient.getBlobContainerClient("tescon1");
System.out.println("\nListing blobs...");
for (BlobItem blobItem : containerClient.listBlobs()) {
System.out.println("\t" + blobItem.getName());
}
}
}
For more details, see Quickstart: Manage blobs with Java v12 SDK.
Update:
If you want a user-interactive way to auth, just use these ways - Authenticating Users instead of ClientSecretCredential.
Actually different auth ways in SDK use different auth flows in Azure AD, e.g. ClientSecretCredential uses client credential flow, it is a non-interactive way, DeviceCodeCredential uses device code flow, it is user-interactive way.
To use these user-interactive ways in your case, you need to navigate to the AD App in the portal -> add the delegated permission user_impersonation of Azure storage.
suppose a particular user should have only read access to the Blob Endpoint and another user might have full CRUD access to the Blob Endpoint, how this will be determined when the user calls this API from a front end application?
Yes, this can be achieved by the delegated permission added above. When the user use the auth ways to login the AD App, the App will let the user consent the user_impersonation permission, after consent(the AAD tenant of the user needs to allow the users to consent the permission by theirselves), the AD App will get the permission from the user, then act on behalf of the user. In short, the permission of the app comes from the user, different users have different permissions, then app will have different permissions.
So in your case, just add users with different roles in the storage account like step 3 above. For read access, just add Storage Blob Data Reader, for CURD access, add Storage Blob Data Contributor/Storage Blob Data Owner, then use the user-interactive ways above, it will work.

Related

Azure Authentication and Authorization using java

How to authenticate azure using java with azure management or client libraries without directly using azure rest API's?
and what are the jars required for this?
Please help with samples.
If you want to use JAVA with Azure management for authentication, you can use the following two methods:
1.Create an instance of ApplicationTokenCredentials to supply the service principal credentials to the top-level Azure object from inside your code:
import com.microsoft.azure.credentials.ApplicationTokenCredentials;
import com.microsoft.azure.AzureEnvironment;
// ...
ApplicationTokenCredentials credentials = new ApplicationTokenCredentials(client,
tenant,
key,
AzureEnvironment.AZURE);
Azure azure = Azure
.configure()
.withLogLevel(LogLevel.NONE)
.authenticate(credentials)
.withDefaultSubscription();
2.File based authentication:
# sample management library properties file
subscription=########-####-####-####-############
client=########-####-####-####-############
key=XXXXXXXXXXXXXXXX
tenant=########-####-####-####-############
managementURI=https\://management.core.windows.net/
baseURL=https\://management.azure.com/
authURL=https\://login.windows.net/
graphURL=https\://graph.windows.net/
please check:here

Retrieve Azure KeyVault secret using client secret

I'm experimenting with various Azure features and currently want to retrieve a secret from KeyVault.
Straight to the case:
I'm using this nuget package to interact with my azure resources.
I've developed a simple .NET Core console app and run it locally.
I have a KeyVault resource with one secret defined which is active and not expired.
I've registered an App in AAD so my locally shipped .NET Core console app has an identity within AAD.
Than I've created a "client secret" within this registered app in AAD to use it to authenticate myself as an app.
After that I've added access policy in my KeyVault resource to allow GET operation for secrets for this registered app:
Then I've developed a small piece of code which should retrieve the desired secret:
public class AzureAuthentication
{
public async Task<string> GetAdminPasswordFromKeyVault()
{
const string clientId = "--my-client-id--";
const string tenantId = "--my-tenant-id--";
const string clientSecret = "--my-client-secret--";
var credentials = new ClientSecretCredential(tenantId, clientId, clientSecret);
var client = new SecretClient(new Uri("https://mykeyvaultresource.vault.azure.net"), credentials);
var secret = await client.GetSecretAsync("admincreds");
return secret.Value.Value;
}
}
However when I'm trying to do this I'm getting an AccessDenied error:
Am I missing something painfully obvious here? Or there is some latency (>30 min for this moment) for which changes from Access policies screen in KeyVault resource are applied?
I test your code and Get permission, it works fine.
From your screenshot, it looks you didn't add the correct service principal related to the AD App to the Access policies.
If you add the service principal related to the AD App, it will appear as APPLICATION, not COMPOUND IDENTITY.
So when you add it, you could search for the client Id(i.e. application Id) or the name of your App Registration directly, make sure you add the correct one.
Make sure your AD App(service principal) has the correct permission in your keyvault -> Access policies

App service to app service auth in Azure using Managed Identity

I have set up two App Services in Azure. 'Parent' and 'Child', both expose API endpoints.
Child has endpoint 'Get'.
Parent has endpoints 'Get' and 'GetChild' (which calls 'Get' on Child using HttpClient).
I want all Child endpoints to require auth via Managed Identity and AAD, and I want all Parent endpoints to allow anonymous. However in Azure I want to set the Parent App Service to have permission to call the Child App Service. Therefore Child endpoints are only accessible by using Parent endpoints (or if you have permissions on a user account to directly use Child).
In the Azure Portal:
Authentication/Authorization
I have enabled 'App Service Authentication' on both App Services.
Child is set to 'Log in with AAD'.
Parent is set to 'Allow Anonymous requests'.
Both have AAD configured under 'Authentication Providers'.
Identity
Set to 'On' for both App Services
Access control (IAM)
Child has Parent as Role Assignment, Type = "App Service or Function App" and Role = "Contributer"
With all the above setup:
Calling Child -> Get, requires me to log in
Calling Parent -> Get, returns the expected response of 200 OK
Calling Parent -> GetChild, returns "401 - You do not have permission to view this directory or page"
Without the use of Client ids/Secrets/Keys/etc, as I thought the idea behind Managed Identity was to throw that all out the window, given all the above, should Parent be able to call Child? And if so, what have I setup wrong?
Calling Parent -> GetChild, returns "401 - You do not have permission to view this directory or page"
Without the use of Client ids/Secrets/Keys/etc, as I thought the idea
behind Managed Identity was to throw that all out the window, given
all the above, should Parent be able to call Child? And if so, what
have I setup wrong?
There are two things that I notice with current setup.
1. Acquire a token using Managed Identity to call "Child" service endpoint from "Parent"
Managed Identity only provides your app service with an identity (without the hassle of governing/maintaining application secrets or keys). This identiy can then be used to acquire tokens for different Azure Resources.
But it is still your App's responsibility to make use of this identity and acquire a token for relevant resource. In this case the relevant resource will be your "Child" API. I think this is probably the part you are missing right now.
Relevant documentation on Microsoft Docs - How to use managed identities for App Service and Azure Functions > Obtain tokens for Azure resources
using Microsoft.Azure.Services.AppAuthentication;
using Microsoft.Azure.KeyVault;
// ...
var azureServiceTokenProvider = new AzureServiceTokenProvider();
string accessToken = await azureServiceTokenProvider.GetAccessTokenAsync("https://vault.azure.net");
// change this to use identifierUri for your child app service.
// I have used the default value but in case you've used a different value, find it by going to Azure AD applications > your app registration > manifest
string accessToken = await azureServiceTokenProvider.GetAccessTokenAsync("https://<yourchildappservice>.azurewebsites.net");
This C#/.NET sample uses Microsoft.Azure.Services.AppAuthentication nuget package and acquires a token for Azure Key Vault. In your case, you will replace https://vault.azure.net with the identifierUri for your "Child" service. It's usually set to https://<yourappservicename>.azurewebsites.net by default, but you can find it's value by going to Azure AD applications and then finding the relevant app registration > manifest. You could also use applicationId for the target application (i.e. "Child") to acquire the token.
In case you're not using C#/.NET, same Microsoft Docs link above also has guidance on how to acuqire token using Managed Identity and REST based calls from any platform. Using REST Protocol
Here is a blog post that also gives a good walk through - Call Azure AD protected website using Managed Service Identity (MSI)
2. Azure RBAC Role Assignments are different from Azure AD roles that you may want to use
I see that you have assigned contributor role to Parent App Service's identity from IAM. This role assignment works for Azure RBAC and help in giving permissions for managing the resources, but Azure AD role claims work differently.
If what you were looking to do is to assign a role to parent app, which can be checked in child app and only then allow the calls there is a different way of setting this up.
I should first mention that this role based setup is for a little advanced scenario and not really mandatory to do. You should be able to call "Child" service from "Parent" once you follow the steps in point 1 described above.
Now once the call from Parent to Child is working, you may want to limit the access to Child app service to only "Parent" or a few valid applications. Here are two approaches to achieve that.
Both the approaches are explained on Microsoft Docs here - Microsoft identity platform and the OAuth 2.0 client credentials flow
Relate SO Posts and Blog
Is there a way to secure an Azure Function that will only be called from a specific Azure Logic App?
Azure Active Directory - How to restrict Backend API App Registration to a specific client App Registration
https://joonasw.net/view/calling-your-apis-with-aad-msi-using-app-permissions
Approach 1 - Use Access Control Lists
When your "Child" API receives a token, it can decode the token and extract the client's application ID from the appid and iss claims. Then it compares the application against an access control list (ACL) that it maintains.
Depending on your requirement, API might grant only a subset of full permissions or all permissions to a specific client.
Approach 2 - Use Application Permissions or Roles
Configure your child API application to expose a set of application permissions (or roles).
This approach is a little more declarative, as you define an application permission that needs to be assigned to any application that can call your child-api.
Navigate to Azure Active Directory > App Registrations > App registration for your child-api app > Manifest
Add a new application role.. using json like this:
"appRoles": [
{
"allowedMemberTypes": [
"Application"
],
"displayName": "Can invoke my API",
"id": "fc803414-3c61-4ebc-a5e5-cd1675c14bbb",
"isEnabled": true,
"description": "Apps that have this role have the ability to invoke my child API",
"value": "MyAPIValidClient"
}]
Assign the app permission to your frontend app
New-AzureADServiceAppRoleAssignment -ObjectId <parentApp.ObjectId> -PrincipalId <parentApp.ObjectId> -Id "fc803414-3c61-4ebc-a5e5-cd1675c14bbb" -ResourceId <childApp.ObjectId>
Now, in the auth token received by your child api, you can check that the role claims collection must contain a role named "MyAPIValidClient" otherwise you can reject the call with Unauthorized exception.
To expand on the accepted answer.
You need to define an "App Role" in the target app registration's manifest. This is the app registration which is used to represent the resource (API App Service).
Then you use the Azure CLI to grant permission for that "App Role" to the Enterprise App (The one generated when you setup a managed identity for the client app). See the "APIs and other Azure AD registered applications" in this article for detailed steps https://blog.yannickreekmans.be/secretless-applications-add-permissions-to-a-managed-identity/
You can retrieve the token using the following once the permissions have been granted. The code snippet below uses Azure.Identity which is now the recommended library for Managed Identity in Azure.
public class AzureAdTokenRetriever : IAzureAdTokenRetriever
{
private readonly ILogger<AzureAdTokenRetriever> logger;
private readonly IMemoryCache inMemoryCache;
public AzureAdTokenRetriever(
ILogger<AzureAdTokenRetriever> logger,
IMemoryCache inMemoryCache)
{
this.logger = logger;
this.inMemoryCache = inMemoryCache;
}
public async Task<string> GetTokenAsync(string resourceId, string scope = "/.default")
{
var resourceIdentifier = resourceId + scope;
if (inMemoryCache.TryGetValue(resourceIdentifier, out var token))
{
this.logger.LogDebug("Token for {ResourceId} and {Scope} were fetched from cache", resourceId, scope);
return (string)token;
}
var tokenCredential = new DefaultAzureCredential();
var accessToken = await tokenCredential.GetTokenAsync(
new TokenRequestContext(new [] { resourceIdentifier }), CancellationToken.None)
.ConfigureAwait(false);
// Set cache options with expiration 5 minutes before the token expires
var cacheEntryOptions = new MemoryCacheEntryOptions().SetAbsoluteExpiration(accessToken.ExpiresOn.AddMinutes(-5));
inMemoryCache.Set(resourceIdentifier, accessToken.Token, cacheEntryOptions);
this.logger.LogDebug("Token for {ResourceId} and {Scope} saved in cache with expiration of {TokenExpiry}",
resourceId, scope, cacheEntryOptions.AbsoluteExpiration);
return accessToken.Token;
}
}

Azure AD - Add app principal to a Group

I have an Azure AD app (AAD App1) which has user assignment enabled. So only, users from a particular group let's say "Group A" can access any resource (let's say an Azure Function API) protected by that Azure AD app.
Now I have another daemon Azure function job, which needs to make an authenticated call to the above mentioned Azure function API. Since this is a daemon job, I have generated another Azure AD app (AAD App2) for this.
Below is my code to get access tokens:
string resourceId = "id of app used to authenticate azure function"; // AAD app ID used by the Azure function for authentication
string clientId = "id of app registered for the daemon job";// AAD app ID of your console app
string clientSecret = "secret of app registered for the daemon job"; // Client secret of the AAD app registered for console app
string resourceUrl = "https://blahblah.azurewebsites.net/api/events";
string domain = "<mytenant>.onmicrosoft.com"; //Tenant domain
var accessToken = await TokenHelper.GetAppOnlyAccessToken(domain, resourceId, clientId, clientSecret);
Now when I try to generate access token to access the Azure function API, I get an invalid grant error as below:
AdalException:
{"error":"invalid_grant","error_description":"AADSTS50105: Application
'' is not assigned to a role for the application
''.\r\nTrace ID:
6df90cf440-c16d-480e-8daf-2349ddef3800\r\nCorrelation ID:
4c4bf7bf-2140-4e01-93e3-b85d1ddfc09d4d\r\nTimestamp: 2018-05-09
17:28:11Z","error_codes":[50105],"timestamp":"2018-05-09
17:28:11Z","trace_id":"690cf440-c16d-480e-8daf-2349ddef3800","correlation_id":"4c4bf7bf-2140-4e01-93ef3-b85d1dc09d4d"}:
Unknown error
I am able to properly generate AAD access tokens if I disable the user assignment.
I am trying to avoid creating a service account here. Is there anyway I can add an app principal to an Azure AD group or add it as a member of another Azure AD app?
Unfortunately, you cannot add an AAD application/service principal as a member of Azure AD group.
I have confirmed this issue in My Answer for another similar question [EDIT - now seems to be possible, see said answer]
You can also upvote this idea in our Feedback Forum. Azure AD Team will review it.
Hope this helps!

Azure AD add keys via Azure CLI

I'm trying to add a key in my Azure AD application using Azure CLI.
But looking throught the Azure CLI API it seems that there is no such command.
For exmaple:
I'm trying to automate the task from the link below via Azure CLI:
http://blog.davidebbo.com/2014/12/azure-service-principal.html
I can create AD application, service principal, but I can't find a way to add key for newly create AD application.
I'll appreciate any ideas and directions :)
Thanks in advance !
For a new AD application, you can specify a key with -p while creating. For example,
azure ad app create -n <your application name> --home-page <the homepage of you application> -i <the identifier URI of you application> -p <your key>
For an existing AD application, surely the Graph API is able to update the AD Application Credential. Read this API reference, and you can see that the password credential is able to use "POST, GET, PATCH". However, it's too complicated to use the Graph API. I have check the Azure CLI. That functionality is not yet implemented, and the source is unreadable for me. Then, I took a look at Azure SDK for Python, because I am familiar with python, and I found out that they have already implemented it in 2.0.0rc2. See the GitHub Repo
I have written a python script. But, in order to use my script you need to install not only azure2.0.0rc2, but also msrest and msrestazure.
from azure.common.credentials import UserPassCredentials
from azure.graphrbac import GraphRbacManagementClient, GraphRbacManagementClientConfiguration
from azure.graphrbac.models import ApplicationCreateParameters, PasswordCredential
credentials = UserPassCredentials("<your Azure Account>", "<your password>")
subscription_id = "<your subscription id>"
tenant_id = "<your tenant id>"
graphrbac_client = GraphRbacManagementClient(
GraphRbacManagementClientConfiguration(
credentials,
subscription_id,
tenant_id
)
)
application = graphrbac_client.application.get('<your application object id>')
passwordCredential = PasswordCredential(start_date="2016-04-13T06:08:04.0863895Z",
end_date="2018-04-13T06:08:04.0863895Z",
value="<your new key>")
parameters = ApplicationCreateParameters(application.available_to_other_tenants,
application.display_name,
"<the homepage of your AD application>",
application.identifier_uris,
reply_urls=application.reply_urls,
password_credentials = [passwordCredential])
application = graphrbac_client.application.update('<your application object id>', parameters)
The only problem with this script is that you are only able to override all the existing keys of you AD application. You are not able to append a new key. This is a problem of the Graph API. The Graph API does not allow users to read an existing key. One possible solution would be storing your existing keys somewhere else. But, this will bring extra security risk.
I don't have any experience of automating adding the key, I'm not sure it's even possible to be honest. However have a look at the ApplicationEntity documentation in the Graph API, it might be possible using a POST request to the web service.

Resources