Security

Forms Authentication

The forms authentication script is only called when users login through the login page. If you use any other authentication method, this script is not called. Role policy scripts are called for all authentication types.
By default, the forms authentication script is configured to accept the user Admin and any password. You can configure this authentication policy to interact with whatever system you like. The script will receive a PSCredential object that contains the user name and password entered by the user at the login page.
Authentication settings are also stored with authentication.ps1
To update forms authentication, you can click Settings Security and then click the Settings button for the forms authentication.
You can update the PowerShell script found in settings to configure how the user is authenticated. You'll need to return a New-PSUAuthenticationResult from the script to indicate whether the user was successfully authenticated.
1
param(
2
[PSCredential]$Credential
3
)
4
5
#
6
# You can call whatever cmdlets you like to conduct authentication here.
7
# Just make sure to return the $Result with the Success property set to $true
8
#
9
10
if ($Credential.UserName -eq 'Admin')
11
{
12
New-PSUAuthenticationResult -Success -UserName 'Admin'
13
}
14
else
15
{
16
New-PSUAuthenticationResult -ErrorMessage 'Bad username or password'
17
}
Copied!

Setting a Password

You can check the password of the credential by using the GetNetworkCredential() method of PSCredential.
1
param(
2
[PSCredential]$Credential
3
)
4
5
#
6
# You can call whatever cmdlets you like to conduct authentication here.
7
# Just make sure to return the $Result with the Success property set to $true
8
#
9
10
if ($Credential.UserName -eq 'Admin' -and $Credential.GetNetworkCredential().Password -eq 'MySuperSecretPassword')
11
{
12
New-PSUAuthenticationResult -Success -UserName 'Admin'
13
}
14
else
15
{
16
New-PSUAuthenticationResult -ErrorMessage 'Bad username or password'
17
}
Copied!

Setting Claims

During forms authentication, you can set claims that will be available within role policies. This can provide a performance benefit when interacting with remote systems since you can perform a single claim lookup and then evaluate the claims locally rather than having to make additional calls to the remote system.
This example uses Active Directory to look up group membership and assign the as claims that will be available within the roles scripts.
1
param(
2
[PSCredential]$Credential
3
)
4
5
#
6
# You can call whatever cmdlets you like to conduct authentication here.
7
# Just make sure to return the $Result with the Success property set to $true
8
#
9
10
$Result = [Security.AuthenticationResult]::new()
11
if ($Credential.UserName -eq 'Admin')
12
{
13
#Maintain the out of box admin user
14
New-PSUAuthenticationResult -UserName 'Admin' -Success
15
}
16
else
17
{
18
# Get current domain using logged-on user's credentials - this validates their credential
19
$CurrentDomain = "LDAP://DC=mydemodomain,DC=com" # Insert Your Domain Here
20
$domain = New-Object System.DirectoryServices.DirectoryEntry($CurrentDomain,($Credential.UserName),$Credential.GetNetworkCredential().password)
21
22
if ($domain.name -eq $null)
23
{
24
"Authentication failed for $($Credential.UserName)!" | Out-File "C:\test\adlogin.txt"
25
write-host "Authentication failed - please verify your username and password."
26
New-PSUAuthenticationResult -UserName $Credential.UserName
27
}
28
else
29
{
30
write-host "Successfully authenticated with domain $($domain.name)"
31
"Authentication success for $($Credential.UserName)!" | Out-File "C:\test\adlogin.txt"
32
33
New-PSUAuthenticationResult -UserName $Credential.UserName -Success -Claims {
34
Get-ADPrincipalGroupMembership $Credential.UserName | Select-Object -ExpandProperty name | ForEach-Object {
35
New-PSUAuthorizationClaim -Type Role -Value $_
36
}
37
}
38
}
39
}
Copied!
Within your roles.ps1 file, you will be able to use these claims to validate group membership.
This example checks to see if the user is part of the SOC_Admins group.
1
param($User)
2
3
$Roles = $User.Claims | Where-Object Type -eq Role | Select-Object -ExpandProperty Value
4
$Roles -contains 'SOC_Admins'
Copied!

OpenID Authentication

You can configure OpenID authentication and authorization by adjusting the settings within the OpenID section of the appsettings.json file. Authorization policies that you configure within Universal will be run on the user's identity after authentication is successful.

Windows Authentication

Windows Authentication provides single-sign on support for browsers and environments that support it. To enable Windows Authentication, set the WindowsAuthentication enabled setting to true in appsettings.json.
1
"Windows": {
2
"Enabled": "true"
3
},
Copied!

Windows Authentication in IIS

To enable Windows Authentication in IIS, ensure that you enable Windows Authentication and disable anonymous authentication.

Windows Authentication outside of IIS

Windows Authentication is supported outside of IIS but requires configuration of the account running the Universal service.

Windows

On Windows, you should install PowerShell Universal as a Windows Service. Once the service is installed, you will need to create a service account user and set the service to run with that user's account. The Windows authentication setting needs to be set to true.
1
"Windows": {
2
"Enabled": "true"
3
},
Copied!
The service account needs to have a Service Principal Name (spn) configured for the computer account. You can do this using the setspn command. The computer name needs to be the full qualified name of the machine running Universal.
1
setspn -S HTTP/myservername.mydomain.com myuser
Copied!
For more information, you can follow the Microsoft documentation for configuring ASP.NET Core Windows Authentication: Configuring a Windows machine for Windows Authentication

Linux

Authorization

User authorization is accomplished with roles. Roles are either assigned based on the policy script you define or a role is assigned manually to the user.

Policy Assignment

By default, roles are assigned by policies. Policies are run when the user logs in. You can change the policy scripts by visiting the Security / Roles tab. Click the Edit Policy button to configure the Policy script.
Policy scripts will receive a ClaimsPrincipal object as a parameter and need to return true or false. Policies that throw errors will be assumed to be false. The ClaimsPrincipal object contains the user's identity and the claims that the user has received. These may include group assignments or other features of a user's account.
You can expect an object with this structure.
1
public class ClaimsPrincipal
2
{
3
public List<Claim> Claims { get; set; } = new List<Claim>();
4
public Identity Identity { get; set; } = new Identity();
5
}
6
7
public class Identity
8
{
9
public string Name { get ;set; }
10
}
11
12
public class Claim
13
{
14
public string Type { get; set; }
15
public string Value { get; set; }
16
public string ValueType { get; set; }
17
public string Issuer { get; set; }
18
public Dictionary<string, string> Properties { get; set; } = new Dictionary<string, string>();
19
}
Copied!

Role Assignment

To assign a role to a user, you can create their identity within Universal and then select the role in the drop down on the Identities page. By default, identities receive a role through policy.

Built in Roles

Administrator

Full access to the entire PowerShell Universal platform and settings.

Operator

Operators have access to add and remove resources such as APIs, Scripts and Dashboards. Operators cannot change settings like environments, roles, or general settings.

Execute

The Execute role grants the ability to run scripts and read access for everything else.

Reader

The Reader role provides read-only access to PowerShell Universal.

Users with Many Groups

If your users are members of more than about 40 groups you may experience problems logging in. This is due to size limits of the HTTP headers in IIS and Kestrel. The more groups a user is a member of, the more authorization claims they have and the large the header.
You can increase the header limit for Kestrel by using the limits configuration in appsettings.json file. You will need to increase the header size. It is a value in bytes and defaults to 32kb.
1
{
2
"Kestrel": {
3
"Endpoints": {
4
"HTTP": {
5
"Url": "http://*:5000"
6
}
7
},
8
"Limits": {
9
"MaxRequestHeadersTotalSize": 132768
10
},
11
"RedirectToHttps": "false"
12
},
Copied!

IIS Authorization

Authorization in IIS works as with any other method but you need to be aware of the request header size limit. You may receive errors when you enable claims that include many groups. They can exceed the header size limit and IIS will return errors. We have found that about 40 Azure Active Directory groups will cause this issue on a default IIS installation.
The error you will receive will either be a 400 error with the request is too long.
If you have HTTPS enabled, you will receive an error about a HTTP2 protocol error.
You can increase the IIS request size by setting the following registry keys. You will need to restart you machine in order for them to take affect.
1
HKLM:\System\CurrentControlSet\Services\Http\Parameters
2
MaxFieldLength: DWORD
3
4
HKLM:\System\CurrentControlSet\Services\Http\Parameters
5
MaxRequestBytes: DWORD
Copied!
More information can be found on Microsoft's documentation.
As an alternative to increasing the request size, you can also reduce the number of groups sent. In Azure Active Directory, you can set to just the groups assigned to the application to prevent all groups from being sent.
In Azure go to App registrations > (Select the app) > Token Configuration, and specify Groups assigned to the application.
Now go to Enterprise Application > (Select the app) > Users and groups. Assign the group(s) you are interested in including in the claims. (Note: this can also be used as a security boundary if you set “User Assignment Required” to Yes in the ‘Properties’ section of the app)
Could not load image

App Tokens

App Tokens can be assigned to services that cannot login interactively. You can grant a new app token to your account by clicking the Grant App Token button within the Security / App Tokens tab.
The token will have a expiration of one year and have the valid roles for your account. To copy the App Token to your account, click the Copy action. To revoke an App Token, click the Revoke action.
You can use App Tokens with the Universal cmdlets or by using web requests directly using Bearer authorization.

Environment

By default, the forms authentication and policy assignment scripts run within the PowerShell Universal process. You can optional configure an external Environment to run your authentication and authorization scripts. When you configure a security environment, an external PowerShell process will be started and configured use your Environment's settings.
To adjust the environment used by the security process, set the -SecurityEnvironment in settings.ps1.
1
Set-PSUSetting -SecurityEnvironment '5.1'
Copied!

Example: Forms Authentication with Active Directory

The following example shows performing a simple "LDAP BIND" in order to validate a users Active Directory Credentials. If a user attempting to access PowerShell Universal is not the Default Admin User they will have to successfully authenticate their credentials with Active Directory via a simple LDAP bind. This can be combined with a AD Group Member check in the Admin, Operator, and Reader role policies to effectively use Active Directory Authentication AND Active Directory Group membership to provide Role Based Access to PowerShell Universal.
1
param(
2
[PSCredential]$Credential
3
)
4
5
#
6
# You can call whatever cmdlets you like to conduct authentication here.
7
# Just make sure to return the $Result with the Success property set to $true
8
#
9
10
$Result = [Security.AuthenticationResult]::new()
11
if ($Credential.UserName -eq 'Admin')
12
{
13
#Maintain the out of box admin user
14
$Result.UserName = 'Default Admin'
15
$Result.Success = $true
16
}
17
else
18
{
19
# Get current domain using logged-on user's credentials - this validates their credential
20
$CurrentDomain = "LDAP://DC=mydemodomain,DC=com" # Insert Your Domain Here
21
$domain = New-Object System.DirectoryServices.DirectoryEntry($CurrentDomain,($Credential.UserName),$Credential.GetNetworkCredential().password)
22
23
if ($domain.name -eq $null)
24
{
25
"Authentication failed for $($Credential.UserName)!" | Out-File "C:\test\adlogin.txt"
26
write-host "Authentication failed - please verify your username and password."
27
$Result.UserName = ($Credential.UserName)
28
$Result.Success = $false
29
}
30
else
31
{
32
write-host "Successfully authenticated with domain $($domain.name)"
33
"Authentication success for $($Credential.UserName)!" | Out-File "C:\test\adlogin.txt"
34
$Result.UserName = ($Credential.UserName)
35
$Result.Success = $true
36
}
37
}
38
39
$Result
Copied!

Example: Policy based on Active Directory Group Membership (Windows Authentication)

This example requires an authentication method that will provide group information during the authentication process. Methods like Windows authentication and WS-Federation can provide this information. Forms authentication will not work with this type of policy.
This example takes advantage of the claims that are provided during authentication. You can check to see if the user has a groupsid (group membership) by using the HasClaim method of the $User object. This method will check the Claims array to see if they have a particular groupsid claim. Use the SID of the group to validate whether they are a group member.
1
New-PSURole -Name 'Administrators' -Policy {
2
param(
3
$User
4
)
5
6
$User.HasClaim("http://schemas.microsoft.com/ws/2008/06/identity/claims/groupsid", 'S-1-5-21-22222222-111111-3333333-153')
7
}
Copied!
For debugging and development purposes, you can check to see what claims a user has been exporting the $User variable to a file.
1
New-PSURole -Name 'Administrators' -Policy {
2
param(
3
$User
4
)
5
6
$User | ConvertTo-Json | Out-File .\myUser.json
7
8
$User.HasClaim("http://schemas.microsoft.com/ws/2008/06/identity/claims/groupsid", 'S-1-5-21-22222222-111111-3333333-153')
9
}
Copied!
You should see a JSON file that contains the user's identity and claims array.
1
{
2
"Claims": [
3
{
4
"Type": "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name",
5
"Value": "LAPTOP-496LAUK8\\adamr",
6
"ValueType": "http://www.w3.org/2001/XMLSchema#string",
7
"Issuer": "AD AUTHORITY",
8
"Properties": "System.Collections.Generic.Dictionary`2[System.String,System.String]"
9
},
10
{
11
"Type": "http://schemas.microsoft.com/ws/2008/06/identity/claims/primarysid",
12
"Value": "S-1-5-21-1001",
13
"ValueType": "http://www.w3.org/2001/XMLSchema#string",
14
"Issuer": "AD AUTHORITY",
15
"Properties": "System.Collections.Generic.Dictionary`2[System.String,System.String]"
16
},
17
{
18
"Type": "http://schemas.microsoft.com/ws/2008/06/identity/claims/groupsid",
19
"Value": "S-1-1-0",
20
"ValueType": "http://www.w3.org/2001/XMLSchema#string",
21
"Issuer": "AD AUTHORITY",
22
"Properties": "System.Collections.Generic.Dictionary`2[System.String,System.String]"
23
},
24
{
25
"Type": "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/denyonlysid",
26
"Value": "S-1-5-114",
27
"ValueType": "http://www.w3.org/2001/XMLSchema#string",
28
"Issuer": "AD AUTHORITY",
29
"Properties": "System.Collections.Generic.Dictionary`2[System.String,System.String]"
30
},
31
{
32
"Type": "http://schemas.microsoft.com/ws/2008/06/identity/claims/groupsid",
33
"Value": "S-1-5-21-1002",
34
"ValueType": "http://www.w3.org/2001/XMLSchema#string",
35
"Issuer": "AD AUTHORITY",
36
"Properties": "System.Collections.Generic.Dictionary`2[System.String,System.String]"
37
},
38
{
39
"Type": "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/denyonlysid",
40
"Value": "S-1-5-32-544",
41
"ValueType": "http://www.w3.org/2001/XMLSchema#string",
42
"Issuer": "AD AUTHORITY",
43
"Properties": "System.Collections.Generic.Dictionary`2[System.String,System.String]"
44
},
45
{
46
"Type": "http://schemas.microsoft.com/ws/2008/06/identity/claims/groupsid",
47
"Value": "S-1-5-32-559",
48
"ValueType": "http://www.w3.org/2001/XMLSchema#string",
49
"Issuer": "AD AUTHORITY",
50
"Properties": "System.Collections.Generic.Dictionary`2[System.String,System.String]"
51
},
52
{
53
"Type": "http://schemas.microsoft.com/ws/2008/06/identity/claims/groupsid",
54
"Value": "S-1-5-32-545",
55
"ValueType": "http://www.w3.org/2001/XMLSchema#string",
56
"Issuer": "AD AUTHORITY",
57
"Properties": "System.Collections.Generic.Dictionary`2[System.String,System.String]"
58
},
59
{
60
"Type": "http://schemas.microsoft.com/ws/2008/06/identity/claims/groupsid",
61
"Value": "S-1-5-4",
62
"ValueType": "http://www.w3.org/2001/XMLSchema#string",
63
"Issuer": "AD AUTHORITY",
64
"Properties": "System.Collections.Generic.Dictionary`2[System.String,System.String]"
65
},
66
{
67
"Type": "http://schemas.microsoft.com/ws/2008/06/identity/claims/groupsid",
68
"Value": "S-1-2-1",
69
"ValueType": "http://www.w3.org/2001/XMLSchema#string",
70
"Issuer": "AD AUTHORITY",
71
"Properties": "System.Collections.Generic.Dictionary`2[System.String,System.String]"
72
},
73
{
74
"Type": "http://schemas.microsoft.com/ws/2008/06/identity/claims/groupsid",
75
"Value": "S-1-5-11",
76
"ValueType": "http://www.w3.org/2001/XMLSchema#string",
77
"Issuer": "AD AUTHORITY",
78
"Properties": "System.Collections.Generic.Dictionary`2[System.String,System.String]"
79
},
80
{
81
"Type": "http://schemas.microsoft.com/ws/2008/06/identity/claims/groupsid",
82
"Value": "S-1-5-15",
83
"ValueType": "http://www.w3.org/2001/XMLSchema#string",
84
"Issuer": "AD AUTHORITY",
85
"Properties": "System.Collections.Generic.Dictionary`2[System.String,System.String]"
86
},
87
{
88
"Type": "http://schemas.microsoft.com/ws/2008/06/identity/claims/groupsid",
89
"Value": "S-1-11-96-977963020-1793067495-765970767",
90
"ValueType": "http://www.w3.org/2001/XMLSchema#string",
91
"Issuer": "AD AUTHORITY",
92
"Properties": "System.Collections.Generic.Dictionary`2[System.String,System.String]"
93
},
94
{
95
"Type": "http://schemas.microsoft.com/ws/2008/06/identity/claims/groupsid",
96
"Value": "S-1-5-113",
97
"ValueType": "http://www.w3.org/2001/XMLSchema#string",
98
"Issuer": "AD AUTHORITY",
99
"Properties": "System.Collections.Generic.Dictionary`2[System.String,System.String]"
100
},
101
{
102
"Type": "http://schemas.microsoft.com/ws/2008/06/identity/claims/groupsid",
103
"Value": "S-1-2-0",
104
"ValueType": "http://www.w3.org/2001/XMLSchema#string",
105
"Issuer": "AD AUTHORITY",
106
"Properties": "System.Collections.Generic.Dictionary`2[System.String,System.String]"
107
},
108
{
109
"Type": "http://schemas.microsoft.com/ws/2008/06/identity/claims/groupsid",
110
"Value": "S-1-5-64-36",
111
"ValueType": "http://www.w3.org/2001/XMLSchema#string",
112
"Issuer": "AD AUTHORITY",
113
"Properties": "System.Collections.Generic.Dictionary`2[System.String,System.String]"
114
}
115
],
116
"Identity": {
117
"Name": "LAPTOP-496LAUK8\\adamr"
118
}
119
}
Copied!

Example: Policy based on Active Directory Group Membership

In this example we will configure out Administrator Policy Script to use LDAP to retrieve the membership of an Active Directory Group. Here we have created a group called "PowerShell Universal Admins" where members of the group should be granted Administrator Access in PowerShell Universal. Here we are doing a simple samaccountname check for the user to ensure they are a member of the group. For more robust environments a SID/DN/ObjectGUID check would be more appropriate.
1
param(
2
$User
3
)
4
5
$UserName = ($User.Identity.Name)
6
$UserName = $UserName.Substring($UserName.IndexOf('\')+1,($UserName.Length -($UserName.IndexOf('\')+1)))
7
8
$IsMember = $false;
9
10
# Perform LDAP Group Member Lookup
11
$Searcher = New-Object DirectoryServices.DirectorySearcher
12
$Searcher.SearchRoot = 'LDAP://CN=Users,DC=berg,DC=com' # INSERT ROOT LDAP HERE
13
$Searcher.Filter = "(&(objectCategory=person)(memberOf=CN=PowerShell Universal Admins,OU=Information Technology,DC=berg,DC=com))" #GROUP INSERT DN TO CHECK HERE
14
$Users = $Searcher.FindAll()
15
$Users | ForEach-Object{
16
If($_.Properties.samaccountname -eq $UserName)
17
{
18
$IsMember = $true;
19
"$UserName is a member of admin group!" | Out-File "C:\test\adgroup.txt"
20
}
21
else {
22
"$UserName is NOT member of admin group!" | Out-File "C:\test\adgroup.txt"
23
}
24
}
25
26
return $IsMember
Copied!
Last modified 7mo ago