Full IGA using Azure AD – Managing access using Entitlement Management

In this blog series on building a full Identity Governance and Administration solution, we have until now covered application roles extensively, and how these can be sent to an application.

For a quick summary, this is how you can define custom application roles, here is how to send these roles using the SCIM protocol, this article shows how to transfer the roles using the OpenID Connect ID Token or SAML claim and here I can show you how to use PowerShell to query the Microsoft Graph for application role assignments for your users and groups.

Continue reading “Full IGA using Azure AD – Managing access using Entitlement Management”

Checking Azure AD tenant id using PowerShell

This is a short blog post with a PowerShell cmdlet that will return you the Azure AD tenant id for a given domain.

function Get-AzureADTenantId
{
    [CmdletBinding()]
    [Alias()]
    [OutputType([string])]
    Param
    (
        # Param1 help description
        [Parameter(Mandatory=$true,
                   ValueFromPipelineByPropertyName=$true,
                   Position=0)]
        $DomainName
    )

    Begin
    {
        Load-Assembly System.Xml.Linq | Out-Null
    }
    Process
    {
        $FederationMetadata = Get-AzureADFederationMetadata -Domain $DomainName
        $FederationMetadata.EntityDescriptor.entityID -split "/" | where{$_ -match "^[a-zA-Z0-9]{8}-[a-zA-Z0-9]{4}-[a-zA-Z0-9]{4}-[a-zA-Z0-9]{4}-[a-zA-Z0-9]{12}$"}
    }
    End
    {
    }
}

Get-AzureADTenantId microsoft.com
Get-AzureADTenantId microsoft.onmicrosoft.com

AAD Identity Protection – End-user behavior

Microsoft is moving from a “force Multi-Factor Authentication for all users” model to a risk based model where users are requested to do Multi-Factor Authentication when their sign-in has a risk assigned to it. This could happen for example by an impossible travel, the user logging in from an IP address which has been in contact with a botnet or other “signals” as Microsoft calls them.

First of all, with AAD Identity Protection, instead of doing manual or scripted assigning of MFA, this is now done with an MFA registration policy. Here is an example on what this can look like:

snip_20160321095252

This will let organizations start with a group of users and expand to more and more users. What will happen is that the user will be prompted to register for MFA when they log on with a browser (or Office with ADAL etc.), but their next logon will not require MFA.

snip_20160321095400

When we look at StrongAuthenticationRequirements of the user, it will be empty, meaning that MFA is not enforced, but the user is still ready to use MFA whenever Azure AD requires it.

Now let us look at the Sign-in risk policy:

snip_20160321100036

Here I have configured the policy to be invoked when the machine learning system in Azure AD find my sign-in a low or medium risk. If a high risk is detected, it is blocked completely.

When I sign in from my regular computer, nothing happens. No MFA; everything is like it has always been. This is perfect, and exactly how this should work. Azure AD detects that everything is safe, no risk at all, so I am not prompted for MFA.

Now, let us try signing in through a RDP session from a computer about 2 hours away by air.

snip_20160321095306

 

snip_2016032110100

Suddenly my sign-in requires Multi-Factor Authentication, because I made an impossible travel.

 

How Active Directory vNext and the PAM feature works under the hood

So, i decided to figure out how the new PAM feature in MIM worked as a POC for a customer. I configured two forests (red.goodworkaround.com and blue.goodworkaround.com), where RED was the Privileged Access Management forest.

I got MIM PAM up and running in a matter of minutes using the TechNet Configuring the MIM Environment for Privileged Access Managment. As PAM is still very new, I figure it would be a good idea to follow a guide for once.

I imported some groups using the code below, so far so good. The groups was created in the RED forest with sidHistory from the BLUE forest.

$cred = get-credential -UserName RED\marius -Message "RED forest domain admin credentials"
New-PAMGroup -SourceGroupName "Service Admin - Service G" -SourceDomain blue.goodworkaround.com -Credentials $cred  -SourceDC blue-pc.blue.goodworkaround.com

I tested with the Domain Admins group, and it failed with the error message about limitations in sidHistory not allowing this type of group. I already knew this going in, but I figured I wanted to try.

My customer really want to control their Domain Admins group, so we digged further into the new functionality in Windows Server vNext / Threshold. Here there is a new feature which they sometimes call “Foreign Principal Groups” and sometimes “Shadow Groups”. Searching around the web gave very, very few answers, but I found a few interesting sites:

What’s New and Changed (Windows Server vNext)
A pdf with some details
3.1.1.13.5 ExpandShadowPrincipal
2.453 Attribute msDS-ShadowPrincipalSid

From these sites I was able to deduce that what the MIM PAM feature is actually doing is creating msDS-ShadowPrincipal objects, which are basically groups with an additional attribute msDS-ShadowPrincipalSID. These are created in the “Shadow Principal Configuration” container (which is actually a msDs-ShadowPrincipalContainer) under the Services node in AD Sites and Services.

The following PowerShell however, yielded an error “New-ADObject : The specified method is not supported”:

$g = Get-ADGroup -Identity "Domain Admins" -Server blue.goodworkaround.com
New-ADObject -Type msDS-ShadowPrincipal -OtherAttributes @{
    "msDS-ShadowPrincipalSid" = $g.SID
} -Path "CN=Shadow Principal Configuration,CN=Services,CN=Configuration,DC=red,DC=goodworkaround,DC=com" -Name "BLUE.Domain Admins"
$g = Get-ADGroup -Identity "My Global Group" -Server blue.goodworkaround.com
New-ADObject -Type msDS-ShadowPrincipal -OtherAttributes @{
    "msDS-ShadowPrincipalSid" = $g.SID
} -Path "CN=Shadow Principal Configuration,CN=Services,CN=Configuration,DC=red,DC=goodworkaround,DC=com" -Name "BLUE.My Global Group"

Digging a bit more around I found that this PAM feature is an optional feature that has to be enabled, just as the AD Recycle Bin. Enabling the feature was easy:


Get-ADOptionalFeature -Filter {Name -eq "Privileged Access Management Feature"} | Enable-ADOptionalFeature -Scope ForestOrConfigurationSet -Target red.goodworkaround.com

Now, the PowerShell above worked and the shadow principals was created:

I have not added any members though. So I did that through the GUI:

So what I have now is the following code


Get-ADObject -Filter {objectClass -eq "msDS-ShadowPrincipal"} -SearchBase "CN=Shadow Principal Configuration,CN=Services,CN=Configuration,DC=red,DC=goodworkaround,DC=com" -Properties member

That returns the following shadow principals (with member as you can see):

And when logging on as this user and running whoami /groups, you will see these as added groups:

As you can imagine, this will also completely remove the requirement to use ADMT (or the library of ADMT) to migrate SIDs for groups

Now, one heads up that I have asked Microsoft is about: Domain Admins does not work over the trust with this method (the SID is probably filtered away on the other side). I will update the article when I have more info.

Update: So, I talked to Mark Wahl about this and it turns out that it is planned a QFE for older Windows Server versions to allow for built-in groups over trusts, but this will probably not happen until Windows Server vNext is released. This means that the PAM feature will not work for built-in groups until this QFE is released.

Office 2013 with ADAL not working with Single Sign-On

I am currently testing out Office 2013 with ADAL which is currently in preview. With ADAL, the Office applications support “Modern Authentication” which means web redirects instead of using the old basic authentication and “proxying credentials” through Office 365. I followed the guidance and enabled ADAL. However, despite of using ADFS and having the adfs website added as an “intranett site” in security settings in IE, all I got was forms based authentication and not single sign-on as I expected. I contacted the Microsoft product group and verified that this was indeed supposed to work and was one of the primary use cases.

If you enable the TCOTrace registry key, the %temp%\outlook.exe.txt logfile is created and here I found the following entry:

ADAL: message=’Could not discover endpoint for Integrate Windows Authentication. Check your ADFS settings. It should support Integrate Widows Authentication for WS-Trust 1.3.’, additionalInformation=’Authority: https://login.windows.net/common

To fix this, you need to enable an ADFS endpoint that is disabled by default. To do this you need to run the following PowerShell cmdlet and restart the ADFS service on all servers in the farm.


Enable-AdfsEndpoint -TargetAddressPath "/adfs/services/trust/13/windowstransport"

Now it should work, with ADAL giving you perfect SSO from your Office applications.

Another bug

I also encountered a bug that Microsoft is fixing (also verified after contacting the product group) in the April update. If you find a log line saying CheckADUser: Not AD user found in the log file, even though you are a domain user, you have encountered this bug. To fix, close all Office apps and delete the following registry key below and try again: HKCU\Software\Microsoft\Office\15.0\Common\Identity\SignedOutADUser

After deleting the registry key, ADAL should not try Integrated Windows Authentication instead of Forms Based Authentication.

Virtual hosting with Apache – the good way

There are so many amazingly bad guides to Apache and virtual hosting, so i decided to create a good one. This guide uses Apache2 running om Debian 6. I will not cover installation and stuff. Also, I cut right to the chase.

First, the NameVirtualHost property should just be declared once, and ports.conf is a good place to have it.

/etc/apache2/ports.conf

NameVirtualHost *:80
Listen 80

Second, do not place all virtual hosts in a single file, that’s not very dynamic. Look at this:

# ls /etc/apache2/sites-*
/etc/apache2/sites-available:
total 8
dr-xr-x--- 2 root           www-data     3896 Jun 26 15:33 .
dr-x------ 5 www-data        www-data     3896 Jun  8 13:15 ..
-rwxr-x--- 1 root           www-data     569  Apr 11 21:51 default
-rwxr-x--- 1 root           www-data     569  Apr 19 11:40 sub1.example.org
-rwxr-x--- 1 root           www-data     569  Apr 19 11:41 sub2.example.org
-rwxr-x--- 1 root           www-data     569  Jun 26 15:25 goodworkaround.com

/etc/apache2/sites-enabled:
total 0
dr-xr-x--- 2 www-data  www-data 3896 Jun 26 15:33 .
dr-x------ 5 www-data  www-data 3896 Jun  8 13:15 ..
lrwxrwxrwx 1 root   root  26 Apr 11 21:52 000-default -> ../sites-available/default
lrwxrwxrwx 1 root   root  41 Apr 19 11:49 sub1.example.org -> ../sites-available/sub1.example.org
lrwxrwxrwx 1 root   root  41 Apr 19 11:50 sub2.example.org -> ../sites-available/sub2.example.org
lrwxrwxrwx 1 root   root  37 Jun 26 15:29 goodworkaround.com -> ../sites-available/goodworkaround.com

So what am I doing that no one else is doing? I am symlinking, and I am splitting each domain or subdomain into separate files. Just use place all the domains in the sites-available folder, and symlink it from sites-enabled. This makes it easy to disable sites temporary, by just removing the symlink and reloading apache. Lets take a look one of those files.

/etc/apache2/sites-available/goodworkaround.com

<VirtualHost *:80>
        ServerAdmin webmaster@goodworkaround.com
        ServerName goodworkaround.com
        # ServerAlias www.goodworkaround.com

        DocumentRoot /home/mariussm/websites/goodworkaround.com

        <Directory />
                Options FollowSymLinks
                AllowOverride None
        </Directory>

        <Directory /home/mariussm/websites/goodworkaround.com>
                Options FollowSymLinks MultiViews
                AllowOverride All
                Order allow,deny
                allow from all
        </Directory>

        ErrorLog ${APACHE_LOG_DIR}/goodworkaround.com.error.log

        # Possible values include: debug, info, notice, warn, error, crit,
        # alert, emerg.
        LogLevel warn

        CustomLog ${APACHE_LOG_DIR}/goodworkaround.com.access.log combined

</VirtualHost>

As you can see, it listens on all interfaces (*:80) on port 80, cares only for the hostname goodworkaround.com and has a root folder. So hey, that’s the easy way.