AD Connector先决条件 - AWS Directory Service
AWS 文档中描述的 AWS 服务或功能可能因区域而异。要查看适用于中国区域的差异,请参阅中国的 AWS 服务入门

本文属于机器翻译版本。若本译文内容与英语原文存在差异,则一律以英文原文为准。

AD Connector先决条件

要使用 AD Connector 连接到您的现有目录,您需要:

VPC

对 VPC 进行如下设置:

  • 至少两个子网。每个子网必须位于不同的可用区。

  • 必须通过虚拟专用网络 (VPN) 连接或 将 VPC 连接到您的现有网络。AWS Direct Connect.

  • VPC 必须具有默认硬件租户。

AWS Directory Service 使用双 VPC 结构。构成目录的 EC2 实例在您的 AWS 账户外部运行,并由 AWS 管理。它们有两个网络适配器 ETH0ETH1ETH0 是管理适配器,存在于在您的账户之外。ETH1 是在您的账户中创建的。

可以通过编程方式选择目录的 ETH0 网络的管理 IP 范围,以确保它与在其中部署目录的 VPC 不冲突。该 IP 范围可以是以下任一对(因为目录在两个子网中运行):

  • 10.0.1.0/24 与 10.0.2.0/24

  • 192.168.1.0/24 与 192.168.2.0/24

我们通过检查 ETH1 CIDR 的第一个八位字节来避免冲突。如果以 10 开头,我们选择一个 192.168.0.0/16 VPC,其中包含 192.168.1.0/24 和 192.168.2.0/24 子网。如果第一个八位字节不是 10,我们选择一个 10.0.0.0/16 VPC,其中包含 10.0.1.0/24 和 10.0.2.0/24 子网。

选择算法不包括 VPC 上的路由。因此,在这种情况下可能会导致 IP 路由冲突。

有关更多信息,请参阅 中的以下主题Amazon VPC 用户指南

有关 AWS Direct Connect 的更多信息,请参阅 AWS Direct Connect 用户指南.

现有 Active Directory

您将需要连接到具有 Active Directory 域的现有网络。

注意

AD Connector 不支持单标签域

此域的功能级别必须是 Windows Server 2003 或更高版本。AD Connector 还支持连接到 Amazon EC2 实例上托管的域。

注意

与 AD Connector 域加入功能结合使用时,Amazon EC2 不支持只读域控制器 (RODC)。

服务账户

您必须拥有现有目录中被委派了以下权限的服务账户的凭证:

  • 读取用户和组 – 必需

  • 将计算机加入域 - 仅当使用“无缝域加入”功能和 时是必需的Amazon WorkSpaces

  • 创建计算机对象 – 仅当使用无缝域加入和 时必需Amazon WorkSpaces

有关更多信息,请参阅向您的服务账户委派权限.

用户权限

所有 Active Directory 用户必须有权读取自己的属性。具体而言,包括以下属性:

  • GivenName

  • SurName

  • Mail

  • SamAccountName

  • UserPrincipalName

  • UserAccountControl

  • MemberOf

默认情况下,Active Directory 用户确实有权读取这些属性。但是,管理员可以随时修改这些权限,因此,您可能希望在设置 AD Connector 之前,验证用户是否具有这些读取权限。

IP 地址

获取您现有目录域中两个 DNS 服务器或域控制器的 IP 地址。

AD Connector 在连接到您的目录时将从这些服务器获取 _ldap._tcp.<DnsDomainName>_kerberos._tcp.<DnsDomainName> SRV 记录,因此这些服务器必须包含这些 SRV 记录。AD Connector 尝试查找将同时提供 LDAP 和 Kerberos 服务的公用域控制器,因此这些 SRV 记录必须至少包含一个公用域控制器。有关 SRV 记录的更多信息,请转到 Microsoft 上的 SRV 资源记录TechNet。

子网的端口

要AD Connector使 将目录请求重定向到现有的 Active Directory 域控制器,现有网络的防火墙必须对 中的两个子网CIDRs的 开放以下端口Amazon VPC。

  • TCP/UDP 53 - DNS

  • TCP/UDP 88 - Kerberos 身份验证

  • TCP/UDP 389 - LDAP

这些是 AD Connector 能够连接到目录所需的最少端口。根据您的特定配置,您可能需要打开其他端口。

注意

如果现有 Active Directory 域的 DNS 服务器或域控制器服务器位于 VPC 中,则与这些服务器关联的安全组必须对 VPC 中的两个子网CIDRs的 开放上述端口。

有关其他端口要求,请参阅 Microsoft 上的 AD 和 AD DS 端口要求TechNet。

Kerberos 预身份验证

用户账户必须启用 Kerberos 预身份验证。有关如何启用此设置的详细说明,请参阅确保已启用 Kerberos 预身份验证. 有关此设置的一般信息,请转到 Microsoft 上的预身份验证TechNet。

加密类型

当通过 Kerberos 对您的 Active Directory 域控制器进行身份验证时,AD Connector 支持以下加密类型:

  • AES-256-HMAC

  • AES-128-HMAC

  • RC4-HMAC

AWS Single Sign-On先决条件

如果计划将 AWS Single Sign-On (AWS SSO) 与 AD Connector 结合使用,则需要确保满足以下条件:

  • 您的 AD Connector 在AWS组织的 中设置管理账户。

  • 您的 AWS SSO 实例位于您在其中设置 AD Connector 的同一区域中。

有关更多信息,请参阅 中的 AWS SSO 先决条件AWS Single Sign-On 用户指南。

多重验证先决条件

为了使用您的 AD Connector 目录支持多重验证,您需要以下内容:

  • 现有网络中具有两个客户端终端节点的远程身份验证拨入用户服务 (RADIUS) 服务器。RADIUS 客户端终端节点具有以下要求:

    • 要创建终端节点,您需要 AWS Directory Service 服务器的 IP 地址。这些 IP 地址可以从目录详细信息的 Directory IP Address (目录 IP 地址) 字段获取。

    • 两个 RADIUS 终端节点必须使用相同的共享密码。

  • 您的现有网络必须允许通过默认的 RADIUS 服务器端口 (1812) 从 AWS Directory Service 服务器传入入站流量。

  • 您的 RADIUS 服务器与您的现有目录的用户名必须相同。

有关将 AD Connector 与 MFA 搭配使用的更多信息,请参阅 为 AD Connector 启用多重验证.

向您的服务账户委派权限

要连接到您的现有目录,您必须在现有目录中拥有被委派了某些权限的 AD Connector 服务账户的凭证。虽然 Domain Admins 组的成员有足够的权限连接到目录,但作为最佳实践,您应使用仅具有连接到目录所需的最小权限的服务账户。以下过程演示如何创建名为 Connectors 的新组,委派将 AWS Directory Service 连接到该组所需的必要权限,然后将新服务账户添加到该组。

此过程必须在已加入目录且已安装 Active Directory User and Computers MMC 管理单元的计算机上执行。您还必须以域管理员身份登录。

向您的服务账户委派权限

  1. 打开 Active Directory User and Computers (Active Directory 用户和计算机),然后在导航树中选择您的域根目录。

  2. 在左侧窗格的列表中,右键单击 Users,选择 New,然后选择 Group.

  3. New Object - Group 对话框中,输入以下内容,然后单击 OK.

    字段 值/选择
    组名 Connectors
    Group scope 全球
    Group type 安全
  4. Active Directory User and Computers 导航树中,选择您的域根目录。在菜单中,选择 Action,然后选择 Delegate Control. 如果您的 AD Connector 已连接到 AWS Managed Microsoft AD,您将无法在域根级别委派控制。在这种情况下,要委派控制权限,请选择目录 OU 下将创建计算机对象的 OU。

  5. Delegation of Control Wizard 页面上,单击 Next,然后单击 Add.

  6. Select Users, Computers, or Groups 对话框中,输入 Connectors,然后单击 OK. 如果找到多个对象,请选择上面创建的 Connectors 组。单击 Next.

  7. Tasks to Delegate 页面上,选择 Create a custom task to delegate,然后选择 Next.

  8. 选择 Only the following objects in the folder,然后选择 Computer objectsUser objects.

  9. 选择 Create selected objects in this folder,然后选择 Delete selected objects in this folder. 然后选择 Next.

    
                            对象类型
  10. 选择 Read,然后选择 Next.

    注意

    如果您将使用无缝域加入或 Amazon WorkSpaces,则还必须启用写入权限,以便 AWS Managed Microsoft AD 可以创建计算机对象。

    
                            对象类型
  11. Completing the Delegation of Control Wizard 页面上验证信息,然后单击 Finish.

  12. 使用强密码创建一个用户账户,并将该用户添加到 Connectors 组。此用户将被称为您的 AD Connector 服务账户,由于它现在是 Connectors 组的成员,因此具有足够的权限将 AWS Directory Service 连接到目录。

测试 AD Connector

要AD Connector使 连接到现有目录,现有网络的防火墙必须对 VPC 中的两个子网CIDRs的 开放某些端口。要测试是否满足这些条件,请执行以下步骤:

测试 连接

  1. 在 VPC 中启动一个 Windows 实例并通过 RDP 连接它。实例必须是您现有域的成员。在该 VPC 实例上执行剩余步骤。

  2. 下载并解压缩DirectoryServicePortTest测试应用程序。其中包含源代码及 Visual Studio 项目文件,您可以根据需要修改该测试应用程序。

    注意

    Windows Server 2003 及更低版本的操作系统不支持此脚本。

  3. 在 Windows 命令提示符下,使用以下选项运行 DirectoryServicePortTest 测试应用程序:

    注意

    仅当域和林功能级别设置为 Windows Server 2012 R2 及更低版本时,才能使用DirectoryServicePortTest测试应用程序。

    DirectoryServicePortTest.exe -d <domain_name> -ip <server_IP_address> -tcp "53,88,389" -udp "53,88,389"
    <domain_name>

    完全限定域名。这可用于测试林和域功能级别。如果不指定域名,则不测试功能级别。

    <server_IP_address>

    现有域中域控制器的 IP 地址。将针对该 IP 地址来测试端口。如果不指定 IP 地址,则不测试端口。

    此测试应用程序确定是否打开了从 VPC 到域的必要端口,并验证最低的林和域功能级别。

    该输出值将类似于以下内容:

    Testing forest functional level. Forest Functional Level = Windows2008R2Forest : PASSED Testing domain functional level. Domain Functional Level = Windows2008R2Domain : PASSED Testing required TCP ports to <server_IP_address>: Checking TCP port 53: PASSED Checking TCP port 88: PASSED Checking TCP port 389: PASSED Testing required UDP ports to <server_IP_address>: Checking UDP port 53: PASSED Checking UDP port 88: PASSED Checking UDP port 389: PASSED

以下是 DirectoryServicePortTest 应用程序的源代码。

/* Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. This file is licensed under the Apache License, Version 2.0 (the "License"). You may not use this file except in compliance with the License. A copy of the License is located at http://aws.amazon.com/apache2.0/ This file is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Net; using System.Net.Sockets; using System.Text; using System.Threading.Tasks; using System.DirectoryServices.ActiveDirectory; using System.Threading; using System.DirectoryServices.AccountManagement; using System.DirectoryServices; using System.Security.Authentication; using System.Security.AccessControl; using System.Security.Principal; namespace DirectoryServicePortTest { class Program { private static List<int> _tcpPorts; private static List<int> _udpPorts; private static string _domain = ""; private static IPAddress _ipAddr = null; static void Main(string[] args) { if (ParseArgs(args)) { try { if (_domain.Length > 0) { try { TestForestFunctionalLevel(); TestDomainFunctionalLevel(); } catch (ActiveDirectoryObjectNotFoundException) { Console.WriteLine("The domain {0} could not be found.\n", _domain); } } if (null != _ipAddr) { if (_tcpPorts.Count > 0) { TestTcpPorts(_tcpPorts); } if (_udpPorts.Count > 0) { TestUdpPorts(_udpPorts); } } } catch (AuthenticationException ex) { Console.WriteLine(ex.Message); } } else { PrintUsage(); } Console.Write("Press <enter> to continue."); Console.ReadLine(); } static void PrintUsage() { string currentApp = Path.GetFileName(System.Reflection.Assembly.GetExecutingAssembly().Location); Console.WriteLine("Usage: {0} \n-d <domain> \n-ip \"<server IP address>\" \n[-tcp \"<tcp_port1>,<tcp_port2>,etc\"] \n[-udp \"<udp_port1>,<udp_port2>,etc\"]", currentApp); } static bool ParseArgs(string[] args) { bool fReturn = false; string ipAddress = ""; try { _tcpPorts = new List<int>(); _udpPorts = new List<int>(); for (int i = 0; i < args.Length; i++) { string arg = args[i]; if ("-tcp" == arg | "/tcp" == arg) { i++; string portList = args[i]; _tcpPorts = ParsePortList(portList); } if ("-udp" == arg | "/udp" == arg) { i++; string portList = args[i]; _udpPorts = ParsePortList(portList); } if ("-d" == arg | "/d" == arg) { i++; _domain = args[i]; } if ("-ip" == arg | "/ip" == arg) { i++; ipAddress = args[i]; } } } catch (ArgumentOutOfRangeException) { return false; } if (_domain.Length > 0 || ipAddress.Length > 0) { fReturn = true; } if (ipAddress.Length > 0) { _ipAddr = IPAddress.Parse(ipAddress); } return fReturn; } static List<int> ParsePortList(string portList) { List<int> ports = new List<int>(); char[] separators = {',', ';', ':'}; string[] portStrings = portList.Split(separators); foreach (string portString in portStrings) { try { ports.Add(Convert.ToInt32(portString)); } catch (FormatException) { } } return ports; } static void TestForestFunctionalLevel() { Console.WriteLine("Testing forest functional level."); DirectoryContext dirContext = new DirectoryContext(DirectoryContextType.Forest, _domain, null, null); Forest forestContext = Forest.GetForest(dirContext); Console.Write("Forest Functional Level = {0} : ", forestContext.ForestMode); if (forestContext.ForestMode >= ForestMode.Windows2003Forest) { Console.WriteLine("PASSED"); } else { Console.WriteLine("FAILED"); } Console.WriteLine(); } static void TestDomainFunctionalLevel() { Console.WriteLine("Testing domain functional level."); DirectoryContext dirContext = new DirectoryContext(DirectoryContextType.Domain, _domain, null, null); Domain domainObject = Domain.GetDomain(dirContext); Console.Write("Domain Functional Level = {0} : ", domainObject.DomainMode); if (domainObject.DomainMode >= DomainMode.Windows2003Domain) { Console.WriteLine("PASSED"); } else { Console.WriteLine("FAILED"); } Console.WriteLine(); } static List<int> TestTcpPorts(List<int> portList) { Console.WriteLine("Testing TCP ports to {0}:", _ipAddr.ToString()); List<int> failedPorts = new List<int>(); foreach (int port in portList) { Console.Write("Checking TCP port {0}: ", port); TcpClient tcpClient = new TcpClient(); try { tcpClient.Connect(_ipAddr, port); tcpClient.Close(); Console.WriteLine("PASSED"); } catch (SocketException) { failedPorts.Add(port); Console.WriteLine("FAILED"); } } Console.WriteLine(); return failedPorts; } static List<int> TestUdpPorts(List<int> portList) { Console.WriteLine("Testing UDP ports to {0}:", _ipAddr.ToString()); List<int> failedPorts = new List<int>(); foreach (int port in portList) { Console.Write("Checking UDP port {0}: ", port); UdpClient udpClient = new UdpClient(); try { udpClient.Connect(_ipAddr, port); udpClient.Close(); Console.WriteLine("PASSED"); } catch (SocketException) { failedPorts.Add(port); Console.WriteLine("FAILED"); } } Console.WriteLine(); return failedPorts; } } }