Posts on this page:
Читая различные обзоры нововведений в Windows Vista/Windows Server 2008 ловлю себя на мысли, что по сути, кроме маркетинговых сообщений ничего нету. Повальная виртуализация, мифический NAP и многое другое (опять же, сильно отдаёт маркетингом). Хочется спросить человека "а покажите, как вы это реализовали на практике, с какими столкнулись трудностями и прочее". А вот с этим у нас всё несколько труднее, т.к. одних Get The Fucacts будет маловато для технических специалистов (на что в последнее время очень часто сетует Павел Нагаев и я с ним в этом плане согласен). Поэтому как часть программы по техническому образованию специалистов хочу постараться разобрать одно интересное нововведение в Windows Server 2008 - хранение EFS сертификатов на смарт-картах с использованием новой опции групповых политик. Тема (на сколько я посмотрел) не освещена совсем, кроме как небольших статей на TechNet'e. Разбираемый вопрос для многих, к сожалению, останется неактуальным по причине дороговизны решения - смарт-карты - не самое дешёвое удовольствие, но я заметил, что интерес к данному вопросу всё же есть.
Итак, для начала я хотел бы рассказать про общий механизм работы шифрующей файловой системы EFS.
Когда пользователь выставляет галочку на чек-боксе Encrypt contents to secure data и нажимает Ok, то происходит несколько вещей:
Процес шифрования:
Таким образом гарантируется, что доступ к файлу имеет только владелец закрытого ключа пользователя и агент восстановления. В Windows Server 2003 появилась возможность предоставлять шифрованные файлы в общий доступ с другими пользователями. При этом с самим файлом ничего не происходит, а только ключ FEK шифруется открытыми ключами сертификатов тех пользователей, которым разрешён доступ к шифрованному файлу. Именно по этой причине невозможно предоставлять доступ к шифрованному файлу группам - группы не могут иметь сертификатов, а только Security Principals (конкретные участники безопасности, как пользователи и компьютеры). А так же эти пользователи уже должны иметь в базе Active Directory EFS-пригодные сертификаты. Когда я только начал изучать EFS, то не совсем понимал этих требований, пока не начал изучать вопрос более детально и понимать т.н. физику процесса.
Более интересный вариант - шифрование файла в сетевой папке.
В качестве альтернативного варианта был предложен WebDAV, который позволял использовать защищённый SSL канал при передаче файла. Хотя SSL не обязателен, поскольку любые операции шифрования/расшифрования происходили на клиенте локально. При доступе к шифрованному файлу, он в таком же виде пересылался по сети клиенту, где последний локально расшифровывал файл. После работы файл заново локально шифровался и уже в шифрованном виде передавался по сети.
При использовании клиента Windows Vista и сервера Windows Server 2008, то используется механизм, который реализован в WebDAV, а именно - файл шифруется и расшифровывается локально на клиенте и файл по сети передаётся в шифрованном виде как есть. Но, тем не менее, этот механизм не решает вопрос доступа с различных машин к шифрованному файлу. Дело в том, что при логоне пользователя на рабочую станцию ему не подгружаются сертификаты, которые были использованы на других машинах, они никогда не покидают профиль пользователя. Если в домене применён Certificate Autoenrollment, то автоматически запрашивается новый сертификат с новой парой открытый/закрытый ключ и который непригоден для расшифрования ранее зашифрованных файлов. Для этой задачи существует 2 решения:
Процесс дешифрования (расшифровки) файла:
При обращении пользователя к файлу 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), но тем не менее, я хочу подвести плавно тему к целевой задаче использования смарт-карт и задач, которые мы сможем решать с их помощью. Ну и для себя немного прояснить материал.
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 исключения для действия по умолчанию:
Это исключения для папок Windows и Program Files. Эти правила вполне обоснованы, поскольку обычные пользователи не имеют права записи ни в папку Windows, ни в Program Files. При этом видно, что третье правило использует обратный слеш! По всей видимости разработчики Microsoft сами себя ввели в заблуждение? Вполне возможно. Далее мы видим, что первое правило перекрывает второе и третье! Можно сказать, что эти правила лишние и достаточно первого. Действительно, если взять систему с Windows Vista или Windows Server 2008, то мы увидим только 2 исключения по умолчанию:
Но на самом деле всё не так и просто. Я бы в жизни не догадался об этом, если бы не Александр Станкевич (MVP Enterprise Security)! Я так и не смог найти внятного объяснения этому факту:
Полагаю, что комментировать тут нечего, на картинке всё и так видно, к чему я клоню ;) Вам мало? Получите ещё:
Вобщем, подумайте, господа администраторы - а вы точно хорошо разобрались в вопросе Software Restriction Policies? У вас ещё есть время подумать, пока ваши пользователи читают эту статью :-D
В предыдущей части мы рассмотрели возможности управления журналами событий средствами .NET Framework, а так же рассмотрели проблематику использования .NET (из моего предыдущего блога) - Странности Get-Eventlog и не совсем богатый функционал. В этом посте мы разберём данный вопрос, но с использованием WMI и все примеры будут выполняться системе под управлением Windows Vista.
В WMI за журнал событий отвечают следующие классы:
WMI, как и .NET поддерживает удалённую работу с использованием ключа -ComputerName, поэтому на этом заострять внимание не будем. За публикацию списка журналов в системе отвечает класс Win32_NTEventlogFile:
В продолжении темы работы с журналом собитый (EventLog) хочу немного рассказать об удалённой работе с EventLog и основными задачами управления. При этом хочу отметить, что у нас есть 2 различных механизма управления:
Но в зависимости от архитектуры ОС оба метода обладают различными характеристиками по времени работы, отображению событий и нагрузке системы. Забегая вперёд скажу, что использование чистого .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.
Снова навеяно темой на форуме TechNet-Ru. Уже не первый раз встречаю топики про архивирование журналов событий для последующего хранения в оффлайне. Безусловно не стоит пытаться скопировать .evt файл из папки Windows, поскольку файлы открыты и заблокированы (как и .pst файлы при запущенном MS Outlook). Для архивирования журнала скриптом нужно либо использовать Volume Shadow Copy либо использовать особые методы (например, вот так: http://support.microsoft.com/kb/312571). В качестве особых методов можно так же выделить использование WMI, которое позволяет решить поставленную задачу и добавит нам удалённой управляемости.
Полностью опираться на тему форума нельзя, ибо задача поставлена некорректно. Смысла в ежедневном удалении логов с машин без предварительного архивирования нету совсем (иначе вы потеряете точку отправления в решении проблемы, когда она возникнет и зачастую единственным выходом будет переинсталляция или восстановление из бакупа). Поэтому немного переформулируем задачу и напишем решение.
Задача: проводить с некоторой периодичностью архивацию журнала событий в файл. Убедиться, что бэкап сделан и после этого очистить журналы. В противном случае сделать что-нибудь другое.
За отправную точку возьмём класс Win32_NTEventlogFile. Давайте, вызовем его: