Как и ожидалось - негативные. Я не знаю почему, но я настолько привык к версии 1.0, что V2 уже стал отвергать на подсознательном уровне, отрицая все потенциальные преимущества V2. Да, в 1.0 много чего не было сделано, приходилось конструировать собственные костыли, чтобы получить нужный результат, но 1.0 казался как-то роднее и приятней. Я тянул до последнего и вчера совершил героическое обновление PowerShell 1.0 до V2 CTP3. При этом следовал чётким инструкциям ReleaseNotes - корректно деинсталлировал версию 1.0 и потом установил V2 CTP3. Баг был обнаружен мною уже при втором запуске консоли или через 1 минуту после установки.

Суть проблемы: на своём ноутбуке с Windows Vista SP1 я использую Software Restriction Policies и действие по умолчанию Disallowed. Для необходимых путей добавлены исключения с действием Unrestricted и из стандартного набора удалёно расширение .chm и всё. А к чему я это всё? Вот мой второй запуск консоли PowerShell V2 CTP3 (после возвращения политики SRP в исходное состояние):

Windows PowerShell V2 (Community Technology Preview - Features Subject to Change)
Copyright (C) 2008 Microsoft Corporation. All rights reserved.

File D:\Users\vpodans\Documents\WindowsPowerShell\Microsoft.PowerShell_profile.ps1 cannot be loaded because its executi
on is blocked by software restriction policies. For more information, contact your system administrator.
At line:1 char:2
+ . <<<<  'D:\Users\vpodans\Documents\WindowsPowerShell\Microsoft.PowerShell_profile.ps1'
    + CategoryInfo          : NotSpecified: (:) [], PSSecurityException
    + FullyQualifiedErrorId : RuntimeException

PS C:\Users\vPodans> Get-ExecutionPolicy
RemoteSigned
PS C:\Users\vPodans>

С dot sourced скриптами дела обстояли так же:

PS C:\Users\vPodans> "Get-Date" | Set-Content -Path .\date.ps1
PS C:\Users\vPodans> Get-Content .\date.ps1
Get-Date
PS C:\Users\vPodans> . .\date.ps1
File C:\Users\vPodans\date.ps1 cannot be loaded because its execution is blocked by software restriction policies. For
more information, contact your system administrator.
At line:1 char:2
+ . <<<<  .\date.ps1
    + CategoryInfo          : NotSpecified: (:) [], PSSecurityException
    + FullyQualifiedErrorId : RuntimeException

PS C:\Users\vPodans>

Любые попытки исполнения файлов скриптов завершились ничем. Первым делом я проверил настройки политик SRP и убедился, что расширения PS1 нету. Причём все файлы скриптов замечательно открываются по двойному клику в PowerGUI и в системном журнале Application никаких записей об этом не зарегистрировано. Далее я попробовал включить расширенное логирование работы Software Restriction Policies:

reg.exe add "HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft\Windows\Safer\CodeIdentifiers" /v LogFileName /d saferlog.txt

и попробовал снова запустить консоль PowerShell. В итоге я получил файл примерно такого содержания:

svchost.exe (PID = 1152) identified C:\Windows\system32\consent.exe as Unrestricted using path rule, Guid = {191cd7fa-f240-4a17-8986-94d480a6c8ca}
svchost.exe (PID = 856) identified C:\Windows\system32\DllHost.exe as Unrestricted using path rule, Guid = {191cd7fa-f240-4a17-8986-94d480a6c8ca}
svchost.exe (PID = 856) identified C:\Windows\system32\DllHost.exe as Unrestricted using path rule, Guid = {191cd7fa-f240-4a17-8986-94d480a6c8ca}
svchost.exe (PID = 1152) identified C:\WINDOWS\system32\WindowsPowerShell\v1.0\powershell.exe as Unrestricted using path rule, Guid = {191cd7fa-f240-4a17-8986-94d480a6c8ca}
powershell.exe (PID = 2688) identified D:\Users\Administrator\WindowsPowerShell\Microsoft.PowerShell_profile.ps1 as Disallowed using default rule, Guid = {11015445-d282-4f86-96a2-9e485f593302}

Первой строчкой отображена попытка запуска запуска приложения с повышенными привилегиями. Далее (самая последняя строчка) видно, что файл профиля (Microsoft.PowerShell_profile.ps1) был действительно блокирован политикой. Но на каком основании блокирован и почему нету соответствующей записи в журнале Application мне до сих пор неизвестно. Я так же произвёл аудит доступа к файлу профиля, но там ничего подозрительного не увидел. Вобщем вот такие дела, господа.

Решение: в качестве временного решения в политику SRP было добавлено 2 исключения:

  • %HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders\Personal%WindowsPowerShell/*.ps1
  • %HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders\Personal%WindowsPowerShell/PowerTab/*.ps1

Для этих двух правил уровень нужно выставить Unrestricted. Ради интереса я попробовал выставить уровень Basic User и что интересно - PS1 файлы так же блокируются, как и при отсутствии этих дополнительных исключений!

Я не знаю, что такого там натворили разработчики PowerShell в CTP3 (я не знаю, была ли такая проблема в более ранних CTP версиях или нет), но мне кажется тут попахивает не просто какой-то ошибкой в коде, а нечто более глобальным, что загрузка PS1 файлов каким-то образом ловится и отшивается политиками Software Restriction Policies. Заодно можно посетовать на практически отсутствующие инструменты отслеживания работы политик SRP :'( (расширенное логирование не сильно помогает в этом деле). Если у кого-то есть возможность повторить ситуацию (работа PowerShell V2 CTP3 с работающей политикой SRP), то отпишитесь в комментах о результатах. Если данный баг подтвердится, то не забудьте подтвердить его здесь:

https://connect.microsoft.com/feedback/ViewFeedback.aspx?FeedbackID=389878&SiteID=99

Monday, December 29, 2008 10:38:01 PM (FLE Standard Time, UTC+02:00)   Comments [4]    

 

Сегодня наконец-то дошли руки до завершения странички с ссылками на актуальные и полезные ресурсы по PowerShell. Я не ставил задачу собрать все имеющиеся ссылки по PowerShell, а только наиболее актуальные, которые будут полезны как новичкам, так и не очень :) . В список ресурсов вошли книги, которые я уже просмотрел (некоторые и прочитал) и рекомендую для тех, кто хочет самостоятельно изучать PowerShell. Так же выложил ссылки на страницы загрузки PowerShell и полезные примочки для него и список ресурсов, где можно задать вопрос по PowerShell и где высока вероятность получить ответ на свой вопрос. Вобщем, это всё находится здесь:

>> Библиотека <<

Данная библиотека будет обновляться лишь по мере появлении чего-то ценного в мире PowerShell. И не забывайте про блоги специалистов по PowerShell в моём BlogRoll.

Следующим этапом думаю заняться инвентаризацией своих наработок и создать страницу с концептуальными скриптами (их хоть и совсем немного, но всё же), которые были мной написаны и могут быть полезны другим людям для автоматизации своих задач.

UPD 6.06.2009:

  • Добавлена книга Эффективное программирование в Windows PowerShell
  • Добавлена книга Administrative tasks using Windows PowerShell

UPD 18.07.2009

  • Добавлена книга Mastering PowerShell
Sunday, December 28, 2008 11:48:42 PM (FLE Standard Time, UTC+02:00)   Comments [3]    

 

В PowerShell есть замечательная возможность нативно оперировать с этими префиксами. Достаточно написать размер в килобайтах или мегабайтах, PowerShell автоматически переведёт его в байты:

[vPodans] 5kb
5120
[vPodans] 10mb
10485760
[vPodans] 40gb
42949672960
[vPodans]

Видите, все значения сами переводятся в байты. Особо не перестарайтесь, потому что TB не является константой и автоматически не преобразовывается. Но когда мы собираем информацию о системе, например, объём физической памяти или жёстких дисков средствами WMI, то данные обычно указываются в байтах и приходится вручную их как-то переводить в килобайты, мегабайты или гигабайты. Штатного функционала для решения данной задачи нету, поэтому приходится использовать примерно такой ход:

(2048 / 1kb).tostring("F00")

и в результате мы получим число 2. Мы 2048 байт разделили на килобайты, чтобы получить значение в килобайтах (если делим на 1MB, то значение получим в мегабайтах соответственно) с округлением до целого числа. Для округления до определённого знака после запятой, то в скобках нужно указать точность, например:

[vPodans] (20000 / 1kb).tostring("F00")
20
[vPodans] (20000 / 1kb).tostring("F01")
19,5
[vPodans] (20000 / 1kb).tostring("F02")
19,53
[vPodans] (20000 / 1kb).tostring("F03")
19,531
[vPodans]

используя эти данные можно написать простенькую функцию, которая будет делать обратное преобразование. Причём она это будет делать автоматически, т.е. сама выбирать в чём отображать значение, в байтах, килобайтах, мегабайтах или гигабайтах. Критерии выбора префикса очень простые - числа от 1 до 1000. Если число выше, то увеличиваем префикс до тех пор, пока не получим число от 1 до 1000. Например, 10485760 делим на 1kb и получаем 10240 килобайт. Такое представление не очень понятное. Поэтому мы ещё раз разделим на 1kb и получим цифру 10, но префикс уже будет - мегабайты. Вот такую функцию предложил Rob Campbell на ньюсгруппах (я её немного подрихтовал, чтобы выглядела более элегантно):

function to_kmg ($bytes, [int]$precision = 0) {
foreach ($i in ("Bytes","KB","MB","GB","TB")) {
    if (($bytes -lt 1024) -or ($i -eq "TB")){
        $bytes = ($bytes).tostring("F0" + "$precision")
        return $bytes + " $i"
        } else {
            $bytes /= 1KB
        }
    }
}

функция просто по очереди перебирает префиксы и проверяет, чтобы число было в пределах от 0 до 1024 (для двоичной системы мне кажется, что лучше использовать именно 1024, а не 1000). Если нет (а оно значит больше, чем 1024), то добавляем следующий префикс и уменьшаем число снова на 1024. Вот так просто это делается. Попробуем проверить функцию на примерах, которые были показаны в самом начале поста:

[vPodans] function to_kmg ($bytes, [int]$precision = 0) {
>> foreach ($i in ("Bytes","KB","MB","GB","TB")) {
>>     if (($bytes -lt 1000) -or ($i -eq "TB")){
>>         $bytes = ($bytes).tostring("F0" + "$precision")
>>         return $bytes + " $i"
>>         } else {
>>             $bytes /= 1KB
>>         }
>>     }
>> }
>>
[vPodans] to_kmg 5120
5 KB
[vPodans] to_kmg 10485760
10 MB
[vPodans] to_kmg 42949672960
40 GB
[vPodans]

Второй аргумент ($precision) позволяет указывать точность значения в знаках после запятой. Вот почти и всё. Однако, есть одно "но". WMI некоторые размеры отдаёт не в байтах (как для дисков), а в килобайтах (как для физической памяти). Поэтому для работы этой функции аргумент $bytes должен быть либо точного размера в байтах, либо с явным указанием префикса (например, 100kb).

Кстати говоря, тут будет полезным почитать по двоичным и десятичным префиксом. Порядок применения префиксов описан в документе IEC-60027-2:

По этим ссылкам достаточно понятно объясняется что такое десятичный префикс и двоичный, а так же и проблемы их использования в компьютерной технике.

Wednesday, December 24, 2008 12:00:36 AM (FLE Standard Time, UTC+02:00)   Comments [2]    

 

По мотивам темы на форуме - http://forums.microsoft.com/TechNet-RU/ShowPost.aspx?PostID=4255895&SiteID=40. PowerShell без проблем может управлять восстановлением системы - SystemRestore средствами WMI. За это отвечает классы

Примечание: SystemRestore доступно только на клиентских версиях - Windows XP/Windows Vista. В серверных редакицях Windows Server нереализовано никак.

вот так выглядит GUI окно системы восстановления в Windows Vista:

systemrestore

Следует так же учесть, что эти классы не находятся в пространстве имён по умолчанию Root\Cimv2, а в Root\Default. Для просмотра всех точек восстановления нужно просто получить объект данного класса (если вы работаете под управлением Windows Vista, то потребуется запустить консоль с повышенными привилегиями!). При получении объекта класса не забудьте указать правильный путь размещения класса в пространстве имён:

[System32] gwmi -Namespace "root\default" -class systemrestore __GENUS : 2 __CLASS : SystemRestore __SUPERCLASS : __DYNASTY : SystemRestore __RELPATH : SystemRestore.SequenceNumber=473 __PROPERTY_COUNT : 5 __DERIVATION : {} __SERVER : THOR __NAMESPACE : root\default __PATH : \\THOR\root\default:SystemRestore.SequenceNumber=473 CreationTime : 20081220184403.157460-000 Description : Scheduled Checkpoint EventType : 100 RestorePointType : 7 SequenceNumber : 473 ......

у меня этих точек восстановления несколько, поэтому я показал только первую точку. Здесь нам будут интересны следующие свойства - Description (название точки восстановления), EventType (тип точки восстановления) и SequenceNumber (порядковый номер точки восстановления). Эти значения нам пригодятся для создания новых точек восстановления и отката системы до определённой точки. Если посмотреть методы класса, то получим:

[System32] gwmi -Namespace "root\default" -class systemrestore | gm


   TypeName: System.Management.ManagementObject#root\default\SystemRestore

Name                MemberType   Definition
----                ----------   ----------
CreationTime        Property     System.String CreationTime {get;set;}
Description         Property     System.String Description {get;set;}
EventType           Property     System.UInt32 EventType {get;set;}
RestorePointType    Property     System.UInt32 RestorePointType {get;set
SequenceNumber      Property     System.UInt32 SequenceNumber {get;set;}
__CLASS             Property     System.String __CLASS {get;set;}
__DERIVATION        Property     System.String[] __DERIVATION {get;set;}
__DYNASTY           Property     System.String __DYNASTY {get;set;}
__GENUS             Property     System.Int32 __GENUS {get;set;}
__NAMESPACE         Property     System.String __NAMESPACE {get;set;}
__PATH              Property     System.String __PATH {get;set;}
__PROPERTY_COUNT    Property     System.Int32 __PROPERTY_COUNT {get;set;
__RELPATH           Property     System.String __RELPATH {get;set;}
__SERVER            Property     System.String __SERVER {get;set;}
__SUPERCLASS        Property     System.String __SUPERCLASS {get;set;}
ConvertFromDateTime ScriptMethod System.Object ConvertFromDateTime();
ConvertToDateTime   ScriptMethod System.Object ConvertToDateTime();
Delete              ScriptMethod System.Object Delete();
GetType             ScriptMethod System.Object GetType();
Put                 ScriptMethod System.Object Put();


[System32]

Мы здесь не видим никаких методов, которые бы позволяли создавать и откатываться. Получив объект мы можем его только удалить. Сами методы хранятся в самом классе:

[System32] [wmiclass]'\\.\root\default:systemrestore' | gm -MemberType method


   TypeName: System.Management.ManagementClass#ROOT\default\SystemRestore

Name                 MemberType Definition
----                 ---------- ----------
CreateRestorePoint   Method     System.Management.ManagementBaseObject CreateRestorePoint(System.String Description,...
Disable              Method     System.Management.ManagementBaseObject Disable(System.String Drive)
Enable               Method     System.Management.ManagementBaseObject Enable(System.String Drive, System.Boolean Wa...
GetLastRestoreStatus Method     System.Management.ManagementBaseObject GetLastRestoreStatus()
Restore              Method     System.Management.ManagementBaseObject Restore(System.UInt32 SequenceNumber)


[System32]

вот тут и хранятся наши методы. Методы Enable и Disable позволяют глобально включать режим восстановления системы или отключать для определённого диска. Чтобы включить мониторинг для всей системы в целом или для конкретного диска нужно выполнить:

# объявляем класс:
$sysrestore = [wmiclass]'\\.\root\default:systemrestore'
# выбираем одно из нужных действий. Обратите внимание, что для изменения состояния
#
мониторинга всей системы скобки не должны быть пустые, а содержать пустые двойные кавычки
$sysrestore.Enable("")
$sysrestore.Enable("c:\")
$sysrestore.Disable("")
$sysrestore.Disable("c:\")

Для создания новой точки воспользуемся методом CreateRestorePoint:

$sysrestore = [wmiclass]"\\.\root\default:systemrestore"
$sysrestore.CreateRestorePoint("MyRestorePoint", 0, 100)

где MyRestorePoint - название точки восстановления, 0 - тип точки восстановления (возможные значения можно посмотреть по ссылке на MSDN) и 100 - тип события. Давайте создадим новую точку восстановления:

[System32] $sysrestore = [wmiclass]"\\.\root\default:systemrestore"
[System32] $sysrestore.CreateRestorePoint("MyRestorePoint", 0, 100)


__GENUS          : 2
__CLASS          : __PARAMETERS
__SUPERCLASS     :
__DYNASTY        : __PARAMETERS
__RELPATH        :
__PROPERTY_COUNT : 1
__DERIVATION     : {}
__SERVER         :
__NAMESPACE      :
__PATH           :
ReturnValue      : 0



[System32]

Значение RetrunValue = 0 говорит, что точка восстановления создана. Убедимся в этом:

[System32] gwmi -Namespace "root\default" -class systemrestore | select -Last 1


__GENUS          : 2
__CLASS          : SystemRestore
__SUPERCLASS     :
__DYNASTY        : SystemRestore
__RELPATH        : SystemRestore.SequenceNumber=475
__PROPERTY_COUNT : 5
__DERIVATION     : {}
__SERVER         : THOR
__NAMESPACE      : root\default
__PATH           : \\THOR\root\default:SystemRestore.SequenceNumber=475
CreationTime     : 20081221183404.006675-000
Description      : MyRestorePoint
EventType        : 100
RestorePointType : 0
SequenceNumber   : 475



[System32]

Вот теперь мы видим нашу точку восстановления с названием MyRestorePoint и её SequenceNumber стал на 1 больше. Чтобы откатиться на эту точку восстановления нужно выполнить метод Restore с указанием SequenceNumber:

$sysrestore = [wmiclass]"\\.\root\default:systemrestore"
$sysrestore.Restore("475")
(
gwmi win32_operatingsystem).reboot()

Для успешного завершения восстановления точки восстановления потребуется перезагрузка системы, которую мы проводим последней строчкой. Вот так просто можно включать, отключать создавать точки восстановления и откатывать систему до их состояния. Чтобы управлять дополнительными параметрами восстановления системы, как выделение места для них, время жизни и т.д. воспользуемся соседним классом:

[System32] gwmi -Namespace "root\default" -class systemrestoreconfig


__GENUS           : 2
__CLASS           : SystemRestoreConfig
__SUPERCLASS      :
__DYNASTY         : SystemRestoreConfig
__RELPATH         : SystemRestoreConfig.MyKey="SR"
__PROPERTY_COUNT  : 5
__DERIVATION      : {}
__SERVER          : THOR
__NAMESPACE       : ROOT\default
__PATH            : \\THOR\ROOT\default:SystemRestoreConfig.MyKey="SR"
DiskPercent       : 15
MyKey             : SR
RPGlobalInterval  : 86400
RPLifeInterval    : 4294967295
RPSessionInterval : 1



[System32]

тут всё просто. DiskPercent показывает процент дискового пространства, которое выделяется под восстановление системы. RPGlobalInterval показывает периодичность создания точек восстановления (а для Windows Vista создание теневых копий) в секундах. RPLifeInterval - время жизни точек восстановления (и теневых копий для Windows Vista) в секундах (по умолчанию 90 дней). Эти параметры можно изменять простым переприсвоением и применением новых значений скриптметодом Put(). К сожалению, провайдер SystemRestore не позволяет удалять точки восстановления (вообще это можно с использованием функции SRRemoveRestorePoint, но это уже совсем другая история программирования).

Вот теперь мы готовы написать скрипт, который позволит в однострочном режиме управлять восстановлением системы.

########################################################
# SystemRestore.ps1
# Version 1.0
#
# Functions for System Restore management
# Note: available for Windows XP and Windows Vista only!
#
# Vadims Podans (c) 2008
# http://www.sysadmins.lv/
########################################################

# функция для преобразования кода возврата в текстовое значение
function _SysRestore_Get-Code ($Action) {
switch ($Action.ReturnValue) {
   "0" {"Success"}
   default {"Unknown error $Action.ReturnValue"}
   }
}

# простая функция получения списка всех доступных точек восстановления в табличном виде
function Get-SystemRestore {
gwmi -Namespace "root\default" -class systemrestore | Select Description, @{n="Date/Time";
e={([System.Management.ManagementDateTimeconverter]::ToDateTime($_.CreationTime)).tostring()}},
@{n="Point number"; e={$_.SequenceNumber}} | ft -AutoSize
}

# функция включения или отключения System Restore для указанного диска или, если диск не указан
# то действие будет принято для всей системы в целом. А так же позволяет откатывать систему
# в более раннее состояние.
function Set-SystemRestoreStatus ($Status, $arg) {
# собственно, само действие включения или отключения System Restore
switch ($status) {
    "Enable" {$action = ([wmiclass]'\\.\root\default:systemrestore').Enable("$arg")}
    "Disable" {$action = ([wmiclass]'\\.\root\default:systemrestore').Disable("$arg")}
# процесс отката системы в более раннюю точку восстановления
    "Restore" {$RP = gwmi -Namespace "root\default" -class systemrestore | ?{$_.SequenceNumber -eq [int]$arg}
# проверяем, что указанная точка восстановления существует.
    if ($RP) {
# если существует, то проводим процесс восстановления в указанную точку
        $action = ([wmiclass]'\\.\root\default:systemrestore').Restore("$arg")
        Write-Host "Performing system restore to earlier status ..."
# получаем код возврата операции
        _SysRestore_Get-Code $action
# если восстановление системы прошло успешно, то принудительно перезагружаем систему 
        if ($(_SysRestore_Get-Code $action) -eq "Success") {
            Write-Warning "To complete System Restore system will reboot in 15 sec"
            shutdown.exe -r -t 15 -f
            }
        }else {
        Write-Host "Specified restore point does not found!"
        }
    }
    "Create" {$action = ([wmiclass]'\\.\root\default:systemrestore').CreateRestorePoint("$arg", 0, 100)}
}
# здесь происходит только генерация сообщения о ходе выполнения операций
if ($arg -and ("Enable", "Disable" -contains $status)) {
    Write-Host "Setting SystemRestore status to $status on" $arg
    _SysRestore_Get-Code $action
    } else {
    Write-Host "Setting SystemRestore status to $status on all drives: "
    _SysRestore_Get-Code $action
    }
}

# функция для изменения настроек System Restore
function Set-SystemRestoreSetting ($setting, $value) {
# получаем текущие настройки
$SRSetting = gwmi -Namespace "root\default" -class systemrestoreconfig
# проверяем выбранный параметр, который будет изменяться
switch ($setting) {
    "DiskSpace" {
# т.к. дисковое место указывается в процентах, то проверяем, что указано число от 1% до 99%
        if ($value -lt 99 -and $value -gt 1) {
            $SRSetting.DiskPercent = $value
        } else {
        Write-Warning "Disk space percentage must be integer and vlaue must be between 1-99"}
    }
# установка глубины точек восстановлений. Указывается в днях. По истечении этого времени
# точки восстановления удаляются, высвобождая место для более новых точек восстановления
    "HistoryDepth" {
        $SRSetting.RPLifeInterval = [int]$value * 86400
    }
# частота автоматического создания точек восстановлений
    "Frequency" {
# извлекаем из заданного параметра периодичности последние 4 символа, которые образуют префикс
# в днях или часах и выставляем множитель для получения секунд. Если это часы, то множитель будет
# 3600, а если в днях, то множитель будет 86400 секунд
        $time = $value.substring($value.length - 4, 4)
        switch ($time) {
            "hour" {$sec = 3600}
            "days" {$sec = 86400}
        }
# извлекаем из заданного параметра периодичности все символы, чтобы получить целочисленное значение
# кроме последних 4, которые являются текстовыми и обозначают лишь префикс
        $SRSetting.RPGlobalInterval = $sec * $value.substring(0, $value.length - 4)
        }
    default {Write-Warning "Parameter must be DiskSpace or HistoryDepth or Frequency"}
    }
# применение новых параметров
$SRSetting.Put()
}

Синтаксис функций может быть следующий:

  • Get-SystemRestore - показывает в табличном виде все существующие точки восстановления. Аргументов не принимает.
  • Set-SystemRestoreStatus Enable c:\ - включает режим SystemRestore.
    где c:\ - диск, для которого включается мониторинг SystemRestore. Параметр опциональный. Если не указан, то включается для всех дисков.
  • Set-SystemRestoreStatus Disable c:\ - отключает режим systemRestore.
    где c:\ - диск, для которого выключается мониторинг SystemRestore. Параметр опциональный. Если не указан, то выключается для всех дисков.
  • Set-SystemRestoreStatus Restore 5 - производит откат системы до указанной точки восстановления. После отката произойдёт принудительная перезагрузка системы.
    где 5 - номер точки восстановления. Номер можно получить выполнив функцию Get-SystemRestore.
  • Set-SystemRestoreStatus Create "MyNewRestorePoint" - создаёт новую точку восстановления
    где MyNewRestorePoint - название точки восстановления.
  • Set-SystemRestoreSetting DiskSpace 10 - задаёт резервируемое место для точек восстановления в процентах от ёмкости диска.
    где 10 - процент диска, которое отводится под SystemRestore. Может иметь значение от 1 до 99
  • Set-SystemRestoreSetting HistoryDepth 10 - задаёт время хранения точек восстановления
    где 10 - количество дней, в течении которого будут храниться точки восстановления. Более старые будут автоматически удаляться.
  • Set-SystemRestoreSetting Frequence 5days - задаёт частоту автоматического создания точек восстановления.
    где 5days - периодичность создания точек восстановления в днях. Можно указывать и в часах, например, 10hour (будет делать автоматические точки восстановления каждые 10 часов). В общем смысле сначала идёт число и потом без пробелов суффикс days или hour.
Monday, December 22, 2008 3:40:46 PM (FLE Standard Time, UTC+02:00)   Comments [0]    

 

В предыдущих частях мы разговаривали о применении смарт-карт для хранения и использования ключей шифрования EFS в Windows Server 2008. Мы так же рассмотрели вопросы реализации агентов восстановления и самого процесса восстановления. Предыдущие 3 части:

И у нас остался только одна категория вопросов: что будет, если сертификат просрочен или будет отозван. В принципе, разницы в поведении между просроченными и отозванными сертификатами не будет, а именно:

  • пользователи не могут шифровать новые файлы отозванными или просроченными сертификатами, но сохраняется возможность расшифровывать ранее зашифрованные файлы;
  • агенты восстановления EFS файлов теряют возможность дешифрования файлов, которые были зашифрованы после истечения срока или отзыва сертификата агента восстановления. В этом случае когда пользователи шифруют файлы, то они заполняют только DDF, но не DRF, который остаётся пустым.
  • агенты восстановления Key Archival так же утрачивают возможность восстановления закрытых ключей пользователей из базы CA, которые были сгенерированы после истечения срока Key Archival сертификата. В этом случае закрытые ключи пользователей не шифруются просроченными сертификатами агентов Key Archival. В результате извлечение BLOB файла из базы CA будет невозможно. Однако, эти агенты могут дешифровывать ключи пользователей, которые запрашивали сертификаты в период валидности сертификатов агентов Key Archival.

В общем смысле понятно, что расшифровывать файлы/ключи можно с просроченными/отозванными сертификатами, но проводить новое шифрование уже невозможно.

Вот теперь, я так полагаю, мы в достаточной мере рассмотрели вопрос использования смарт-карт для EFS. В качестве эпилога скажу, что рекомендуется использовать комбинированные шаблоны для агентов восстановления. Например, если агент восстановления будет хранить свои закрытые ключи от EFS и Key Archival на смарт-карте, то выгодней будет использовать шаблон Smart Card Logon и в его EKU добавить уже необходимые области применения (например, Key Archival), что упрощает процедуру эксплуатации таких сертификатов в будущем. Не нужно будет теперь заботиться, что отдельные сертификаты будут истекать в разное время и при компрометации смарт-карты придётся отзывать несколько сертификатов - достаточно будет отозвать один сертификат. То же самое касается и пользователей, которые используют смарт-карты. Достаточно в Smart Card Logon EKU добавить Basic EFS и всё будет хорошо. Почти хорошо, если у вас есть Enterprise или Datacenter редакция Windows Server :)

Вопросы обновления сертификатов и прочие lifecycle эксплуатации (например, введение Certificate Autoenrollment и прочее) смарт-карт и EFS уже выходят за рамки моей статьи, поэтому вам придётся этот материал изучать самостоятельно. И на последок я подобрал несколько полезных ссылок, которые помогут с освоением данного материала:

Sunday, December 21, 2008 7:13:10 PM (FLE Standard Time, UTC+02:00)   Comments [3]    

 

В предыдущих частях мы рассмотрели принципы работы EFS, изучили основные вопросы безопасности и доступности ключей, а так же показали как создавать агентов восстановления файлов EFS и агентов восстановления ключей:

Когда у нас все агенты восстановления подготовлены и подготовлены шаблоны EFS + Smart Card Logon, то необходимо заранее подготовить смарт-карты и выпустить для них сертификаты. Для этого можно воспользоваться консолью MMC - certmgr.msc и выполнить запрос сертификата:

efs10.3

пользователь выполнил запрос сертификата и драйвер предложил сразу положить сертификат на токен. Данную процедуру необходимо проделать для всех пользователей, которые будут шифровать файлы сертификатами и хранить их на смарт-картах. После этого можно приступать к настройке групповой политики:

efsgpo

В Public Key Policies нужно выбрать свойства Encrypting File System и получим такое окно. Здесь у нас появилось много новых настроек, которых раньше не было. Вот скрин этого же окна в Windows Server 2003:

efsgpo1

тут мы можем только или разрешить или запретить использование EFS. Пробежимся по настройкам новой консоли:

  • Encrypt the contents of the users's Documents folder - при использовании этой опции будет зашифрована вся папка Documents пользователя. Особого смысла в ней не вижу. Но кому-то может пригодиться.
  • Require a smart card for EFS - опция, которая позволяет принудительно хранить EFS ключи шифрования на смарт-картах. Если эта опция включена, но смарт-карты не реализованы, то шифрование будет недоступно. Т.к. у нас смарт-карты есть, то выставляем здесь флажок.
  • Create caching-capable user key from smart card - данная опция позволяет кэшировать ключи шифрования для обеспечения возможности шифрования при изъятом токене или смарт-карте. Параметры кэширования настраиваются дополнительно на вкладке Cache. Если этот флажок сброшен, то будут действовать настройки кеширования драйвера смарт-карты.
  • Enable pagefile encryption - разрешает шифровать файл подкачки. При включении шифрования файла подкачки может замедлиться работа системы и увеличиться нагрузка на систему. Во всяком случае первый раз файл подкачки будет шифроваться достаточно долго.
  • Display key backup notifications when user key is created or changed - включает или отключает уведомление пользователя, что его ключ шифрования был создан или изменён.

Чуть ниже мы можем позволить пользователям генерировать самоподписанные сертификаты EFS при недоступности существующего сертификата или недоступности централизованного Enterprise CA. В нашем случае использование самоподписанных сертификатов будет запрещено.

Примечание: часто замечаю, что администраторы сетей сильно пренебрегают вопросами планирования инфтраструктуры PKI и в Enterprise среде не разворачивают Certification Authority (хотя технических препятствий зачастую просто нету), а используют самоподписанные сертификаты. Я постоянно утверждаю, что самоподписанные сертификаты - вселенское зло, поскольку они неуправляемы. Во-первых, им никто (кроме самого издателя) не доверяет, отсутствует механизм управления настройками сертификатов и отсутствуют механизмы отзыва сертификата при компрометации ключа. Особенно часто такие сертификаты используют для веб-серверов или для OWA (Outlook Web Access).

И в самом низу указывается шаблон сертификатов, который будет использоваться для шифрования файлов. В моём случае это SM EFS. Если используются раздельные сертификаты для смарт-карт и EFS (версии 1), то следует оставить значение по умолчанию - Basic EFS.

Когда групповые политики будут настроены, то можно приступать к самому процессу шифрования. Когда пользователь захочет зашифровать файл, то он должен на файле или папке выбрать Properties -> Advanced -> Encrypt contents to secure data. И появится окно с предложением использовать сертификат смарт-каты для шифрования:

efs10.2

После чего потребуется выбрать сертификат из списка доступных сертификатов на смарт-карте и которые пригодны для шифрования:

efs10.4

Выбрав указанный сертификат потребуется ввести PIN от смарт-карты или токена. После этого файл или папка будут зашифрованы. Во время текущего сеанса, когда смарт-карта или токен установлены шифрованные файлы будут открываться без необходимости дополнительного ввода PIN. Но при изъятии смарт-карты и при установке обратно, либо при первой попытке открытия шифрованного файла после перелогона в трее появится значок, говорящий, что требуется авторизоваться на смарт-карте:

efs15

и потребует ввода PIN в смарт-карту.

Если ключ пользователя был утерян или временно недоступен, то назначенный групповой политикой агент восстановления EFS (как минимум должен иметь право Change extanded attributes на файл) проделывает точно такую же процедуру с вводом PIN от своего токена, где хранится ключ от EFS Recovery Agent и получает возможность расшифровать файл, как бы это сделал пользователь в штатном режиме. Но если токен был потерян, испорчен (т.е. угрозы похищения ключа нету), то не расшифровывать же все файлы пользователя? Можно расшифровывать, а можно и просто восстановить ключ пользователя из базы CA.

Для восстановления ключа шифрования из базы CA потребуется выполнить следующие шаги:

  1. войти в систему под учётной записью, которая имеет право управления Certification Authority (обычно администратор), запустить консоль CMD и в ней выполнить следующую команду:
    certutil -getkey 6148fa9e000000000022 %path%\outblob
    где 6148fa9e000000000022 - серийный номер сертификата. Его можно посмотреть как в самом сертификате, так и в консоли Certifiaction Authority добавив в показ колонки Serial Number
    %path%\outblob - путь к выходному BLOB файлу. Вывод команды должен выглядеть примерно так:

    C:\Users\Administrator>certutil -getkey 6148fa9e000000000022 .\desktop\outblob
    Querying dc1.contoso.com\contoso-DC1-CA.....................

    "dc1.contoso.com\contoso-DC1-CA"
      Serial Number: 6148fa9e000000000022
      Subject: CN=Administrator, CN=Users, DC=contoso, DC=com
      UPN:administrator@contoso.com
      NotBefore: 12/18/2008 10:26 AM
      NotAfter: 12/18/2009 10:26 AM
      Template: SMEFS, SM EFS
      Version: 3
      Cert Hash(sha1): 3c 43 ff 2d 0e 99 35 c4 a1 59 43 47 bc b6 50 91 1c b7 f5 8a

    Recipient Info[0]:
    CMSG_KEY_TRANS_RECIPIENT(1)
    CERT_ID_ISSUER_SERIAL_NUMBER(1)
        Serial Number: 61312c8f000000000020
        Issuer: CN=contoso-DC1-CA, DC=contoso, DC=com
        Subject: CN=Recovery Agent, CN=Users, DC=contoso, DC=com
    CertUtil: -GetKey command completed successfully.

  2. Вот этот BLOB файл нужно передать (например, по электронной почте или через сервис мгновенных сообщений) пользователю, который в CA является агентом восстановления ключей и чей сертификат находится во вкладке Recovery Agents.
  3. получив этот файл агент восстановления должен установить смарт-карту или токен в ридер или USB, где находится закрытый ключ от KRA сертификата. Далее ему нужно запустить консоль CMD и в командной строке выполнить:
    certutil -recoverkey %path%\outblob user.pfx
    где %path%\outblob - путь к BLOB файлу, который был получен на шаге 2
    user.pfx - имя PKCS #12 файла, который будет содержать пару из открытого и закрытого ключа пользователя. Вот так примерно выглядит вывод команды:

    C:\Users\Recovery Agent>certutil -recoverkey outblob administrator.pfx
    dwFlags = CA_VERIFY_FLAGS_CONSOLE_TRACE (0x20000000)
    dwFlags = CA_VERIFY_FLAGS_DUMP_CHAIN (0x40000000)
    ChainFlags = CERT_CHAIN_REVOCATION_CHECK_CHAIN_EXCLUDE_ROOT (0x40000000)
    HCCE_LOCAL_MACHINE
    CERT_CHAIN_POLICY_BASE
    -------- CERT_CHAIN_CONTEXT --------
    ChainContext.dwInfoStatus = CERT_TRUST_HAS_PREFERRED_ISSUER (0x100)

    SimpleChain.dwInfoStatus = CERT_TRUST_HAS_PREFERRED_ISSUER (0x100)

    CertContext[0][0]: dwInfoStatus=10c dwErrorStatus=0
      Issuer: CN=contoso-DC1-CA, DC=contoso, DC=com
      NotBefore: 12/15/2008 1:26 AM
      NotAfter: 12/15/2013 1:36 AM
      Subject: CN=contoso-DC1-CA, DC=contoso, DC=com
      Serial: 6fc7646fe91510bc4631b9dcc08805e3
      c3 44 cb 46 1d d8 d8 cf dc 35 fb 7c 27 cb 7d a1 d4 64 47 2c
      Element.dwInfoStatus = CERT_TRUST_HAS_NAME_MATCH_ISSUER (0x4)
      Element.dwInfoStatus = CERT_TRUST_IS_SELF_SIGNED (0x8)
      Element.dwInfoStatus = CERT_TRUST_HAS_PREFERRED_ISSUER (0x100)

    Exclude leaf cert:
      da 39 a3 ee 5e 6b 4b 0d 32 55 bf ef 95 60 18 90 af d8 07 09
    Full chain:
      c3 44 cb 46 1d d8 d8 cf dc 35 fb 7c 27 cb 7d a1 d4 64 47 2c
    ------------------------------------
    Verified Issuance Policies: All
    Verified Application Policies: All

    Computed Hash: b3 57 bf 4b fe 18 16 bf 38 a4 17 00 17 27 4e b9 43 64 d4 14
    Decrypted PKCS7 Message Content

    User Certificate:
        Serial Number: 6148fa9e000000000022
        Issuer: CN=contoso-DC1-CA, DC=contoso, DC=com
        Subject: CN=Administrator, CN=Users, DC=contoso, DC=com
        Cert Hash(sha1): 3c 43 ff 2d 0e 99 35 c4 a1 59 43 47 bc b6 50 91 1c b7 f5 8a

    Enter new password:
    Confirm new password:
    CertUtil: -RecoverKey command completed successfully.

  4. После успешного расшифрования BLOB файла агенту восстановления будет предложено ввести и подтвердить пароль для .pfx файла, который нужно будет потом ввести при импорте файла в Certificate Store.
  5. Передать полученный файл пользователю любым доступным способом и сообщить пароль к нему. Не следует и файл и пароль передавать одним путём (например, через почту или Messenger).

Если же ключ был скомпрометирован (была утеряна смарт-карта и есть основания, что PIN известен 2-м и более лицам), то сертификат следует отозвать.

Вот мы и рассмотрели полностью (не скажу, что очень детально, но достаточно сносно для первой хау-ту :) ) вопрос имплементации новой возможности Windows Vista/Windows Server 2008 - хранение и использование ключей EFS на смарт-картах. Осталось рассмотреть только один вопрос - что будет, когда различные сертификаты истекут или будут отозваны, который я постараюсь (если всё получится) осветить в заключительной 4-й части.

Thursday, December 18, 2008 9:19:47 PM (FLE Standard Time, UTC+02:00)   Comments [0]    

 

В предыдущей части мы поговорили об основах работы шифрующей файловой системы EFS и основными трудностями, которые появляются при использовании EFS. И одной из важных задач становится защита ключей шифрования надёжным способом от взлома, но и обеспечить возможность восстановления данных при потере ключей шифрования и их доступность. Я уже отметил, что варианта у нас всего 2 - архивирование ключей на внешний контейнер (например, смарт-карта или USB диск) либо Key Archival, когда закрытые ключи дополнительно хранятся в базе Certification Authority. Причём, оба этих метода можно спокойно совместить. Но здесь Microsoft нас снова "радует" (что вполне ожидаемо) - Key Archival работает только при условии, что:

  • Certification Authority (CA) работает под управлением Windows Server 2003/2008 Enterprise/Datecenter Edition.
  • CA является Enterprise CA, а не StandAlone CA.
  • криптопровайдер (CSP) поддерживает хранение закрытых ключей.

Механизм Key Archival работает следующим образом:

  1. Клиент ищет доступный CA в базе AD и устанавливает с ним соединение. При соединении у CA запрашивается сертификат основанный на шаблоне CA Exchange (который используется только для этих целей).
  2. Клиент удостоверяется, что сертификат валидный (имя в сертификате соответствует имени CA, срок годности сертификата ещё не истёк и т.д.) и проверяет списки CRL на предмет отзыва этого сертификата. А так же клиент проверяет, что сертификат подписан именно тем CA, с которым он сейчас работает и на имя этого CA. Это гарантирует, что CA сможет расшифровать полученный на последующих этапах шифрованный закрытый ключ.
  3. Если с сертификатом всё в порядке, то клиент шифрует сгенерированный закрытый ключ открытым ключом сертификата CA Exchange. Если это клиент Windows Vista/Windows Server 2008, то клиент может шифровать свой ключ новыми алгоритмами CNG (Cryptography Next Generation) и использовать алгоритм AES для шифрования. По сути, Windows XP тоже поддерживает AES, но эта поддержка была внедрена только начиная с SP1 и здесь для Windows XP нету возможности использовать AES.
  4. Далее клиент подготавливает полный запрос PKI для CA и этот запрос направляет в CA.
  5. CA в свою очередь дешифрует полученный запрос и сверяет соответствие закрытого и открытого ключа в запросе.
  6. Если соответствие подтверждено, то CA шифрует полученный закрытый ключ рандомным симметричным ключом с использованием 3DES или AES )(только для ОС Windows Vista и выше).
  7. Затем этот симметричный ключ шифруется одним или несколькими открытыми ключами агентов восстановления, которые определены в свойствах CA и которые допущены к архивированию ключей и сохраняет всё это в специальном BLOB (Binary Large Object) формате в своей базе.

Исходя из этой процедуры нетрудно представить процесс восстановления:

  1. администратор CA извлекает необходимый BLOB файл из базы CA.
  2. Т.к. этот файл зашифрован открытым ключом агента (или агентов) восстановления, то файл передаётся любому допустимому агенту восстановления для дешифрации.
  3. агент восстановления использует свой закрытый ключ для дешифрации файла и создаёт .pfx файл, попутно защищая его паролем.
  4. .pfx файл затем передаётся конечному пользователю, который у себя на машине импортирует пару из открытого и закрытого ключа в контейнер Certificate Store.

Для начала нам потребуются агенты восстановления, которые будут уполномочены восстанавливать ключи из базы CA. Для этого в CA существует шаблон Key Recovery Agent. Из изменений, которые потребуются - на вкладке General поставить галочку для публикации сертификата в AD и в Security добавить нужных пользователей (но лучше будет поместить нужных агентов восстановления в отдельную группу и ей назначить право Read и Enroll). Затем следует в консоли CA добавить этот шаблон в список издаваемых сертификатов. Когда эта процедура будет завершена агенты восстановления должны подать заявку на выдачу сертификатов восстановления.

Примечание: здесь стоит отметить, что автоматическая выдача сертификатов работать не будет. Администратор CA должен вручную одобрить каждый запрос в секции Pending Requests. После того как запрос будет одобрен, изданный сертификат появится в секции Crtificate Enrollment Requests. Если агент восстановления для запроса использует Windows 2000/XP/2003, то запрос необходимо произвести запрос сертификата используя Certificates Web Enrollment pages из интернет-браузера.

Когда сертификат получен, то можно в CA разрешать Key Archival. Но если у вас есть смарт-карты, то лучше экспортировать пару ключей Key Recovery Agent на смарт-карту. В моём случае (используя Aladdin eToken Pro) потребовалось выполнить стандартную процедуру экспорта сертификата в PKCS #12 файл (.pfx) и используя ПО токена импортировать сертификат на токен. При экспорте ключей в файл не забудьте поставить галочку для удаления ключей из Certificate Store в случае успешного экспорта. После этой операции агент восстановления перестаёт быть зависимым к своему профилю и будет неуязвим к атакам на профиль.

Теперь администратор CA должен выбрать свойства CA и перейти на вкладку Recovery Agents:

efs3

В этом окне нужно переставить переключатель в Archive the key и кнопкой Add добавить сертификаты тех агентов восстановления, которые будут уполномочены для восстановления ключей. Именно этими сертификатами CA будет шифровать ключи пользователей.

Следующим этапом будет изменение шаблонов для поддержки Key Archival. Шаблоны версии 1 не поддерживают данную функцию, а только V2 и V3, а эти шаблоны поддерживаются только в Windows Server Enterprise и Datacenter редакциях. Конкретно для EFS придётся создать новый шаблон версии 2 или 3:

efs4

Я его назвал SM EFS (Smart Card EFS). Изображение может отличаться от вашего случая. Я предполагаю, что у меня все CA будут работать под управлением Windows Server 2008. Для 2003 картинка может отличаться, но суть останется та же. Далее, нас заинтересует вкладка Request Handling:

efs5

Здесь нужно поставить галочку напротив Archive subject's encryption private key. Галочка ниже (Use advanced Symmetric algorithm to send key to the CA) используется только для клиентов Windows Vista и выше. Если этим шаблоном будут пользоваться более ранние клиенты, то её выставлить нельзя! И последнее, что явно потребуется:

efs6

драйвер токена Alddin eToken Pro для хранения сертификатов EFS на токене требует, чтобы на токене так же был и логонный сертификат, иначе он впадает в дикий ступор :) При использовании Windows Server Standard редакции потребуется 2 сертификата - отдельно на основе шаблона Smart Card Logon и EFS Basic. Т.е. не обязательно, чтобы один сертификат содержал все функции. Т.к. у меня CA работает под управлением Enterprise то я создал один шаблон и в Extensions добавил необходимые политики действия, а именно - добавил Smart Card Logon. Тогда я могу с использованием одного сертификата и аутентифицироваться в домене и шифровать файлы. Официальная документация требует, чтобы в списке CSP провайдеров был указан провайдер смарт-карты (токена), но в моём случае это не обязательно, поскольку при издании сертификата драйвер сам предлагает экспортировать сертификат на токен. Шаблон можно сохранять и назначить его для издания сертификатов:

Certificate Templates -> New -> Certificate Template to issue -> в списке выбрать SM EFS

И последняя часть, которая нам потребуется - указание агента восстановления для EFS.

Примечание: мы выше уже создавали агента восстановления, но у нас будет 2 различных агента восстановления - отдельно для EFS, который будет задан в GPO и агент восстановления ключей из базы CA. Это 2 совершенно разные роли и следует чётко различать назначение каждого агента.

На практике по умолчанию агентом восстановления становится первый администратор первого контроллера домена в лесу Active Directory. В целях безопасности не следует его оставлять как есть. Для этого я создал новую учётную запись с именем Recovery Agent и добавил его в группу EFS Recovery. Более ни в каких группах пользователь не состоит. Чтобы сделать его агентом восстановления нужно сначала разрешить групе EFS Recovery запрашивать сертификат на основе шаблона EFS Recovery Agent:

efs7

Я просто добавил право Read и Enroll для группы EFS Recovery. Затем пользователь, член EFS Recovery должен залогониться, запустить оснастку certmgr.msc и выполнить запрос сертификата:

efs8

И ему будет предложен шаблон EFS Recovery Agent. Когда сертификат будет выдан, то у меня драйвер токена перехватил запрос и предложил положить сертификат на токен:

efs9

и после нажатия на Ok потребуется ввести PIN токена:

efs9.1

Теперь этот сертификат хранится только на токене. В установках по умолчанию для eToken Pro при извлечении токена из USB очищается весь контейнер Personal в Certificate Store. Поэтому когда агент восстановления извлекает токен, то его профиль больше не представляет никакой ценности в плане закрытых ключей, которыми можно восстанавливать EFS файлы. Как известно, при установке смарт-карты или токена в ридер или USB, то происходит копирование ключей с токена в хранилище Certificate Store. Т.к. по умолчанию сертификат шаблона EFS Recovery Agent не публикуется в AD, поэтому агент восстановления должен экспортировать .cer (сертификат с открытым ключом) в папку вручную из программного хранилища. Именно эти сертификаты будут использоваться для шифрования ключей EFS и последующего сохранения в поле DRF.

Далее администратор должен открыть объект групповой политики нужного контейнера и перейти в секцию Encrypting File System, как это показано на рисунке:

efs9.2

Я уже удалил сертификат администратора по умолчанию и добавил сертификат нового агента восстановления EFS, который был сохранён самим агентом в .cer файл.

Примечание: только после этого шага и применения этой политики на все компьютеры контейнера, к которому прилинкована политика данный агент сможет расшифровывать EFS файлы, которые были зашифрованы после этого шага. Все файлы, которые были зашифрованы до этого момента будут недоступны для нового агента восстановления. Этот факт следует учитывать.

Итак, во второй части мы рассмотрели технические вопросы механизмов восстановления, которые включают в себя использование агентов восстановления:

  • Key Archival - архивирование всех закрытых ключей пользователей в базе CA, что обеспечивает возможность восстановления ключей шифрования с минимальными затратами по восстановлению нормальной работы шифрованных файлов.
  • EFS Recovery Agent - в контексте обсуждаемого вопроса применили агента восстановления EFS файлов без необходимости восстановления ключей из базы CA. Тем более Key Archival в ряде случаев будет невозможен для применения.

Так же посмотрели почти пошаговый процесс реализации обоих механизмов и показали как агенты восстановления могут защищать свои ключи восстановления сохранив их на внешние криптоконтейнеры - смарт-карты или токены. Мы подготовили базу для восстановления файлов в случае "а если ...". План мероприятий по восстановлению данных должен быть применён и реализован до эксплуатации EFS и цифровых сертификтов в целом. В следующей части я покажу настройки групповых политик для хранения EFS сертификатов пользователей на смарт-картах на примере использования токена eToken Pro, а так же (если объём следующей части позволит) покажу действие агентов восстановления в случае "а если ...".

Tuesday, December 16, 2008 2:38:07 PM (FLE Standard Time, UTC+02:00)   Comments [7]    

 

Читая различные обзоры нововведений в Windows Vista/Windows Server 2008 ловлю себя на мысли, что по сути, кроме маркетинговых сообщений ничего нету. Повальная виртуализация, мифический NAP и многое другое (опять же, сильно отдаёт маркетингом). Хочется спросить человека "а покажите, как вы это реализовали на практике, с какими столкнулись трудностями и прочее". А вот с этим у нас всё несколько труднее, т.к. одних Get The Fucacts будет маловато для технических специалистов (на что в последнее время очень часто сетует Павел Нагаев и я с ним в этом плане согласен). Поэтому как часть программы по техническому образованию специалистов хочу постараться разобрать одно интересное нововведение в Windows Server 2008 - хранение EFS сертификатов на смарт-картах с использованием новой опции групповых политик. Тема (на сколько я посмотрел) не освещена совсем, кроме как небольших статей на TechNet'e. Разбираемый вопрос для многих, к сожалению, останется неактуальным по причине дороговизны решения - смарт-карты - не самое дешёвое удовольствие, но я заметил, что интерес к данному вопросу всё же есть.

Итак, для начала я хотел бы рассказать про общий механизм работы шифрующей файловой системы EFS.

efs1

Когда пользователь выставляет галочку на чек-боксе Encrypt contents to secure data и нажимает Ok, то происходит несколько вещей:

  1. Проверяется ключ реестра (HKCU\Software\Microsoft\Microsoft\Windows NT\CurrentVersion\EFS\CurrentKey\CertificateHash) на предмет наличия установленного сертификата по умолчанию для EFS.
  2. Если сертификата по умолчанию для EFS не представлено, то проверяется Certificate Store (консоль certmgr.msc) на наличие сертификата, у которого в EKU (Enchanced Key Usage) содержится специальный идентификатор объекта (OID), который отвечает за шифрование.
  3. Если сертификат с таким OID не был найден и если машина находится в домене, то клиент направляет запрос в CA (Certification Authority) на получение сертификата для EFS.
  4. Если машина не в домене, либо в домене отсутствует CA, либо в CA отсутствует соответствующий шаблон, то машина генерирует самоподписанный сертификат (когда сертификат выдан пользователем на самого себя).

Процес шифрования:

  • Локальная подсистема безопасности LSA (Local Security Authority) генерирует рандомный ключ шифрования FEK (File Encryption Key) и этим ключом с использованием симметричного алгоритма шифрования шифрует содержимое файла. Симметричное шифрование во много раз быстрее алгоритма с открытым ключом, но и менее безопасно, поскольку обе половинки симметричного ключа нужно хранить в надёжном и, в то же время, доступном месте. Для этого LSA выбирает открытый ключ сертификата пользователя и шифрует им симметричный ключ шифрования FEK ассиметричным алогоритмом и записывает шифр ключа в специальное поле файла. Кроме этого ключ FEK шифруется открытым ключом сертификата агента восстановления, который назначен либо локальной, либо групповой политикой. Примерная картина хранения ключей изображена на картинке, где DDF - Data Decryption Field, DRF - Data Recovery Field, DRA - Data Recovery Agent:

efs2

Таким образом гарантируется, что доступ к файлу имеет только владелец закрытого ключа пользователя и агент восстановления. В Windows Server 2003 появилась возможность предоставлять шифрованные файлы в общий доступ с другими пользователями. При этом с самим файлом ничего не происходит, а только ключ FEK шифруется открытыми ключами сертификатов тех пользователей, которым разрешён доступ к шифрованному файлу. Именно по этой причине невозможно предоставлять доступ к шифрованному файлу группам - группы не могут иметь сертификатов, а только Security Principals (конкретные участники безопасности, как пользователи и компьютеры). А так же эти пользователи уже должны иметь в базе Active Directory EFS-пригодные сертификаты. Когда я только начал изучать EFS, то не совсем понимал этих требований, пока не начал изучать вопрос более детально и понимать т.н. физику процесса.

Более интересный вариант - шифрование файла в сетевой папке.

  • Когда клиенты Windows 2000/XP/2003/Vista обращаются к сетевой папке по SMB/CIFS к серверу Windows 2000/2003, то файл будет шифроваться локально на удалённой системе. Для этого на удалённой системе загружается профиль пользователя и по вышеуказанным методам ищет пригодный для шифрования EFS сертификат или генерирует самоподписанный. Но данный метод обладал недостатками, поскольку удалённый сервер должен быть доверенный для делегирования, что позволяло обеспечить проверку подлинности пользователя с использованием Kerberos, что могло снизить безопасность сети, а так же это вызывало проблемы доступа к шифрованным файлам по сети с других рабочих станций.

В качестве альтернативного варианта был предложен WebDAV, который позволял использовать защищённый SSL канал при передаче файла. Хотя SSL не обязателен, поскольку любые операции шифрования/расшифрования происходили на клиенте локально. При доступе к шифрованному файлу, он в таком же виде пересылался по сети клиенту, где последний локально расшифровывал файл. После работы файл заново локально шифровался и уже в шифрованном виде передавался по сети.

При использовании клиента Windows Vista и сервера Windows Server 2008, то используется механизм, который реализован в WebDAV, а именно - файл шифруется и расшифровывается локально на клиенте и файл по сети передаётся в шифрованном виде как есть. Но, тем не менее, этот механизм не решает вопрос доступа с различных машин к шифрованному файлу. Дело в том, что при логоне пользователя на рабочую станцию ему не подгружаются сертификаты, которые были использованы на других машинах, они никогда не покидают профиль пользователя. Если в домене применён Certificate Autoenrollment, то автоматически запрашивается новый сертификат с новой парой открытый/закрытый ключ и который непригоден для расшифрования ранее зашифрованных файлов. Для этой задачи существует 2 решения:

  1. хранение закрытого ключа на смарт-карте (теперь он единый для пользователя на всех компьютерах!). К плюсам можно отнести стойкость к взлому профиля, поскольку ключ хранится на внешнем криптоконтейнере. В случае утери/похищения смарт-карты она остаётся под защитой - необходимо ещё знать PIN карты. Вероятность атаки на PIN снижается за счёт блокирования криптоконтейнера при определённом кол-ве неудачных попыток ввода PIN. Из минусов - требуется дополнительное аппаратное обеспечение.
  2. использование Credential Roaming Service, который является расширением перемещаемого профиля и учётные данные (в общем смысле Credentials) подобно перемещаемому профилю  следуют за пользователем. К плюсам можно отнести стоимость внедрения - не требует покупки специальных криптоконтейнеров (смарт-карт). Но от этого более уязвим к взлому и атакам на профиль.

Процесс дешифрования (расшифровки) файла:

При обращении пользователя к файлу LSA проверяет локальное хранилище Certificate Store на предмет наличия закрытого ключа сертификата EFS. По логике ассиметричного шифрования открытый и закрытый ключ взаимно дешифруют друг друга, что означает следующее:

  • файл зашифрованный открытым ключом может быть расшифрован только закрытым ключом, который сопоставлен с этим открытым ключом. Даннмый метод применяется для шифрования данных, как это показано выше.
  • файл зашифрованный закрытым ключом может быть расшифрован только открытым ключом, который сопоставлен с этим закрытым ключом. Данный метод применяется для цифровой подписи, когда пользователь своим закрытым ключом шифрует подпись, а получатель пользуясь открытым ключом пользователя расшифровывает подпись и убеждается в подлинности отправителя.

Используя закрытый ключ EFS сертификата LSA расшифровывает DDF для извлечения FEK, который ранее был зашифрован открытым ключом. После извлечения FEK происходит прямая дешифрация файла. Если же в хранилище Cetificate Store не было обнаруженного ни единого закрытого ключа, который подходил бы любому шифру DDF, то пользовтелю будет отказано в доступе - Access Denied.

При этом стоит отметить, что имя пользователя на сертификате дешифрования совсем не обязан соответствовать залогиненному пользователю. Поэтому нету ограничений на переименование пользователей в процессе.

Примечание: Однако, есть ограничение на смену пароля, а точнее его сброс. Только ленивый не слышал об утилитах, которые сбрасывают пароли локальных и доменных учётных записей, а так же администратор домена или локальной машины может сбрасывать пароли пользователей, тем самым позволяя с новым известным паролем получить доступ к профилю пользователя. Чтобы не допустить потенциальной кражи ключей в EFS заложен механизм зависимости от пароля. Т.е. сам закрытый ключ зашифрован паролем пользователя. Если пароль пользователя будет сброшен, то все данные, которые были зашифрованы пользователем будут утеряны (на самом деле в некоторых случаях это не совсем верно, но в общем смысле - да), поскольку нечем будет расшифровать закрытый ключ. Это справедливо только для сброса пароля. Когда пользователь меняет пароль планово (по сроку изменения пароля заданного в групповых политиках) или внепланово и с указанием предыдущего пароля, то ключи шифрования обновляются (расшифровываются старым паролем и шифруются новым) и после смены пароля доступ к этим файлам не прекращается. Данный механизм описан здесь: http://support.microsoft.com/kb/290260

Какие ещё могут наступить проблемы? А любые проблемы, которые связаны с профилем. Например, был повреждён профиль пользователя и заменён на новый - вы потеряете ключи. Пользователю назначен новый перемещаемый профиль - вы потеряете ключи. А пользователь может сам зайти в свой Certification Store и удалить ключи - вы потеряете ключи. Универсальной панацеи от этого нету - есть только более или менее эффективные средства защиты и сохранности данных и ключей шифрования.

Если пользователь потерял доступ к своим закрытым ключам EFS, то агент восстановления - Recovery Agent может своим ключом расшифровать нужные файлы и пользователь получит к ним доступ в открытом виде. После этого пользователь может заново сгенерированным ключом снова зашифровать данные. Но сами ключи шифрования можно восстановить только одним единственным способом - Key Recovery.

Организация плана защиты, восстановления ключей и восстановления доступа будет рассмотрена во второй части. В принципе, весь этот материал доступно изложен на TechNet'e (http://technet.microsoft.com/en-us/library/cc700811.aspx), но тем не менее, я хочу подвести плавно тему к целевой задаче использования смарт-карт и задач, которые мы сможем решать с их помощью. Ну и для себя немного прояснить материал.

Monday, December 15, 2008 11:46:50 PM (FLE Standard Time, UTC+02:00)   Comments [6]    

 

SRP или Sotware Restriction Policies - мощный инструмент управления безопасности и контроля запуска приложений в ОС Windows. Политики ограниченного использования программ достаточно просты и состоят из менее, чем 2-х десятков настроек. Но в то же время политика SRP требует очень серьёзного понимания предмета. Я в своё время сделал 2 попытки популяризировать SRP среди администраторов постсоветского пространства как в своём предыдущем блоге, так и в журнале "Системный администратор", где я старался более подробно рассказать про технологию:

Однако, к великому сожалению, в журнальной статье был упущен один момент, который обнаружили сегодня на форуме: http://forum.sysfaq.ru/index.php?showtopic=14462

В статье говорится:

Для унификации работы с папками профилей пользователей политика SRP предлагает возможность чтения значений из реестра. Ветка реестра: HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders\ содержит пути для большинства пользовательских папок профиля. Для указания пути к Start Menu рекомендуется использовать значения реестра для каждой локальной машины. Чтобы использовать значения реестра, его ключ нужно заключить в знаки процента «%», как это показано на примерах:

%HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders\Programs%*.lnk

%HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders\Start Menu%*\*.lnk

Тут всё почти верно, кроме последнего примера. Дело в том, что данный пример не будет работать по одной простой причине:

http://technet.microsoft.com/en-us/library/cc786941.aspx

A registry path rule suffix must not contain a backslash (\) character immediately after the last percent sign (%) in the rule

Тут, конечно же, неточность есть и в статье TechNet'а, поскольку обратный слеш ( \ ) нельзя использовать не только сразу после завершающего ключ реестра знака процента ( % ), но и далее, когда используется дополнительный суффикс. Дополнительный суффикс используется для продолжения пути извлечённого из реестра (об этом прочитаете в статье журнала). К сожалению, нигде в интернете (а так же и в документации TechNet) я не смог найти решения данной проблемы, когда суффикс состоит из 2 и более уровня папок (вида %regkey%folder\subfolder1\subfolder2\etc). Ещё в момент написания статьи для журнала я быстро нашёл решение - если нельзя использовать обратный слеш ( \ ), то почему бы не попробовать использовать прямой слеш ( / )? И попал пальцем в небо. Особого криминала здесь нету, просто при составлении правил политик SRP учтите, что при использовании относительных путей в виде ключей реестра и продолжении пути суффиксами для вложенных папок используйте только прямой слеш - ( / ), тогда всё будет работать прекрасно. Напоминаю, что вы так же можете использовать подстановочные знаки как, например ( * ) Данный момент не был учтён в статье по моей невнимательности.

А теперь поговорим о том, чего я тогда ещё не знал. И, уважаемые администраторы, убедитесь, что ваши пользователи не прочитали материал ниже раньше вас ;)

По умолчанию в Windows XP/Windows Server 2003 установлено 4 исключения для действия по умолчанию:

  • %HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\SystemRoot%
  • %HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\SystemRoot%*.exe
  • %HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\SystemRoot%System32\*.exe
  • %HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\ProgramFilesDir%

Это исключения для папок Windows и Program Files. Эти правила вполне обоснованы, поскольку обычные пользователи не имеют права записи ни в папку Windows, ни в Program Files. При этом видно, что третье правило использует обратный слеш! По всей видимости разработчики Microsoft сами себя ввели в заблуждение? Вполне возможно. Далее мы видим, что первое правило перекрывает второе и третье! Можно сказать, что эти правила лишние и достаточно первого. Действительно, если взять систему с Windows Vista или Windows Server 2008, то мы увидим только 2 исключения по умолчанию:

  • %HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\SystemRoot%
  • %HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\ProgramFilesDir%

Но на самом деле всё не так и просто. Я бы в жизни не догадался об этом, если бы не Александр Станкевич (MVP Enterprise Security)! Я так и не смог найти внятного объяснения этому факту:

TempACL

Полагаю, что комментировать тут нечего, на картинке всё и так видно, к чему я клоню ;) Вам мало? Получите ещё:

SpoolACL

Вобщем, подумайте, господа администраторы - а вы точно хорошо разобрались в вопросе Software Restriction Policies? У вас ещё есть время подумать, пока ваши пользователи читают эту статью :-D

Thursday, December 11, 2008 7:44:05 PM (FLE Standard Time, UTC+02:00)   Comments [2]    

 

В предыдущей части мы рассмотрели возможности управления журналами событий средствами .NET Framework, а так же рассмотрели проблематику использования .NET (из моего предыдущего блога) - Странности Get-Eventlog и не совсем богатый функционал. В этом посте мы разберём данный вопрос, но с использованием WMI и все примеры будут выполняться системе под управлением Windows Vista.

В WMI за журнал событий отвечают следующие классы:

  1. Win32_NTEventlogFile
  2. Win32_NTLogEvent
  3. Win32_NTLogEventComputer
  4. Win32_NTLogEventLog
  5. Win32_NTLogEventUser

WMI, как и .NET поддерживает удалённую работу с использованием ключа -ComputerName, поэтому на этом заострять внимание не будем. За публикацию списка журналов в системе отвечает класс Win32_NTEventlogFile:

[vPodans] gwmi Win32_NTEventlogFile FileSize LogfileName Name NumberOfRecords -------- ----------- ---- --------------- 8458240 Application C:\Windows\System32\Winevt... 16154 69632 DFS Replication C:\Windows\System32\Winevt... 6 69632 HardwareEvents C:\Windows\System32\Winevt... 0 69632 Internet Explorer C:\Windows\System32\Winevt... 0 69632 Key Management Service C:\Windows\System32\Winevt... 0 69632 ODiag C:\Windows\System32\Winevt... 267 1118208 OSession C:\Windows\System32\Winevt... 524 20975616 System C:\Windows\System32\Winevt... 42472 12652544 Windows PowerShell C:\Windows\System32\Winevt... 14208 [vPodans]

Стандартная процедура вывода списка журналов в системе. А теперь и сами эвенты:

[vPodans] gwmi win32_ntlogevent -filter "logfile='application'" | select -first 1 Category : 0 Category String : Event Code : Event Identifier : Type Event : Insertion Strings : Log File : Message : The User Profile Service has started successfully. Record Number : Source Name : Time Generated : Time Written : Type : Information User Name : [vPodans]

как бы информации не сильно много. Давайте немного приведём вид в более оперативный:

[vPodans] gwmi win32_ntlogevent -filter "logfile='application'" | select recordnumber, timegenerated, sourcename, eventc ode, message -first 1 | ft -a recordnumber timegenerated sourcename eventcode message ------------ ------------- ---------- --------- ------- 1 20070710111520.000000-000 Microsoft-Windows-User Profiles Service 1531 The User Profile Service ha... [vPodans]

уже более читабельно, кроме вывода даты. Чтобы подвести вывод даты и времени в формат [datetime] нужно воспользоваться конвертером времени:

[System.Management.ManagementDateTimeconverter]::ToDateTime("20070710111520.000000-000")

Чтобы привести итоговый вывод времени в укороченный формат даты и времени к данному выражению достаточно применить метод ToString().

([System.Management.ManagementDateTimeconverter]::ToDateTime("20070710111520.000000-000")).ToString()

[vPodans] [System.Management.ManagementDateTimeconverter]::ToDateTime("20070710111520.000000-000")

otrdiena, 2007. gada 10. julija 14:15:20


[vPodans] ([System.Management.ManagementDateTimeconverter]::ToDateTime("20070710111520.000000-000")).ToString()
10.07.07. 14:15:20
[vPodans]

А теперь всё сделаем так (с использованием хэш-таблиц), чтобы это всё на лету преобразовывалось и мы получили сразу готовый и более опрятный вывод:

$a =  gwmi win32_ntlogevent -filter "logfile='application'" | select -first 1
$a | Select recordnumber, @{n="timegenerated";
e={([System.Management.ManagementDateTimeconverter]::ToDateTime($_.timegenerated)).tostring()}},
sourcename, eventcode, message | ft -AutoSize
[vPodans] $a =  gwmi win32_ntlogevent -filter "logfile='application'" | select -first 1
[vPodans] $a | Select recordnumber, @{n="timegenerated";
>> e={([System.Management.ManagementDateTimeconverter]::ToDateTime($_.timegenerated)).tostring()}},
>> sourcename, eventcode, message | ft -AutoSize
>>

recordnumber timegenerated      sourcename                              eventcode message
------------ -------------      ----------                              --------- -------
           1 10.07.07. 14:15:20 Microsoft-Windows-User Profiles Service      1531 The User Profile Service has start...


[vPodans]

вот так при помощи костылей мы привели вывод в нечто более божеское. В остальном WMI не представляет ничего интересного, что могло бы представиться полезным (за исключением возможности бэкапа журналов, которую я разобрал в предыдущем посте). Разобрать работу Win32_NTLogEventUser и Win32_NTLogEventComputer в принципе можно уже и самостоятельно.

Исходя из вышеизложенного можно понять, что WMI вариант управления эвентлогом не самый удобный и практичный. Но позволяет извлекать события из журналов Windows Vista/Windows Server 2008 в читабельном виде. При этом можно изменять различные параметры журналов событий которые описаны здесь: http://msdn.microsoft.com/en-us/library/aa394225(VS.85).aspx

Изменения делаются по одной общей схеме:

$EventLog = gwmi Win32_NTEventlogFile -Filter "logfilename = '$logfilename'"
$EventLog.Property = "New Property Value"
$EventLog.Put()

И в качестве последнего штриха приведу замеры скорости работы с эвентлогом с использованием .NET и WMI:

  • Windows Server 2003
[user name] (measure-command {gwmi win32_ntlogevent -filter "logfile='application' and eventcode=8194"}).totalseconds
13.0865344
[user name] (measure-command {(new-object diagnostics.eventlog("application")).Entries | ? {$_.EventID -eq 8194}}).totalseconds
20.7334336
[user name]

  • Windows Vista:
[vPodans] (measure-command {gwmi win32_ntlogevent -filter "logfile='application' and eventcode=8194"}).totalseconds
18,0120719
[vPodans] (measure-command {(new-object diagnostics.eventlog("application")).Entries | ? {$_.EventID -eq 8194}}).totalseconds
13,3134996
[vPodans]

Как видим, для Windows Server 2003 более предпочтительным по скорости является WMI (от которого там толку не сильно много), а в Windows Vista - наоборот, .NET работает шустрее. Но из-за проблем с отображением событий журнала Security его польза в Windows Vista весьма сомнительна.

Tuesday, December 09, 2008 10:00:42 PM (FLE Standard Time, UTC+02:00)   Comments [0]    

 

В продолжении темы работы с журналом собитый (EventLog) хочу немного рассказать об удалённой работе с EventLog и основными задачами управления. При этом хочу отметить, что у нас есть 2 различных механизма управления:

  1. .NET Framework
  2. WMI

Но в зависимости от архитектуры ОС оба метода обладают различными характеристиками по времени работы, отображению событий и нагрузке системы. Забегая вперёд скажу, что использование чистого .NET в Windows Server 2003 занимает хоть и немного больше времени, но загрузка процессора ниже, чем у WMI. А вот в Windows Vista .NET работает быстрее по времени и с меньшей нагрузкой на процессор. Однако, учитывая нюансы, которые изложены здесь: Странности Get-Eventlog и тот факт, что командлет Get-Eventlog основан на .NET, то его применимость в условиях Windows Vista/2008 весьма сомнительна. Поэтому вариант с использованием .NET я буду писать применимо для Windows XP/2003.

Начнём с простого: управление журналом событий реализовано в классе System.Diagnostics.Eventlog. Для перечисления списка журналов можно воспользоваться статическим методом GetEventLogs. Как известно, в PowerShell статические методы указываются после двойного знака двоеточия - ::

[Administrator] [System.Diagnostics.EventLog]::GetEventLogs("dc1") Max(K) Retain OverflowAction Entries Name ------ ------ -------------- ------- ---- 16 384 0 OverwriteAsNeeded 289 Application 512 0 OverwriteAsNeeded 45 Directory Service 512 7 OverwriteOlder 10 DNS Server 512 0 OverwriteAsNeeded 52 File Replication Service 131 072 0 OverwriteAsNeeded 12 369 Security 16 384 0 OverwriteAsNeeded 498 System 15 360 0 OverwriteAsNeeded 625 Windows PowerShell [Administrator]

При этом обратите внимание на аргумент в скобках - в них можно указывать имена удалённых компьютеров для получения списка журналов с них. В данном случае я посмотрел список журналов на контроллере домена.  Чтобы получить события из журнала нужно создать объект класса System.Diagnostics.Eventlog:

New-Object System.Diagnostics.Eventlog("Application","dc1")

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

[Administrator] $EventLog = new-Object System.Diagnostics.Eventlog("Application","dc1") [Administrator] $EventLog | gm -MemberType property TypeName: System.Diagnostics.EventLog Name MemberType Definition ---- ---------- ---------- Container Property System.ComponentModel.IContainer Container {get;} EnableRaisingEvents Property System.Boolean EnableRaisingEvents {get;set;} Entries Property System.Diagnostics.EventLogEntryCollection Entries {get;} Log Property System.String Log {get;set;} LogDisplayName Property System.String LogDisplayName {get;} MachineName Property System.String MachineName {get;set;} MaximumKilobytes Property System.Int64 MaximumKilobytes {get;set;} MinimumRetentionDays Property System.Int32 MinimumRetentionDays {get;} OverflowAction Property System.Diagnostics.OverflowAction OverflowAction {get;} Site Property System.ComponentModel.ISite Site {get;set;} Source Property System.String Source {get;set;} SynchronizingObject Property System.ComponentModel.ISynchronizeInvoke SynchronizingObject {get;set;} [Administrator]

Заодно мы и посмотрели свойства полученного объекта. Для получения списка логов нужно воспользоваться свойством Entries:

[Administrator] $EventLog.Entries | select -last 5 Index Time Type Source EventID Message ----- ---- ---- ------ ------- ------- 285 Nov 29 21:05 Info SceCli 1704 Security policy in the Group policy objects has been applied ... 286 Nov 29 21:35 Info SceCli 1704 Security policy in the Group policy objects has been applied ... 287 Nov 29 21:40 Info SceCli 1704 Security policy in the Group policy objects has been applied ... 288 Nov 29 21:43 Info SceCli 1704 Security policy in the Group policy objects has been applied ... 289 Dec 08 20:19 Info SceCli 1704 Security policy in the Group policy objects has been applied ... [Administrator]

Кстати говоря, если посмотреть вывод команды Get-Eventlog, то он будет идентичный. Т.е. можно смело предположить, что командлет Get-Eventlog использует именно механизмы .NET. А теперь попробуем рассмотреть статические методы данного класса:

[Administrator] $EventLog | gm -MemberType methods -Static TypeName: System.Diagnostics.EventLog Name MemberType Definition ---- ---------- ---------- CreateEventSource Method static System.Void CreateEventSource(String source, String logName), static System.... Delete Method static System.Void Delete(String logName), static System.Void Delete(String logName... DeleteEventSource Method static System.Void DeleteEventSource(String source), static System.Void DeleteEvent... Equals Method static System.Boolean Equals(Object objA, Object objB) Exists Method static System.Boolean Exists(String logName), static System.Boolean Exists(String l... GetEventLogs Method static System.Diagnostics.EventLog[] GetEventLogs(), static System.Diagnostics.Even... LogNameFromSourceName Method static System.String LogNameFromSourceName(String source, String machineName) ReferenceEquals Method static System.Boolean ReferenceEquals(Object objA, Object objB) SourceExists Method static System.Boolean SourceExists(String source), static System.Boolean SourceExis... WriteEntry Method static System.Void WriteEntry(String source, String message), static System.Void Wr... WriteEvent Method static System.Void WriteEvent(String source, EventInstance instance, Params Object[... [Administrator]

Первый метод, CreateEventSource позволяет создать свой источник событий в эвентлоге. А так же свой журнал:

[Administrator] [System.Diagnostics.EventLog]::CreateEventSource("MySource", "PowerShell CustomLog", "dc1") [Administrator] [System.Diagnostics.EventLog]::GetEventLogs("dc1") Max(K) Retain OverflowAction Entries Name ------ ------ -------------- ------- ---- 16 384 0 OverwriteAsNeeded 289 Application 512 0 OverwriteAsNeeded 45 Directory Service 512 7 OverwriteOlder 10 DNS Server 512 0 OverwriteAsNeeded 52 File Replication Service 512 7 OverwriteOlder 0 PowerShell CustomLog 131 072 0 OverwriteAsNeeded 12 369 Security 16 384 0 OverwriteAsNeeded 498 System 15 360 0 OverwriteAsNeeded 625 Windows PowerShell [Administrator]

Вот так очень просто мы создали собственный источник событий (здесь не отображён) и свой собственный журнал событий! Причём, всё так же удалённо! Для удаления журнала и источника события необходимо использовать статические методы DeleteEventSource и Delete:

[System.Diagnostics.EventLog]::Delete("LogName")
[
System.Diagnostics.EventLog]::DeleteEventSource("SourceName")

Чтобы управлять размером журнала нужно использовать свойство MaximumKilobytes. Увеличим размер журнала PowerShell CustomLog с 512 килобайт до 2-х мегабайт:

[Administrator] $EventLog = new-Object System.Diagnostics.Eventlog("PowerShell CustomLog","dc1") [Administrator] $EventLog.MaximumKilobytes = 2048 [Administrator] [System.Diagnostics.EventLog]::GetEventLogs("dc1") Max(K) Retain OverflowAction Entries Name ------ ------ -------------- ------- ---- 16 384 0 OverwriteAsNeeded 289 Application 512 0 OverwriteAsNeeded 45 Directory Service 512 7 OverwriteOlder 10 DNS Server 512 0 OverwriteAsNeeded 52 File Replication Service 2 048 7 OverwriteOlder 0 PowerShell CustomLog 131 072 0 OverwriteAsNeeded 12 369 Security 16 384 0 OverwriteAsNeeded 498 System 15 360 0 OverwriteAsNeeded 625 Windows PowerShell

Если сравнить 2 последних вывода, то заметим, что размер действительной изменился. Давайте запишем какое-нибудь событие в журнал. Для этого воспользуемся методом WriteEntry:

[Administrator] $EventLog.Source = "MySource" [Administrator] $EventLog.WriteEntry("Hello World!", "Information") [Administrator] $EventLog = new-Object System.Diagnostics.Eventlog("PowerShell CustomLog","dc1") [Administrator] $EventLog.entries Index Time Type Source EventID Message ----- ---- ---- ------ ------- ------- 1 Dec 08 22:55 Info MySource 0 Hello World! [Administrator]

Сперва мы указали источник события, потом применили метод WriteEntry, где ввели текст и тип события. Последней строкой мы убедились, что событие успешно записалось! Что касается типа события, то тут самому особо придумать ничего нельзя, потому что список типов жёстко регулируется:

[Administrator] [enum]::GetNames([System.Diagnostics.EventLogEntryType]) Error Warning Information SuccessAudit FailureAudit [Administrator]

Чтобы очистить журнал от событий нужно использовать метод Clear:

$EventLog = New-Object System.Diagnostics.Eventlog("PowerShell CustomLog","dc1")
$EventLog.Clear()

И напоследок расскажу о действии при заполнении журнала. Для этого используется метод ModifyOverflowPolicy и метод может иметь следующие свойства:

Эти свойства, думаю, в представлении не нуждаются, поэтому покажу примеры их использования:

$EventLog = New-Object System.Diagnostics.Eventlog("PowerShell CustomLog","dc1")
$EventLog.ModifyOverflowPolicy("OverwriteAsNeeded",$null)
$EventLog.ModifyOverflowPolicy("DoNotOverwrite",$null)
$EventLog.ModifyOverflowPolicy("OverwriteOlder",14)

К сожалению, средствами .NET невозможно архивировать журналы собитий, как это я описывал в предыдущей статье. Вот, вроде рассказал всё, что мне казалось интересным по этой теме. В следующий раз расскажу об управлении эвентлогом средствами WMI.

Monday, December 08, 2008 11:58:03 PM (FLE Standard Time, UTC+02:00)   Comments [0]    

 

Снова навеяно темой на форуме TechNet-Ru. Уже не первый раз встречаю топики про архивирование журналов событий для последующего хранения в оффлайне. Безусловно не стоит пытаться скопировать .evt файл из папки Windows, поскольку файлы открыты и заблокированы (как и .pst файлы при запущенном MS Outlook). Для архивирования журнала скриптом нужно либо использовать Volume Shadow Copy либо использовать особые методы (например, вот так: http://support.microsoft.com/kb/312571). В качестве особых методов можно так же выделить использование WMI, которое позволяет решить поставленную задачу и добавит нам удалённой управляемости.

Полностью опираться на тему форума нельзя, ибо задача поставлена некорректно. Смысла в ежедневном удалении логов с машин без предварительного архивирования нету совсем (иначе вы потеряете точку отправления в решении проблемы, когда она возникнет и зачастую единственным выходом будет переинсталляция или восстановление из бакупа). Поэтому немного переформулируем задачу и напишем решение.

Задача: проводить с некоторой периодичностью архивацию журнала событий в файл. Убедиться, что бэкап сделан и после этого очистить журналы. В противном случае сделать что-нибудь другое.

За отправную точку возьмём класс Win32_NTEventlogFile. Давайте, вызовем его:

[vPodans] gwmi Win32_NTEventlogFile FileSize LogfileName Name -------- ----------- ---- 8458240 Application C:\Windows\System32\Winevt... 69632 DFS Replication C:\Windows\System32\Winevt... 69632 HardwareEvents C:\Windows\System32\Winevt... 69632 Internet Explorer C:\Windows\System32\Winevt... 69632 Key Management Service C:\Windows\System32\Winevt... 69632 ODiag C:\Windows\System32\Winevt... 1118208 OSession C:\Windows\System32\Winevt... 20975616 System C:\Windows\System32\Winevt... 11603968 Windows PowerShell C:\Windows\System32\Winevt... [vPodans]

Он показал список доступных журналов, с которыми мы можем работать. Давайте взглянем на методы данного класса:

[vPodans] gwmi Win32_NTEventlogFile | gm -MemberType method TypeName: System.Management.ManagementObject#root\cimv2\Win32_NTEventlogFile Name MemberType Definition ---- ---------- ---------- BackupEventlog Method System.Management.ManagementBaseObject BackupEventlog(System.String ArchiveFi... ChangeSecurityPermissions Method System.Management.ManagementBaseObject ChangeSecurityPermissions(System.Manag... ChangeSecurityPermissionsEx Method System.Management.ManagementBaseObject ChangeSecurityPermissionsEx(System.Man... ClearEventlog Method System.Management.ManagementBaseObject ClearEventlog(System.String ArchiveFil... Compress Method System.Management.ManagementBaseObject Compress() CompressEx Method System.Management.ManagementBaseObject CompressEx(System.String StartFileName... Copy Method System.Management.ManagementBaseObject Copy(System.String FileName) CopyEx Method System.Management.ManagementBaseObject CopyEx(System.String FileName, System.... DeleteEx Method System.Management.ManagementBaseObject DeleteEx(System.String StartFileName) GetEffectivePermission Method System.Management.ManagementBaseObject GetEffectivePermission(System.UInt32 P... Rename Method System.Management.ManagementBaseObject Rename(System.String FileName) TakeOwnerShip Method System.Management.ManagementBaseObject TakeOwnerShip() TakeOwnerShipEx Method System.Management.ManagementBaseObject TakeOwnerShipEx(System.String StartFil... Uncompress Method System.Management.ManagementBaseObject Uncompress() UncompressEx Method System.Management.ManagementBaseObject UncompressEx(System.String StartFileNa... [vPodans]

Здесь нас заинтересует 2 метода - BackupEventlog и ClearEventlog (там ещё есть ChangeSecurityPermissions - я бы не советовал с ним связываться :-D). В упрощённом варианте копирование и удаление будет сводиться к:

$LogName = "Application"
$Eventlog = gwmi Win32_NTEventlogFile -Filter "LogFileName = '$LogName'"
$Eventlog.BackupEventLog("F:\Eventlogs\$env:computername\$LogName" + "_" + "$(Get-Date -Format dd.MM.yyyy).evt")
$EventLog.Clear()

В данном случае с локальной машины заархивируем журнал Application в путь (после преобразований переменных) F:\Eventlogs\THOR\Application_04.12.2008.evt, после чего данный журнал будет очищен. Ничего сложного нету совсем. Разве что кроме одной детали. Такой фокус не получится с журналом Security, поскольку это жрунал аудита. И для очистки журнала потребуется право Manage auditing and security log привилегия SeAudit (в общем смысле это будет административная учётная запись). Поэтому перед использованием метода ClearEventlog нужно подключить эти самые привилегии:

$EventLog.PSBase.Scope.Options.EnablePrivileges = $true

вот и всё. Осталось только решить задачу ротации журналов (удалять все файлы, которые старше 365 дней или 1 года):

dir F:\Eventlogs\$env:computername -Recurse | ? {$_.lastwritetime -gt (Get-Date).AddDays(-365)} | del -Force

В качестве последнего штриха стоит оформить всё в красивый скрипт:

######################################################## # EventLog Backup Manager.ps1 # Version 1.0 # # Eventlog archiving script # # Vadims Podans (c) 2008 # http://www.sysadmins.lv/ ######################################################## function Backup-Eventlog { param ([string]$Computer) Begin { # разово получаем текущую дату в простом формате для пристыковки даты к имени файла $date = Get-Date -Format dd.MM.yyyy } Process { # получение списка всех журналов событий на текущем компьютере $Eventlog = gwmi Win32_NTEventlogFile -ComputerName $Computer $Eventlog | % { # косметическая переменная для вставки имени лога в текстовые сообщения $CurrentName = $_.LogFileName $CurrentLog = $_ # включение SeAudit привилегий $_.PSBase.Scope.Options.EnablePrivileges = $true # задаём путь, куда будут складываться архивы эвентлога $path = "\\BackupServer\Eventlogs\$computer\$CurrentName" + "_" + $date + ".evt" Write-Host "Processing $CurrentName log on $computer .." # непосредственно сам бэкап $Backup = $_.BackupEventLog($path) # проверка статуса бэкапа текущего журнала if ($Backup.ReturnValue -eq 0) { Write-Host "Success" # если бэкап прошёл успешно, то можно удалять записи в журнале и командлетом Out-Null
# подавляем ненужный вывод на экран
$CurrentLog.ClearEventlog() | out-null } else { Write-Warning "Unexpected error occured. Operation aborted" } } } End { # эта часть обеспечивает удаление старых архивов и их ротацию после каждого запуска скрипта # стоит отметить, что данная секция выполняется только один раз и после проведения всей архивации Write-Host "Purging old archives .." dir \\BackupServer\Eventlogs\$computer -Recurse | ? {$_.lastwritetime -gt (Get-Date).AddDays(-365)} | del -Force } }

как бы и всё.

Thursday, December 04, 2008 11:55:32 PM (FLE Standard Time, UTC+02:00)   Comments [0]    

 

Из-за сильной занятости на работе в последнее время не получается уделять время блогу. Но сегодня выкроил немного времени и решил рассказать одну, на мой взгляд, интересную тему, которую каждый решает по-своему и не всегда очень удобным способом (о чём свидетельствует книга по PowerShell и ньюсгруппы). И на это есть весомые причины. А так же расскажу о весьма странном поведении ряда командлетов (на примере Get-Acl) при работе с файлами.

Давайте рассмотрим вопрос с начала и выйдем на нашу проблему. При поиске группы файлов PowerShell, как и многие другие командные оболочки, позволяет создавать маску поиска, как "*", "?". Безусловно с ними проблем быть не может, т.к. эти знаки в именах файлов встречаться не могут. Но если в имени файла используется символ открывающейся или закрывающейся квадратной скобки - "[" и "]", соответственно. Данные символы допустимы для именования файлов, но интерпретатор PowerShell их воспринимает как мета-символы подстановочного шаблона/маски, например:

[a-z]* - все объекты, которые начинаются с букв латинского алфавита
*[0-9] - все объекты, которые заканчиваются любой цифрой и т.д.

При этом встаёт вопрос, как сказать интерпретатору, что квадратная скобка в данном случае является литералом, а не мета-символ? Для начала я в папке создал 3 файла, 2 из которых содержат квадратные скобки:

[Folder] dir Directory: Microsoft.PowerShell.Core\FileSystem::D:\Users\_Shared Documents\Folder Mode LastWriteTime Length Name ---- ------------- ------ ---- -a--- 02.12.08. 0:47 11 text.txt -a--- 02.12.08. 0:47 296 text[0].txt -a--- 02.12.08. 0:48 769 text[text.txt [Folder]

Давайте просмотрим содержимое файла text[text.txt:

[Folder] Get-Content text[text.txt Get-Content : Cannot retrieve the dynamic parameters for the cmdlet. The specified wildcard pattern is not valid: text[ text.txt At line:1 char:12 + Get-Content <<<< text[text.txt [Folder]

занятно, не правда ли? Здесь мы столкнулись с проблемой, что интерпретатор PowerShell воспринял скобку в имени файла за мета-символ. Бороться с этим можно различными методами. Как вариант - использовать параметр -LiteralPath, что будет говорить интерпретатору о буквальном чтении пути. Проверим:

[Folder] Get-Content -LiteralPath text[text.txt Quest, Quest Software, the Quest Software logo, Aelita, Benchmark Factory, Big Brother, DataFactory, DeployDirector, ERDisk, Fastlane, Final, Foglight, Funnel Web, I/Watch, Imceda, InLook, InTrust, IT Dad, JClass, JProbe, LeccoTech, LiveReorg, NBSpool, NetBase, PerformaSure, PL/Vision, Quest Central, RAPS, SharePlex, Sitraka, SmartAlarm, Speed Change Manager, Speed Coefficient, Spotlight, SQL Firewall, SQL Impact, SQL LiteSpeed, SQL Navigator, SQLab, SQLab Tuner, SQLab Xpert, SQLGuardian, SQLProtector, SQL Watch, Stat, Stat!, Toad, T.O.A.D., Tag and Follow, Vintela, Virtual DBA, and XRT are trademarks and registered trademarks of Quest Software, Inc. Other trademarks and registered trademarks used in this guide are property of their respective owners. [Folder]

Как видите, мы получили содержимое файла (там отрывок из дисклаймера Quest Software). Однако, параметром -LiteralPath обладают далеко не все командлеты. Например, Get-Acl. У него нету такого параметра, поэтому получить ACL список такого файла будет очень проблематично. Посмотрим пример:

[Folder] Get-Item -LiteralPath text[text.txt Directory: Microsoft.PowerShell.Core\FileSystem::D:\Users\_Shared Documents\Folder Mode LastWriteTime Length Name ---- ------------- ------ ---- -a--- 02.12.08. 0:59 769 text[text.txt [Folder] Get-Item -LiteralPath text[text.txt | Get-Acl Get-Acl : The specified wildcard pattern is not valid: text[text.txt At line:1 char:45 + Get-Item -LiteralPath text[text.txt | Get-Acl <<<<

Get-Item спокойно работает с LiteralPath, но при передаче его по конвейеру в командлет Get-Acl мы получаем ошибку. Хотя, по идее это должно было сработать. Кстати говоря, это первый обнаруженный недостаток. Вместо LiteralPath можно использовать 4 backtick (text````[text.txt), либо проще (что мне показалось более очевидным) - поместить литеральную скобку в подстановочный шаблон:

[Folder] Get-Item text````[text.txt Directory: Microsoft.PowerShell.Core\FileSystem::D:\Users\_Shared Documents\Folder Mode LastWriteTime Length Name ---- ------------- ------ ---- -a--- 02.12.08. 0:59 769 text[text.txt [Folder] Get-Item text[[]text.txt Directory: Microsoft.PowerShell.Core\FileSystem::D:\Users\_Shared Documents\Folder Mode LastWriteTime Length Name ---- ------------- ------ ---- -a--- 02.12.08. 0:59 769 text[text.txt [Folder]

В первом примере я использовал рецепт из книги Windows PowerShell In Action (страница 310), второй я додумал сам. Однако, оба эти приёма заменяют параметр -LiteralPath, но так же не решают проблемы командлетов, как Get-Acl. Если передавать эти объекты в Gat-Acl, то всё равно мы будем получать ошибки. Как вариант решения - использование промежуточной функции-фильтра (это рецепт решения Kiron из ньюсгрупп):

filter Escape-Bracket {
$_.psPath = $_.psPath -replace '\[|]','`$0'
$_
}

При использовании фильтра наш литеральный символ заменяем подстановочным знаком "?", пропускаем через фильтр и подаём на командлет Get-Acl:

[Folder] Get-Item text?text.txt | Escape-Bracket | Get-Acl | ft -a Directory: Microsoft.PowerShell.Core\FileSystem::D:\Users\_Shared Documents\Folder Path Owner Access ---- ----- ------ text[text.txt Thor\vPodans Everyone Allow FullControl... [Folder]

Так оно работает. В качестве первого предположения я подумал на тип объектов:

[Folder] (Get-Item 'text[[]text.txt').GetType().fullname System.IO.FileInfo [Folder] (Get-Acl text.txt).GetType().fullname System.Security.AccessControl.FileSecurity [Folder]

Что при Get-Item мы получаем тип объектов FileInfo, который не содержит ACL, поэтому данный командлет передаёт в конвейер свойство PsPath, который уже Get-Acl использует для повторного извлечения уже ACL объекта.

Однако, я обратил внимание, что при использовании UNC путей вместо локальных промежуточная функция не требуется и вполне работает подстановочная маска заключённая в квадратные скобки:

[Folder] Get-Acl 'text[[]text.txt' Get-Acl : The specified wildcard pattern is not valid: text[text.txt At line:1 char:8 + Get-Acl <<<< 'text[[]text.txt' [Folder] Get-Acl 'text[[]0[]].txt' Get-Acl : Cannot find path 'D:\Users\_Shared Documents\Folder\text[0].txt' because it does not exist. At line:1 char:8 + Get-Acl <<<< 'text[[]0[]].txt' [Folder] Get-Acl '\\Thor\_Shared Documents\Folder\text[[]text.txt' | ft -a Directory: Microsoft.PowerShell.Core\FileSystem::\\Thor\_Shared Documents\Folder Path Owner Access ---- ----- ------ text[text.txt Thor\vPodans Everyone Allow FullControl... [Folder] Get-Acl '\\Thor\_Shared Documents\Folder\text[[]0[]].txt' | ft -a Directory: Microsoft.PowerShell.Core\FileSystem::\\Thor\_Shared Documents\Folder Path Owner Access ---- ----- ------ text[0].txt Thor\vPodans Everyone Allow FullControl... [Folder]

В первых двух командах я поместил квадратную скобку в подстановочный шаблон (заключил во внешние квадратные скобки) для локальных путей, а во втором - сделал то же самое, но для UNC пути. При этом, типы объектов не изменились:

[Folder] (Get-Item '\\thor\_Shared Documents\Folder\text[[]text.txt').gettype().fullname System.IO.FileInfo [Folder] (Get-Acl '\\thor\_Shared Documents\Folder\text[[]text.txt').gettype().fullname System.Security.AccessControl.FileSecurity [Folder]

Следовательно, моё предположение было неверным. Вполне возможно, что это баг командлетов, поэтому я на всякий случай запостил проблему на Connect'е:

 https://connect.microsoft.com/feedback/ViewFeedback.aspx?FeedbackID=386138&SiteID=99

Желающие могут перейти по ссылке и подтвердить данную ошибку. Всем спасибо за участие :)

Tuesday, December 02, 2008 2:28:52 PM (FLE Standard Time, UTC+02:00)   Comments [0]    

 

 · 
All content © 2008 - 2010, Vadims Podāns
"Spaces" Theme provided by: Vadims Podāns
About


E-mail - Send mail to the author(s)
Live Messenger -
My former blog -
For english language visitors

Translate via Google Translator

Библиотека
Календарик
<March 2010>
SunMonTueWedThuFriSat
28123456
78910111213
14151617181920
21222324252627
28293031123
45678910

Карта расположения посетителей
Favorites

Домашняя страничка Теры Патрик

Disclaimer
Вся информация на сайте предоставляется на условиях «как есть», без предоставления каких-либо гарантий и прав.

При использовании материалов c данного сайта ссылка на оригинальный источник обязательна.