-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathRoleMap.psm1
More file actions
168 lines (142 loc) · 5.69 KB
/
Copy pathRoleMap.psm1
File metadata and controls
168 lines (142 loc) · 5.69 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
<#
.SYNOPSIS
Maps the three role names used in intended-state.yml to CyberArk safe permissions.
.DESCRIPTION
CyberArk safe membership is ~22 independent booleans. Handing that surface to
whoever edits the state file guarantees drift: two people who both mean
"auditor" will tick different boxes, and six months later nobody can say which
set was intended.
So the state file speaks in three roles, and this module is the only place the
translation lives. Change a role's meaning here and every safe converges on the
next reconciliation run.
The three roles map to the separation any PAM review expects:
audit - can see that an account exists and read the log. Cannot retrieve
a credential. This is the set people most often get wrong, because
ListAccounts feels harmless and RetrieveAccounts is the actual
boundary between "oversight" and "access".
use - can retrieve and connect. Cannot change membership or delete.
The incident-responder set.
full - can administer the safe and its membership. Deliberately does NOT
include the dual-control request authorizations; approving your own
access request is not an administrative convenience.
#>
$script:RoleDefinitions = @{
audit = @{
ListAccounts = $true
ViewAuditLog = $true
ViewSafeMembers = $true
}
use = @{
UseAccounts = $true
RetrieveAccounts = $true
ListAccounts = $true
ViewAuditLog = $true
ViewSafeMembers = $true
InitiateCPMAccountManagementOperations = $true
}
full = @{
UseAccounts = $true
RetrieveAccounts = $true
ListAccounts = $true
AddAccounts = $true
UpdateAccountContent = $true
UpdateAccountProperties = $true
InitiateCPMAccountManagementOperations = $true
SpecifyNextAccountContent = $true
RenameAccounts = $true
DeleteAccounts = $true
UnlockAccounts = $true
ManageSafe = $true
ManageSafeMembers = $true
BackupSafe = $true
ViewAuditLog = $true
ViewSafeMembers = $true
AccessWithoutConfirmation = $true
}
}
function Get-LabRoleNames {
<#
.SYNOPSIS
The valid role names. Used to validate the state file before any writes.
#>
[CmdletBinding()]
param()
return $script:RoleDefinitions.Keys | Sort-Object
}
function Get-LabRolePermission {
<#
.SYNOPSIS
Returns the permission hashtable for a role, ready to splat into
Add-PASSafeMember or Set-PASSafeMember.
.EXAMPLE
Add-PASSafeMember -SafeName LAB-Linux-Root -MemberName lab-auditors @(Get-LabRolePermission audit)
#>
[CmdletBinding()]
param(
[Parameter(Mandatory)]
[string]$Role
)
if (-not $script:RoleDefinitions.ContainsKey($Role)) {
throw "Unknown role '$Role'. Valid roles: $((Get-LabRoleNames) -join ', ')"
}
# Return a copy so a caller cannot mutate the definition for everyone else.
return $script:RoleDefinitions[$Role].Clone()
}
function Compare-LabRolePermission {
<#
.SYNOPSIS
Compares a live safe member's permissions against what their role should grant.
.DESCRIPTION
Returns objects describing each mismatch. Two kinds matter, and they are
not equally urgent:
Extra - the member holds a permission the role does not grant. This is
privilege creep and it is the finding that matters.
Missing - the role grants it, the member lacks it. Usually a broken
workflow, not a security problem.
.OUTPUTS
PSCustomObject[] with Permission, Expected, Actual, Kind
#>
[CmdletBinding()]
param(
[Parameter(Mandatory)]
[string]$Role,
[Parameter(Mandatory)]
[AllowNull()]
$ActualPermissions
)
$expected = Get-LabRolePermission -Role $Role
$results = [System.Collections.Generic.List[object]]::new()
# psPAS returns permissions as a nested object; normalise to a hashtable of
# name -> bool so the comparison below does not care about shape.
$actual = @{}
if ($null -ne $ActualPermissions) {
foreach ($p in $ActualPermissions.PSObject.Properties) {
if ($p.Value -is [bool]) { $actual[$p.Name] = $p.Value }
}
}
# Every permission the role grants must be present and true.
foreach ($name in $expected.Keys) {
$has = $actual.ContainsKey($name) -and $actual[$name]
if (-not $has) {
$results.Add([pscustomobject]@{
Permission = $name
Expected = $true
Actual = $false
Kind = 'Missing'
})
}
}
# Anything true that the role does not grant is privilege creep.
foreach ($name in $actual.Keys) {
if ($actual[$name] -and -not $expected.ContainsKey($name)) {
$results.Add([pscustomobject]@{
Permission = $name
Expected = $false
Actual = $true
Kind = 'Extra'
})
}
}
return $results
}
Export-ModuleMember -Function Get-LabRoleNames, Get-LabRolePermission, Compare-LabRolePermission