Windows Server для самых маленьких. Часть 22: Как работает доверие между лесами?

Все части серии «Windows Server для самых маленьких»

В прошлой части доверие между доменами одного леса появлялось само, было двусторонним и транзитивным, и его нельзя было ни настроить, ни отключить. Между лесами всё наоборот: доверия нет, пока его не создали, у него есть направление, тип, список суффиксов, фильтры и режимы, и каждый из этих параметров — ответ на вопрос «что чужой лес сможет делать в моём». Партнёрский лес в этой части — partner.example.net: компания, с которой нужно делить ресурсы, не сливая каталоги.

Все команды ниже выполняются с рабочей станции администратора ws1 в PowerShell 7 с RSAT (часть 15); то, что работает только на самом контроллере, идёт через Invoke-Command (часть 14). Наш лес — corp.example.com с контроллерами dc1, dc2, dc3 и dc4, PDC — dc1; у партнёра три контроллера: dc1, dc2 и dc3.partner.example.net. Между лесами уже есть двустороннее доверие, и всё, что его читает, показано с настоящим выводом. Команды, которые доверие создают или меняют, приведены по документации, без вывода.


Типы, направление, транзитивность

Направление. Доверие — отношение между двумя сторонами, и оно одностороннее по определению: доверяющий домен (trusting, на стороне ресурсов) принимает удостоверения пользователей доверенного (trusted, на стороне учётных записей). «Corp доверяет partner» означает: пользователи partner могут получать доступ к ресурсам corp, но не наоборот. Со стороны corp это исходящее доверие (outgoing: мы доверяем им), со стороны partner — входящее (incoming: нам доверяют). Двустороннее доверие — два односторонних.

Транзитивность. Транзитивное доверие распространяется дальше двух сторон: доверяя лесу partner, мы доверяем всем его доменам. Нетранзитивное — только одному домену, с которым создано.

Типы:

Тип С кем Транзитивно Аутентификация Когда
Между лесами (forest) корневой домен другого леса да, на все домены обоих лесов Kerberos и NTLM, маршрутизация суффиксов имён основной тип между двумя лесами AD
Внешнее (external) один домен другого леса (или домен NT 4.0) нет Kerberos и NTLM, только в этот домен, без маршрутизации суффиксов когда нужен один домен, а не лес
Realm realm Kerberos не от Microsoft (MIT, Heimdal, FreeIPA) по выбору только Kerberos единый вход с Unix-инфраструктурой
Сокращающее (shortcut) домен своего леса да Kerberos и NTLM спрямить путь направляющих билетов в глубоком дереве

Напомню, что делают протоколы из таблицы. Kerberos (часть 6) — билеты: клиент получает у KDC своего домена билет на службу, а если служба в чужом домене — направляющий билет (referral TGT, krbtgt/<чужой домен>), зашифрованный общим ключом двух доменов, с которым идёт к KDC того домена. NTLM — вызов-ответ: сервер передаёт ответ клиента своему контроллеру, тот по защищённому каналу Netlogon — контроллеру домена пользователя (сквозная аутентификация).

Доверие между лесами требует функционального уровня леса не ниже Windows Server 2003 с обеих сторон, то есть любого современного. Оно создаётся только между корневыми доменами двух лесов и на третий лес не распространяется: если corp доверяет partner, а partner — third.example.org, то corp лесу third не доверяет. Дальше речь о доверии между лесами; внешнее отличается областью действия и строгостью фильтрации SID.


DNS

Прежде чем KDC corp выдаст направляющий билет в partner, клиент должен найти контроллеры partner; прежде чем контроллер corp создаст доверие, он должен найти контроллеры partner, и наоборот. Каждый лес должен разрешать имена другого — не только partner.example.net, но и SRV-записи _msdcs.partner.example.net, по которым Locator находит контроллеры (часть 7).

Обычный способ — условный форвардер, хранимый в каталоге и реплицируемый на все DNS-серверы леса:

PS C:\> Add-DnsServerConditionalForwarderZone -ComputerName dc1 -Name partner.example.net -MasterServers 198.51.100.10, 198.51.100.11 -ReplicationScope Forest

Другой вариант, который тоже работает, — stub-зона: она хранит только записи SOA и NS зоны партнёра с адресами его DNS-серверов и сама обновляет их с этих серверов, поэтому при замене DNS-серверов партнёра список у нас меняется сам. Так выглядит stub-зона, хранимая в каталоге и реплицируемая на весь лес:

PS C:\> Get-DnsServerZone -CimSession dc1 -Name partner.example.net | Format-Table ZoneName, ZoneType, IsDsIntegrated, ReplicationScope, MasterServers -AutoSize

ZoneName            ZoneType IsDsIntegrated ReplicationScope MasterServers
--------            -------- -------------- ---------------- -------------
partner.example.net Stub               True Forest           {198.51.100.10, 198.51.100.11}

Создаётся она Add-DnsServerStubZone -Name partner.example.net -MasterServers … -ReplicationScope Forest. Проверка с рабочей станции:

PS C:\> Resolve-DnsName _ldap._tcp.dc._msdcs.partner.example.net -Type SRV | Where-Object Type -eq SRV | Select-Object NameTarget, Port

NameTarget              Port
----------              ----
dc2.partner.example.net  389
dc1.partner.example.net  389
dc3.partner.example.net  389

PS C:\> Resolve-DnsName dc1.partner.example.net | Select-Object IPAddress

IPAddress
---------
198.51.100.10

Без Where-Object Type -eq SRV в таблице появятся строки с пустым NameTarget: это записи A контроллеров из дополнительной секции ответа, у которых такого поля нет.

Партнёр делает то же самое для corp.example.com. Кроме DNS нужны время в пределах пяти минут между лесами (у Kerberos нет исключений для чужих realm, часть 12) и сеть: между контроллерами обоих лесов в обе стороны — LDAP 389, SMB 445, Kerberos 88, сопоставитель конечных точек RPC 135 и динамические порты RPC (через них идут LSA при создании доверия и Netlogon), от клиентов каждого леса к контроллерам другого — Kerberos 88 и LDAP 389.


Создание

В модуле ActiveDirectory из командлетов доверия есть только один:

PS C:\> Get-Command -Module ActiveDirectory *Trust*

CommandType Name
----------- ----
     Cmdlet Get-ADTrust

New-ADTrust и Set-ADTrust нет. netdom trust, по документации, доверие между двумя лесами AD создать не может: им создают внешние доверия и доверия с realm, а у существующих — проверяют, сбрасывают ключ и меняют флаги. Доверие между лесами создают оснасткой «Active Directory Domains and Trusts» или классом .NET System.DirectoryServices.ActiveDirectory.Forest, который модуль не обернул. Если есть учётные данные администратора партнёра, доверие создаётся с обеих сторон одной командой, и пароль доверия обе стороны получают случайный:

PS C:\> $local  = [System.DirectoryServices.ActiveDirectory.Forest]::GetCurrentForest()
PS C:\> $cred   = Get-Credential 'PARTNER\admin'
PS C:\> $ctx    = New-Object System.DirectoryServices.ActiveDirectory.DirectoryContext('Forest', 'partner.example.net', $cred.UserName, $cred.GetNetworkCredential().Password)
PS C:\> $remote = [System.DirectoryServices.ActiveDirectory.Forest]::GetForest($ctx)
PS C:\> $local.CreateTrustRelationship($remote, 'Bidirectional')

TrustDirection принимает Inbound, Outbound, Bidirectional — с точки зрения своего леса. Обычный случай с партнёром, у которого свои администраторы и чьи учётные данные нам не дают, — по одной стороне, с общим паролем, который вводят оба администратора:

PS C:\> $local.CreateLocalSideOfTrustRelationship('partner.example.net', 'Outbound', (Read-Host 'Trust password'))

и у партнёра — CreateLocalSideOfTrustRelationship('corp.example.com', 'Inbound', <тот же пароль>). Этот пароль нужен только для установки доверия: дальше его меняет PDC доверяющей стороны раз в 30 дней (подробнее — в разделе о ключе доверия). Создавать доверие могут Domain Admins корневого домена или Enterprise Admins — с каждой стороны свои.

Внешнее доверие к одному домену партнёра создаётся netdom:

PS C:\> netdom trust corp.example.com /domain:partner.example.net /add /twoway /userD:PARTNER\admin /passwordD:* /userO:CORP\alice.admin /passwordO:*

Готовое доверие читает тот же класс:

PS C:\> $local.GetTrustRelationship('partner.example.net')

TopLevelNames            : {partner.example.net}
ExcludedTopLevelNames    : {}
TrustedDomainInformation : {partner.example.net}
SourceName               : corp.example.com
TargetName               : partner.example.net
TrustType                : Forest
TrustDirection           : Bidirectional

PS C:\> $local.GetSidFilteringStatus('partner.example.net')
True
PS C:\> $local.GetSelectiveAuthenticationStatus('partner.example.net')
False

Фильтрация SID включена, выборочная аутентификация — нет; что это значит, ниже. Тот же взгляд из модуля:

PS C:\> Get-ADTrust -Filter * | Format-Table Name, Direction, TrustType, ForestTransitive, SIDFilteringQuarantined, SIDFilteringForestAware, SelectiveAuthentication, TGTDelegation -AutoSize

Name                    Direction TrustType ForestTransitive SIDFilteringQuarantined SIDFilteringForestAware SelectiveAuthentication TGTDelegation
----                    --------- --------- ---------------- ----------------------- ----------------------- ----------------------- -------------
partner.example.net BiDirectional   Uplevel             True                   False                   False                   False         False

Здесь легко ошибиться: SIDFilteringForestAware: False не означает, что фильтрации нет. Это свойство показывает бит TREAT_AS_EXTERNAL (о нём ниже), который фильтрацию у доверия между лесами ослабляет; False — фильтрация полная, и GetSidFilteringStatus выше честно отвечает True. SIDFilteringQuarantined — бит карантина, который ставят внешним довериям, у доверия между лесами он False. TGTDelegation: False — делегирование TGT через доверие явно не разрешено, то есть запрещено.

Netlogon видит доверие так:

PS C:\> nltest /trusted_domains
List of domain trusts:
    0: PARTNER partner.example.net (NT 5) (Direct Outbound) (Direct Inbound) ( Attr: foresttrans )
    1: CORP corp.example.com (NT 5) (Forest Tree Root) (Primary Domain) (Native)
The command completed successfully

nltest /domain_trusts /all_trusts /v выдаёт тот же список и добавляет SID и GUID доменов. Защищённый канал к домену партнёра держит каждый контроллер, и смотреть его нужно на контроллере, а не на рабочей станции:

PS C:\> nltest /server:dc1 /sc_query:partner.example.net
Flags: 30 HAS_IP  HAS_TIMESERV  Authentication Service: Netlogon
Trusted DC Name \\dc3.partner.example.net
Trusted DC Connection Status Status = 0 0x0 NERR_Success
The command completed successfully

PS C:\> nltest /sc_query:partner.example.net
I_NetLogonControl failed: Status = 1355 0x54b ERROR_NO_SUCH_DOMAIN

У рабочей станции защищённый канал только к своему домену, поэтому без /server: ответ — «нет такого домена». /sc_query только читает состояние канала (о нём — в части 18); nltest /sc_verify:partner.example.net и netdom trust corp.example.com /domain:partner.example.net /verify /kerberos по документации проверяют доверие активно и при неработающем канале пересоздают его, поэтому их запускают, когда что-то уже сломано.


Объект доверия

Каждое доверие — объект trustedDomain в CN=System, как в прошлой части, но с другими битами в trustAttributes. В скобках — свойства Get-ADTrust, которые модуль вычисляет из этих битов:

Бит Имя Что
0x1 NON_TRANSITIVE нетранзитивное (внешнее) (DisallowTransivity)
0x2 UPLEVEL_ONLY только для клиентов Windows 2000 и новее (UplevelOnly)
0x4 QUARANTINED_DOMAIN карантин: строгая фильтрация SID у внешнего доверия (SIDFilteringQuarantined)
0x8 FOREST_TRANSITIVE доверие между лесами (ForestTransitive)
0x10 CROSS_ORGANIZATION выборочная аутентификация (SelectiveAuthentication)
0x20 WITHIN_FOREST внутри леса (IntraForest)
0x40 TREAT_AS_EXTERNAL доверие между лесами, но фильтрация SID ослаблена до внешнего (SIDFilteringForestAware)
0x80 USES_RC4_ENCRYPTION realm-доверие с MIT Kerberos, умеющим RC4 (UsesRC4Encryption)
0x100 USES_AES_KEYS TGT через доверие шифруются AES (UsesAESKeys)
0x200 CROSS_ORGANIZATION_NO_TGT_DELEGATION делегирование TGT запрещено; после обновлений 2019 года вместо него действует 0x800
0x400 PIM_TRUST вместе с 0x40 — доверие с лесом привилегированного доступа (PIM), со своими правилами фильтрации SID
0x800 CROSS_ORGANIZATION_ENABLE_TGT_DELEGATION делегирование TGT явно разрешено (TGTDelegation)
0x1000 DISABLE_AUTH_TARGET_VALIDATION отключена проверка имени домена при сквозной аутентификации NTLM

PIM (Privileged Identity Management) — схема, в которой учётные записи администраторов живут в отдельном защищённом лесу, а в рабочем лесу получают права через такое доверие на время.

PS C:\> $t = Get-ADObject -Filter 'objectClass -eq "trustedDomain" -and Name -eq "partner.example.net"' -SearchBase 'CN=System,DC=corp,DC=example,DC=com' -Properties trustPartner, flatName, trustType, trustDirection, trustAttributes, msDS-SupportedEncryptionTypes
PS C:\> $t | Format-List trustPartner, flatName, trustType, trustDirection, trustAttributes, msDS-SupportedEncryptionTypes

trustPartner                  : partner.example.net
flatName                      : PARTNER
trustType                     : 2
trustDirection                : 3
trustAttributes               : 8
msDS-SupportedEncryptionTypes : 24

PS C:\> '0x{0:X}' -f $t.trustAttributes
0x8

trustType 2 — домен Active Directory (1 — домен NT 4.0, 3 — realm MIT); trustDirection 3 — двустороннее (1 — входящее, 2 — исходящее); trustAttributes 0x8 — только FOREST_TRANSITIVE. Бита NO_TGT_DELEGATION нет, и это нормально: обновления 2019 года не выставляют его, а меняют умолчание — без бита 0x800 пересланные TGT через доверие не ходят. msDS-SupportedEncryptionTypes 24 (0x18) — AES128 и AES256, о ключе доверия ниже. У доверия между лесами есть ещё двоичный атрибут msDS-TrustForestTrustInfo со списком суффиксов партнёра — следующий раздел.

Ключ доверия лежит в атрибутах trustAuthIncoming и trustAuthOutgoing этого объекта; читать их не нужно и не стоит, а у входящего доверия есть ещё парная учётная запись с именем домена партнёра и $ на конце:

PS C:\> Get-ADObject -LDAPFilter '(sAMAccountName=PARTNER$)' -Properties sAMAccountType, userAccountControl, pwdLastSet | Format-List DistinguishedName, @{n='sAMAccountType'; e={'0x{0:X}' -f $_.sAMAccountType}}, @{n='userAccountControl'; e={'0x{0:X}' -f $_.userAccountControl}}, @{n='pwdLastSet'; e={[datetime]::FromFileTime($_.pwdLastSet)}}

DistinguishedName  : CN=PARTNER$,CN=Users,DC=corp,DC=example,DC=com
sAMAccountType     : 0x30000002
userAccountControl : 0x820
pwdLastSet         : 28.10.2026 16:26:12

sAMAccountType 0x30000002 — учётная запись доверия, в userAccountControl 0x800 — INTERDOMAIN_TRUST_ACCOUNT (и 0x20, пароль не обязателен по политике). Удалять или переименовывать её руками нельзя: она часть доверия.


Суффиксы имён

Пользователь bob@partner.example.net вводит свой UPN на рабочей станции corp, или клиент corp просит билет на cifs/fs1.partner.example.net. Контроллер corp должен понять, в какой лес отправить запрос. Для этого у доверия между лесами есть маршрутизация суффиксов имён (name suffix routing): при создании доверия каждый лес собирает у партнёра его пространства имён — имена деревьев доменов, UPN- и SPN-суффиксы, SID доменов — и хранит их в msDS-TrustForestTrustInfo объекта доверия. Объекты доверия реплицируются в глобальный каталог (часть 10), и когда KDC не находит SPN в своём лесу, глобальный каталог сравнивает его суффикс со списками доверий и возвращает подсказку, в какой лес отправить клиента.

PS C:\> $local.GetTrustRelationship('partner.example.net').TopLevelNames

Name                 Status
----                 ------
partner.example.net Enabled

У нашего партнёра один уникальный суффикс — имя его леса; дочерние домены и имена вида *.partner.example.net маршрутизируются вместе с ним. Состояние суффикса — одно из четырёх: Enabled, NewlyCreated, AdminDisabled, ConflictDisabled. При создании доверия все уникальные суффиксы включены, а новый уникальный суффикс, который партнёр добавит потом (UPN-суффикс, новое дерево), появится после проверки доверия, но маршрутизироваться не будет, пока администратор доверяющей стороны его не включит.

Конфликт суффиксов возникает, когда то же DNS-имя, NetBIOS-имя или SID домена уже встречается в нашем лесу или у другого доверенного леса: тогда суффикс в новом доверии отключается, и маршрутизироваться в лес партнёра он не будет. Например, если corp использует альтернативный UPN-суффикс example.org, а у партнёра он тоже есть, то вход alice@example.org из corp к партнёру не уйдёт. Отсюда частая картина: вход по UPN через доверие не работает, а по PARTNER\bob работает. Список суффиксов в netdom:

PS C:\> netdom trust corp.example.com /namesuffixes:partner.example.net
PS C:\> netdom trust corp.example.com /namesuffixes:partner.example.net /togglesuffix:2

Первая команда нумерует суффиксы, вторая включает или выключает суффикс с этим номером. Нумерация может меняться, поэтому /togglesuffix запускают сразу после /namesuffixes, а суффикс в конфликте можно включить только после того, как его выключили в другом доверии. Отключённый суффикс — отключённый путь: имена под ним через доверие не маршрутизируются.


Фильтрация SID

Из прошлой части: внутри леса контроллеры не проверяют, откуда взялся SID в sIDHistory или в группах пользователя. Между лесами проверяют, и это называется фильтрацией SID (SID filtering): контроллер доверяющего леса, получив через доверие билет с PAC или ответ на сквозную аутентификацию NTLM, вычёркивает из списка SID те, что доверенный лес заявлять не вправе. У доверия между лесами пропускаются только SID доменов леса партнёра (тех, что записаны в msDS-TrustForestTrustInfo). SID из sIDHistory, указывающий на группу Enterprise Admins нашего леса, будет удалён, даже если администратор партнёра старательно его туда записал; удаляются и SID третьих лесов, и служебные SID вроде S-1-5-9 (Enterprise Domain Controllers): контроллеры чужого леса нашими контроллерами не становятся.

Каждый такой случай контроллер пишет в журнал Security событием 4675 «SIDs were filtered» (подкатегория аудита Logon). У нас оно встречается регулярно:

PS C:\> Invoke-Command -ComputerName dc3, dc4 -ScriptBlock {
    Get-WinEvent -FilterHashtable @{LogName='Security'; Id=4675} -ErrorAction SilentlyContinue |
        ForEach-Object { ([xml]$_.ToXml()).Event.EventData.Data | Where-Object Name -eq SidList | ForEach-Object { $_.'#text'.Trim() } } |
        Group-Object -NoElement
} | Format-Table PSComputerName, Count, Name -AutoSize

PSComputerName Count Name
-------------- ----- ----
dc3                8 %{S-1-5-9}
dc4               14 %{S-1-5-9}

Во всех событиях вычеркнут один SID — S-1-5-9, а учётная запись — компьютер из домена партнёра: контроллер партнёра обращался к нашему, и его принадлежность к Enterprise Domain Controllers своего леса у нас отброшена. Так фильтр и должен работать; событие 4675 с другими SID, особенно с SID наших групп, — повод выяснить, кто их туда записал.

Внешнее доверие фильтрует строже или мягче — смотря по биту QUARANTINED_DOMAIN. С карантином (netdom trust … /quarantine:Yes) проходят только SID самого доверенного домена; контроллеры, начиная с Windows Server 2003 и Windows 2000 SP4, ставят карантин всем новым внешним довериям сами.

Ослабить фильтрацию у доверия между лесами можно битом TREAT_AS_EXTERNAL: netdom trust corp.example.com /domain:partner.example.net /EnableSIDHistory:Yes. Тогда доверие фильтруется как внешнее, и SID из sIDHistory пользователей партнёра, созданные администраторами (RID от 1000), проходят. Встроенные привилегированные группы нашего леса — Domain Admins, Enterprise Admins и другие с RID меньше 1000 — отсекаются и в этом режиме, но любая наша собственная группа, например группа с правами администратора на серверах, становится доступна администратору партнёра: он может вписать её SID в sIDHistory своей учётной записи. Причина включать это одна — миграция пользователей из партнёрского леса с сохранением старых SID, чтобы права на ресурсы продолжали работать до переназначения. На время миграции и обратно (/EnableSIDHistory:No) — сразу после. Признак ослабленного доверия — SIDFilteringForestAware: True у Get-ADTrust.


Выборочная аутентификация

По умолчанию доверие между лесами даёт аутентификацию по всему лесу (forest-wide): любой пользователь partner может аутентифицироваться на любой машине corp. Прав на ресурсы у него от этого нет — но сервер, на котором он аутентифицировался, добавляет ему в токен Authenticated Users своего леса, а эта группа встречается в правах на общие папки, в правах по умолчанию на объекты каталога и в группе Pre-Windows 2000 Compatible Access, которой разрешено читать учётные записи. Партнёрский лес с сотней человек, которые ни в чём не виноваты, получает возможность читать наш каталог целиком.

Выборочная аутентификация (selective authentication) меняет умолчание: пользователь partner может аутентифицироваться только на тех машинах corp, на учётных записях которых ему или его группе явно выдано расширенное право Allowed to Authenticate. Включают её на доверяющей стороне, для исходящего доверия:

PS C:\> netdom trust corp.example.com /domain:partner.example.net /SelectiveAUTH:Yes

То же — $local.SetSelectiveAuthenticationStatus('partner.example.net', $true). Право выдаётся на объекте компьютера в каталоге, а не на самом сервере. Его GUID берётся из CN=Extended-Rights раздела конфигурации:

PS C:\> Get-ADObject -SearchBase "CN=Extended-Rights,$((Get-ADRootDSE).configurationNamingContext)" -LDAPFilter '(displayName=Allowed to Authenticate)' -Properties rightsGuid | Format-List Name, rightsGuid

Name       : Allowed-To-Authenticate
rightsGuid : 68B1D179-0D15-4d4f-AB71-46152E79A7BC
PS C:\> $acl = Get-Acl 'AD:\CN=FS1,OU=Servers,DC=corp,DC=example,DC=com'
PS C:\> $sid = (Get-ADGroup -Identity 'Partner Project Team' -Server partner.example.net).SID
PS C:\> $allowedToAuth = [Guid]'68b1d179-0d15-4d4f-ab71-46152e79a7bc'
PS C:\> $acl.AddAccessRule((New-Object System.DirectoryServices.ActiveDirectoryAccessRule($sid, 'ExtendedRight', 'Allow', $allowedToAuth)))
PS C:\> Set-Acl 'AD:\CN=FS1,OU=Servers,DC=corp,DC=example,DC=com' -AclObject $acl

Теперь группа партнёра аутентифицируется на fs1 и больше нигде; права на папки fs1 выдаются ей обычным образом, через доменно-локальную группу corp, как в следующей части. Если служба работает не от имени компьютера, а под доменной учётной записью (SQL Server под svc-sql), для Kerberos право выдаётся на этой учётной записи; для NTLM — всегда на компьютере.

Как это работает: контроллер корневого домена corp, получив запрос через доверие с выборочной аутентификацией, добавляет пользователю в данные авторизации SID S-1-5-1000 — Other Organization. Контроллер, выдающий сервисный билет на fs1, видит этот SID, проверяет право Allowed to Authenticate на объекте fs1 и без него билет не выдаёт. Свои пользователи и пользователи доверий без выборочной аутентификации получают на серверах вместо него S-1-5-15 — This Organization:

PS C:\> whoami /groups /fo csv | ConvertFrom-Csv | Where-Object 'Group Name' -match 'Organization' | Select-Object 'Group Name', SID

Group Name                     SID
----------                     ---
NT AUTHORITY\This Organization S-1-5-15

У пользователя партнёра при выборочной аутентификации на этом месте будет NT AUTHORITY\Other Organization — по нему права можно выдавать или запрещать отдельно.

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


Делегирование через доверие

Третий параметр безопасности — делегирование TGT. Неограниченное делегирование из части 6 пересылает TGT пользователя серверу; через доверие это означало бы, что сервер партнёра получает TGT нашего пользователя и может действовать от его имени где угодно у нас. После обновлений 2019 года (KB4490425) пересланные TGT через входящие доверия между лесами и внешние доверия по умолчанию не выдаются — и у новых, и у существующих доверий, без всякой настройки. Разрешает их только бит ENABLE_TGT_DELEGATION, который ставит netdom trust corp.example.com /domain:partner.example.net /EnableTGTDelegation:Yes, и Get-ADTrust показывает его как TGTDelegation: True. Клиент тоже знает о запрете: в klist у билетов в лес партнёра стоит флаг кеша DISABLE-TGT-DELEGATION (вывод — в «Смотрим руками»).

Если партнёрское приложение действительно требует неограниченного делегирования для пользователей другого леса — это повод переделать его на делегирование на основе ресурса, которое работает через доверие и TGT не пересылает. /EnableTGTDelegation:Yes стоит воспринимать как отключение защиты; у доверия с карантином включить его нельзя вовсе.


Ключ доверия и AES

Ключ доверия — общий пароль двух сторон, из которого получаются ключи Kerberos для направляющих билетов, и у него, как у любого принципала Kerberos, есть типы шифрования. Тип шифрования направляющего билета KDC выбирает по клиенту и доверию, а не по клиенту и службе. Исторически доверие поддерживало RC4, и клиент, которому оставили только AES, к службам в лесу партнёра не попадал: KDC отвечал KDC_ERR_ETYPE_NOSUPP. Документация здесь расходится: статья об этой ошибке говорит, что по умолчанию доверие поддерживает RC4, а описание события 4769 — что после обновлений для контроллеров и доверий AES поддерживается по умолчанию, пока атрибут не меняли вручную. Чтобы не зависеть от умолчаний, типы шифрования задают на объекте доверия явно, с обеих сторон — каждый администратор на своей:

PS C:\> ksetup /setenctypeattr partner.example.net AES128-CTS-HMAC-SHA1-96 AES256-CTS-HMAC-SHA1-96

ksetup работает с объектом доверия через локальную LSA контроллера, поэтому запускается на контроллере; с рабочей станции он отвечает ошибкой:

PS C:\> ksetup /getenctypeattr partner.example.net
Query of attributes on partner.example.net failed with 0xc0000034
Failed /GetEncTypeAttr : 0xc0000034

PS C:\> Invoke-Command -ComputerName dc1 -ScriptBlock { ksetup /getenctypeattr partner.example.net }
Enctypes for domain partner.example.net: AES128-CTS-HMAC-SHA1-96 AES256-CTS-HMAC-SHA1-96

То же — флажок «The other domain supports Kerberos AES Encryption» в свойствах доверия, а в каталоге — значение 24 (0x18) в msDS-SupportedEncryptionTypes объекта доверия, которое мы видели выше. Если клиенты ещё пользуются RC4, к списку добавляют RC4-HMAC-MD5.

Пароль доверия меняется сам: раз в 30 дней PDC доверяющей стороны (часть 17) создаёт новый, сохраняет прежний рядом и по защищённому каналу просит контроллер доверенной стороны записать новый у себя. У двустороннего доверия это происходит дважды, для каждого направления отдельно. Видно это по метаданным репликации объекта доверия:

PS C:\> Get-ADReplicationAttributeMetadata -Object $t -Server dc1 -Properties trustAuthIncoming, trustAuthOutgoing, msDS-SupportedEncryptionTypes, trustAttributes | Format-Table AttributeName, LastOriginatingChangeTime, Version -AutoSize

AttributeName                 LastOriginatingChangeTime Version
-------------                 ------------------------- -------
msDS-SupportedEncryptionTypes 09.09.2026 15:21:17             1
trustAttributes               20.12.2022 19:33:09             1
trustAuthOutgoing             28.10.2026 16:26:12            47
trustAuthIncoming             24.10.2026 19:51:04            47

Доверию почти четыре года, и у каждого ключа 47-я версия — по смене в месяц. Исходящий ключ (мы доверяем партнёру, PDC — наш) и входящий (меняет PDC партнёра) обновились в разные дни. trustAttributes с создания не менялся ни разу, а AES включили позже — это видно по дате у msDS-SupportedEncryptionTypes. Значения самих ключей командлет не показывает: метаданные — это только кто, когда и какая версия.


Realm

Доверие с realm Kerberos без Active Directory — MIT Kerberos или Heimdal на Linux, FreeIPA (каталог с Kerberos для Linux) — создаётся netdom как обмен общим ключом:

PS C:\> netdom trust corp.example.com /domain:EXAMPLE.ORG /add /realm /passwordT:*
PS C:\> ksetup /addkdc EXAMPLE.ORG kdc1.example.org
PS C:\> ksetup /setenctypeattr EXAMPLE.ORG AES256-CTS-HMAC-SHA1-96

На стороне MIT — kadmin addprinc krbtgt/CORP.EXAMPLE.COM@EXAMPLE.ORG с тем же паролем. Пользователи realm не существуют в каталоге, и чтобы alice@EXAMPLE.ORG могла открыть папку в corp, её принципал сопоставляют с учётной записью: Set-ADUser alice-ext -Add @{ altSecurityIdentities = 'Kerberos:alice@EXAMPLE.ORG' }. Чужой KDC не кладёт в билет PAC, групп из другого realm нет, NTLM через такое доверие не ходит — только Kerberos и только то, что сопоставлено. В части 30 к этому механизму мы вернёмся с Linux-стороны.


Обслуживание

Если смена пароля доверия не дошла до партнёра (сеть лежала, защищённый канал не поднялся), доверяющий контроллер продолжает пользоваться старым паролем и через 15 минут пробует снова; ломается доверие, когда у одной стороны не остаётся ни нового, ни прежнего пароля другой. Тогда у пользователей — ошибка 1788 «The trust relationship between the primary domain and the trusted domain failed.», а в журнале System контроллеров — события Netlogon: 5719 («This computer was not able to set up a secure session with a domain controller in domain …») у того, кто не может поднять защищённый канал, и 5805 или 5722 («The session setup from the computer … failed to authenticate») у того, к кому он не смог подключиться.

Проверка и починка — по документации: netdom trust corp.example.com /domain:partner.example.net /verify и /reset с учётными данными обеих сторон (/userD, /passwordD, /userO, /passwordO), у доверия между лесами — ещё $local.RepairTrustRelationship($remote). Без учётных данных партнёра каждая сторона задаёт общий пароль заново у себя: $local.UpdateLocalSideOfTrustRelationship('partner.example.net', <новый пароль>) у нас и то же у партнёра.

Удаление — $local.DeleteTrustRelationship($remote) (обе стороны) или netdom trust corp.example.com /domain:partner.example.net /remove. Если партнёр уже удалил свою сторону и у нас остался объект trustedDomain, его убирает $local.DeleteLocalSideOfTrustRelationship('partner.example.net') или netdom … /remove /oneside:trusting.


Смотрим руками

Путь билета через доверие, глазами клиента corp, которому нужна шара на контроллере партнёра:

PS C:\> klist get cifs/dc1.partner.example.net | Select-String 'retrieved'
A ticket to cifs/dc1.partner.example.net has been retrieved successfully.

PS C:\> klist | Select-String 'Server: (krbtgt/PARTNER|cifs/dc1.partner)' -Context 1,9
  #0>   Client: alice.admin @ CORP.EXAMPLE.COM
>       Server: krbtgt/PARTNER.EXAMPLE.NET @ CORP.EXAMPLE.COM
        KerbTicket Encryption Type: AES-256-CTS-HMAC-SHA1-96
        Ticket Flags 0x40a10000 -> forwardable renewable pre_authent name_canonicalize
        Start Time: 10/29/2026 15:03:45 (local)
        End Time:   10/30/2026 1:03:45 (local)
        Renew Time: 11/5/2026 13:28:12 (local)
        Session Key Type: AES-256-CTS-HMAC-SHA1-96
        Cache Flags: 0x200 -> DISABLE-TGT-DELEGATION
        Kdc Called: dc1.corp.example.com

  #3>   Client: alice.admin @ CORP.EXAMPLE.COM
>       Server: cifs/dc1.partner.example.net @ PARTNER.EXAMPLE.NET
        KerbTicket Encryption Type: AES-256-CTS-HMAC-SHA1-96
        Ticket Flags 0x40a50000 -> forwardable renewable pre_authent ok_as_delegate name_canonicalize
        Start Time: 10/29/2026 15:03:45 (local)
        End Time:   10/30/2026 1:03:45 (local)
        Renew Time: 11/5/2026 13:28:12 (local)
        Session Key Type: AES-256-CTS-HMAC-SHA1-96
        Cache Flags: 0x200 -> DISABLE-TGT-DELEGATION
        Kdc Called: dc3.partner.example.net

klist get сам печатает весь кеш после строки об успехе, поэтому нужные билеты выбраны отдельно. Направляющий билет krbtgt/PARTNER.EXAMPLE.NET выдан нашим KDC (@ CORP.EXAMPLE.COM, Kdc Called: dc1) и зашифрован ключом доверия — AES-256, потому что на доверии включён AES. Сервисный билет выдал уже KDC партнёра (dc3.partner.example.net), и в нём флаг ok_as_delegate — служба партнёра доверена для делегирования. Но у обоих билетов флаг кеша DISABLE-TGT-DELEGATION: свой TGT клиент службам партнёра пересылать не станет.

На нашем контроллере этот запрос — событие 4769, где служба — сам домен партнёра:

PS C:\> Invoke-Command -ComputerName dc1 -ScriptBlock {
    $e = Get-WinEvent -FilterHashtable @{LogName='Security'; Id=4769; StartTime=(Get-Date).AddMinutes(-60)} |
        Where-Object { $_.Properties[2].Value -eq 'PARTNER.EXAMPLE.NET' } | Select-Object -First 1
    $d = [ordered]@{}; ([xml]$e.ToXml()).Event.EventData.Data | ForEach-Object { $d[$_.Name] = $_.'#text' }
    'ServiceName','TicketEncryptionType','Status','ServiceSupportedEncryptionTypes','ServiceAvailableKeys' | ForEach-Object { '{0,-32}: {1}' -f $_, $d[$_] }
}
ServiceName                     : PARTNER.EXAMPLE.NET
TicketEncryptionType            : 0x12
Status                          : 0x0
ServiceSupportedEncryptionTypes : 0x18 (AES128-SHA96, AES256-SHA96)
ServiceAvailableKeys            : AES128-SHA96, AES256-SHA96

0x12 — AES256, 0x18 — типы, разрешённые на доверии, и ключи AES у него есть. Если бы на доверии остался только RC4, здесь было бы 0x17, а у клиента без RC4 — отказ.

Все доверия леса с их параметрами, одной таблицей:

PS C:\> Get-ADTrust -Filter * -Properties * | Format-Table Name, Direction, ForestTransitive, SelectiveAuthentication, SIDFilteringForestAware, TGTDelegation, @{n='EncTypes'; e={$_.'msDS-SupportedEncryptionTypes'}}, Created -AutoSize

Name                    Direction ForestTransitive SelectiveAuthentication SIDFilteringForestAware TGTDelegation EncTypes Created
----                    --------- ---------------- ----------------------- ----------------------- ------------- -------- -------
partner.example.net BiDirectional             True                   False                   False         False       24 20.12.2022 19:33:09

В лесу с дочерними доменами здесь будут и доверия внутри леса — их отличает IntraForest: True. Эту таблицу стоит получать раз в месяц: доверие, у которого SIDFilteringForestAware или TGTDelegation стали True, у внешнего доверия SIDFilteringQuarantined стал False, а у партнёрского пропала выборочная аутентификация, — вопрос к тому, кто его менял.


Источники

Доверия:

Спецификации и схема:

Команды:


Доверие создано, фильтры включены, суффиксы маршрутизируются. Пользователь партнёра может аутентифицироваться на fs1 — но прав у него там по-прежнему нет, и выдать их не так просто, как кажется.

Дальше — часть 23: доступ через доверие на практике. Какие группы принимают членов из чужого леса, а какие нет, и почему встроенная группа Administrators не примет доменно-локальную; схема выдачи прав на файловые ресурсы, на серверы, на SQL Server и RDP пользователям другого леса; как выглядят чужие пользователи в каталоге — foreignSecurityPrincipals; и что проверять, когда «доступ есть, а не пускает».

Оставить комментарий могут только зарегистрированные пользователи.

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