Создание и изменение в Powershell NTFS разрешений ACL


31 января 2020


Как работать с разрешениями NTFS ACL в Powershell

Основной способ ограничения доступа к файлам и папкам дает файловая система NTFS с ее таблицами ACL. Это может быть право только на чтение файла (открытие и просмотр), на чтение и запись (открытие, просмотр, изменение и сохранение) и многие другие. Такие права мы чаще устанавливаем через GUI назначая права не конечному пользователю, а группе в которой он состоит. Все действия по созданию и изменению мы так же можем сделать через Powershell.

На мой взгляд использование консоли Powershell лишнее при выдаче таких прав. Риск совершить ошибку в консоли намного выше, чем при работе в GUI, да и время для написания команды уйдет больше. Есть специфичные задачи, например в виде аудита или перенос прав где Powershell очень поможет.


Основы разрешений

ACL (access controll list) - делится на два вида:

  1. SACL (System Access Control List) - используется для аудита;
  2. DACL (Discretionary Access Control List) - используется для выдачи и проверки разрешений пользователям и группам.

Оба этих типа разрешений хранятся в специальной таблице MFT (Master File Table).

Основное средство для редактирования этих разрешений в GUI можно увидеть зайдя в свойства файла или папки:

В области 4 выделены следующие разрешения:

  • Read - открытие файла и папки;
  • List folder contents - открытие папки;
  • Write - создание файлов и папок и их изменение;
  • Read & Execute - открытие и запуск исполняемых файлов;
  • Modify - открытие, создание, изменение и удаление файлов и папок;
  • Full Control - включает разрешения modify, а так же управление разрешениями файла или папки.

Чаще всего мы работаем с разрешениями выше, но есть еще один список с возможностью настройки прав более тонко:

Как можно догадаться - разрешения указанные в области 1 это просто набор нескольких правил из области 3. Их так же еще называют "Premission Sets" и "Special Premissions".

Групповые разрешения могут принимать флаги Allow и Deny, которые разрешат или запретят указанные действия. Указывать разрешения для пользователей через Deny считается плохой практикой и практически не используется.

Кроме этого существует наследование:

Наследование помогает установить разрешения для одной папки так, что оно будет применяться ко вложенным файлам и папкам. Если наследование отключить (2), то у нас будет возможность убрать все наследуемые разрешения или оставить их.

 

 

Получение текущих разрешений в Powershell

На примере ниже я верну разрешения для папки "C:\TestFolder\"

Get-ACL -Path "C:\TestFolder\" | fl

В области 1 выделен владелец папки, а под областью 2 отображаются все группы и пользователи с правами.

Мы можем проверять права не только локальной, но и сетевой папки. На примере ниже возвращена та же папка:

Get-ACL -Path "\\localhost\C$\TestFolder\" | fl

Поиск всех папок с правами у определенной группы

Представим, что мы хотим проверить права данные определенной группе. Мы можем заходить в свойства каждой папки и смотреть вкладку "Безопасности", а можем сделать это через Powershell.

Обычно у нас есть какой-то каталог с общим доступом с папками внутри, на которые мы выдаем разрешения. В моем случае такой каталог "Moscow", а на папки внутри я уже даю права:

В следующем примере я узнаю на какие папки установлены разрешения для TestGroup:

# Получаем все директории внутри это папки
$path = Get-ChildItem -Path 'C:\Moscow\'
# Получаем все выданные разрешения
$acl = $path | Get-ACL
# Группа, которую мы ищем
$group = 'Domain\TestGroup'
# Ищем группу в ACL
foreach ($folder in $acl){
    if ($folder.Access.IdentityReference -eq $group){
            Write-Output  "Путь папки: " $folder.Path
            # Получаем только разрешения
            $access = ($folder.Access | where IdentityReference -eq $group).FileSystemRights
            Write-Output  "Разрешения выданные группе: " $access
            }
}

 

Изменение, копирование и добавление разрешений

Еще одна причина использовать Powershell - это возможность копирования разрешений. Например так я поставлю разрешения для папки Folder2 такие же, как и Folder2:

# Получаем действующие разрешения у папки Folder1
$acl = Get-ACL -Path "C:\Folder1"
# Устанавливаем эти же разрешения для папки Folder2
$acl | Set-Acl -Path "C:\Folder2"

Возможности добавить нового пользователя в список ACL или изменить его права одним командлетом у нас нет. В обоих случаях мы должны будет создавать копию объекта ACL, изменять ее отдельным классом, а затем применять используя метод. Сам объект, который мы получаем через Get-ACL, имеет множество методов:

Get-ACL -Path "C:\Folder1" | Get-Member -MemberType Method

Для создания нового объекта с правами используется класс "FileSystemAccessRule", который в команде будет выглядеть так:

New-Object Security.AccessControl.FileSystemAccessRule('IdentityReference\String',
        'FileSystemRights', 'InheritanceFlags, PropagationFlags', 'AccessControlType')

Расшифровать значения можно следующим образом:

  • IdentityReference\String - пользователь или группа формата "DOMAIN\Administrator";
  • FileSystemRights - сами разрешения, например "Read";
  • InheritanceFlags и PropagationFlags - определяют наследование. Например вы можете сделать так, что папки внутри указанной будут наследовать разрешения, а файлы нет.  Ниже будут приведены несколько примеров. Более подробно об этом можно почитать на сайте Microsoft;
  • AccessCpmtrolType - разрешить или запретить (Allow/Deny). 

Изменение и добавление прав у пользователя и групп

Допустим у нас есть пользователь "Test User (001)" с возможностью чтения папки "Folder1" и мы хотим добавить еще права на запись. Это будет выглядеть так:

# Делаем копию существующих прав на папку
$old_acl = Get-ACL -Path "C:\Folder1"
# Создаем права, которые хотим добавить к папке
$new_acl = New-Object System.Security.AccessControl.FileSystemAccessRule('DOMAIN\Test User (1)', 'Write', 'ContainerInherit, ObjectInherit', 'None', 'Allow')
# Изменяем скопированные разрешения
$old_acl.AddAccessRule($new_acl)
# Устанавливаем все разрешения к папке
Set-ACL -Path "C:\Folder1" -ACLObject $old_acl

Наследование типа 'ContainerInherit, ObjectInherit' говорит о том, что оно касается этой папки и всех вложенных папок и файлов.

Таким же образом работает добавление новой группы или пользователя в список ACL:

# Делаем копию существующих прав на папку
$old_acl = Get-ACL -Path "C:\Folder1"
# Права и новый пользователь
$new_acl = New-Object System.Security.AccessControl.FileSystemAccessRule('DOMAIN\Test 2', 'Write,Read', 'ContainerInherit, ObjectInherit', 'None', 'Allow')
# Изменяем скопированные разрешения
$old_acl.AddAccessRule($new_acl)
# Устанавливаем все разрешения к папке
Set-ACL -Path "C:\Folder1" -ACLObject $old_acl

Права, которые мы можем дать имеют сокращенное название и они отображены далее:

У вас может быть много ошибок связанных с созданием класса с новыми разрешениями. Я бы советовал выводить существующие права и сравнивал бы их с новыми - это позволит снизить вероятность ошибок. 

При этом вы можете применить набор разрешений, например в виде Write, но результат будет отображаться в виде "Special Premission":

Если бы я указал наследование в виде 'ContainerInherit, ObjectInherit', то права бы применились как нужно:

Поэтому я рекомендую смотреть существующие разрешения на примере того, как я сделал это выше.

Удаление прав у пользователя или группы

Для удаления всех разрешений есть метод "RemoveAccessRuleAll". Работает он так же:

# Делаем копию существующих прав на папку
$old_acl = Get-ACL -Path "C:\Folder1"
# Права и новый пользователь
$new_acl = New-Object System.Security.AccessControl.FileSystemAccessRule('DOMAIN\Test User (1)', 'Write,Read', 'ContainerInherit, ObjectInherit', 'None', 'Allow')
# Удаляем все разрешения
$old_acl.RemoveAccessRuleAll($new_acl)
# Устанавливаем все разрешения к папке
Set-ACL -Path "C:\Folder1" -ACLObject $old_acl

Для удаления конкретного права, например только возможность чтения, есть метод "RemoveAccessRule". С этим методом у меня были проблемы после которых действующие разрешения менялись на Special и изменить эту ситуацию у меня не получилось. Если вам нужно все же убрать одно разрешение - я бы советовал удалять все разрешения у пользователя, а затем добавлять их заново. У пользователя было право на Чтение и Запись, но мне нужно было оставить только чтение:

# Делаем копию существующих прав на папку
$old_acl = Get-ACL -Path "C:\Folder1"
# Указываем пользователя для удаления
$new_acl = New-Object System.Security.AccessControl.FileSystemAccessRule('DOMAIN\Test User (1)', 'Read', 'ContainerInherit, ObjectInherit', 'None', 'Allow')
# Удаляем все разрешения данного пользователя
$old_acl.RemoveAccessRuleAll($new_acl)
# Применяем правильные разрешения
$old_acl.AddAccessRule($new_acl)
# Записываем все разрешения
Set-ACL -Path "C:\Folder1" -ACLObject $old_acl

Смена владельца

Смена владельца файла или папки делается через метод SetOwner. Этот метод, в качестве идентификатора, принимает SID пользователя и что бы его узнать нужно использовать класс "System.Security.Principal.Ntaccount". На практике это выглядит так:

# Получаем старый список ACL
$old_acl = Get-ACL -Path "C:\Folder2"
# Получаем SID пользователя
$user_sid = New-Object System.Security.Principal.Ntaccount("DOMAIN\Test 3")
# Устанавливаем нового владельца
$old_acl.SetOwner($user_sid)
# Записываем
Set-Acl -Path "C:\Folder2" -ACLObject $old_acl

 

Включение или отключение наследования папок и файлов

Для управления наследованием используется метод "SetAccessRuleProtection", который устанавливает следующее:

  1. Нужно ли блокирование от родительской папки.
  2. Нужна ли перезапись прав.

Значения указываются в $True или $False. Например так я включу наследование и заменю существующие разрешения родительскими:

# Получаем старый список ACL
$acl = Get-Acl -Path "C:\Folder1\OneMoreFolder"
# Устанавливаю наследование и перезапись прав
$acl.SetAccessRuleProtection($false,$true)
# Записываю
Set-Acl -Path "C:\Folder1\OneMoreFolder" -ACLObject $acl

 

Сбор всех прав и их экспорт в CSV

Учитывая примеры выше мы можем временно собирать отчеты по выданным правам. Пример того, как может выглядеть отчет:

Такой отчет сформирует следующий скрипт:

# Указываем путь к директории, где нужно собрать все права
$folder_path = dir -Directory -Path "C:\WorkFolder" -Recurse -Force
# Переменная, которая будет хранить данные из цикла
$Report = @()
Foreach ($folder in $folder_path) {
    # Получаем права на текущий каталог
    $Acl = Get-Acl -Path $Folder.FullName
    foreach ($Access in $acl.Access)
        {
            # Формируем массив PSCustomObject из каждого права
            $Properties = [ordered]@{
                                 'Папка'=$Folder.FullName;`
                                 'Пользователь или группа'=$Access.IdentityReference;`
                                 'Права'=$Access.FileSystemRights;`
                                 'Наследование'=$Access.IsInherited}
            # Записываем в переменную
            $Report += New-Object -TypeName PSObject -Property $Properties
        }
}
# Экспортируем в CSV
$Report | Export-Csv -path "C:\Permissions.csv" -NoTypeInformation -Encoding Unicode
...

Теги: #powershell


Популярные тэги
О блоге
Этот блог представляет собой конспекты выученного материала, преобретенного опыта и лучшие практики в системном администрировании и программировании.