LDAP 清单指南

本指南涵盖了有关此集合中包含的 LDAP 清单插件的信息。此清单插件可用于从 LDAP 服务器源(如 Microsoft Active Directory)构建清单。

连接信息

有关如何配置 LDAP 连接的详细信息,请参见LDAP 连接指南。插件文档中描述的每个连接选项都在清单 yml 配置文件中指定,如下所示。

plugin: microsoft.ad.ldap

# LDAP connection options can be defined in the yaml config.
auth_protocol: simple
username: UserName
password: MyPassword123
tls_mode: ldaps

属性

LDAP 清单插件可用于基于计算机对象的 LDAP 属性为检索到的每个主机设置自定义事实。检索自定义属性是通过清单插件定义中的 attributes 选项完成的。该值设置为以下三种类型之一

  • 空字符串或 null

  • 模板字符串

  • 字典

注意

虽然单个属性只能设置为这些类型之一,但可以对不同的属性使用不同的值类型。

也可以使用 compose 清单选项来使用清单插件提供的内置 compose 模板,但必须首先通过 attributes 选项请求 LDAP 属性,并在 compose 模板中通过 attributes 在其上设置的主机事实引用它们。

空字符串或 null

attributes:
  comment:
  objectSid: ''
  ms-Mcs-AdmPwd:

在这种情况下,每个属性值都将设置为主机事实,因为它们受 LDAP 架构强制转换,请参阅值类型和模板。每个事实的名称将基于属性名称,其中 - 替换为 _。在上面的示例中,主机事实 commentobjectSidms_Mcs_AdmPwd 将设置为强制转换的值。

# ansible-inventory -i microsoft.ad.ldap.yml --host MYHOST --vars --yaml

ansible_host: MYHOST.domain.com
comment: test comment
ms_Mcs_AdmPwd: Password123!
objectSid: S-1-5-21-1234-1108
microsoft_ad_distinguished_name: CN=MYHOST,CN=Computers,DC=domain,DC=com

模板字符串

attributes:
  comment: this
  objectSid: raw | microsoft.ad.as_sid
  ms-Mcs-AdmPwd: raw | first

此格式将根据指定的模板值设置主机事实。每个模板都隐式地用 {{ ... }} 包裹,并通过 Jinja2 处理以产生结果。这意味着模板字符串可以包含 Ansible 和其他集合提供的过滤器,以将原始 LDAP 值转换为更有用的值。 this 变量引用强制转换的 LDAP 属性值,raw 引用原始 LDAP 属性值的 base64 编码字节字符串列表。有关模板内部可以执行的操作的更多信息,请参阅值类型和模板。每个主机事实都将以属性名称命名,其中 - 替换为 _。在上面的示例中,主机事实 commandobjectSidms_Mcs_AdmPwd 将设置为模板结果。

# ansible-inventory -i microsoft.ad.ldap.yml --host MYHOST --vars --yaml

ansible_host: MYHOST.domain.com
comment: test comment
ms_Mcs_AdmPwd: UGFzc3dvcmQxMjMh
objectSid:
- S-1-5-21-1234-1108
microsoft_ad_distinguished_name: CN=MYHOST,CN=Computers,DC=domain,DC=com

字典

attributes:
  comment:
    # Jinja2 native types will automatically convert this to a dict as
    # the value is a json string.
    my_comment:
    other_var: this | from_json
  objectSid:
    sid: raw | microsoft.ad.as_sid | first
  ms-Mcs-AdmPwd:
    ansible_password: this

可以在每个属性值上设置的最终值是一个字典,其中键是要设置的主机事实,值是用于派生最终值的模板。它可以是 null 或空字符串,以引用该属性的 LDAP 强制转换值 (this),或者是一个模板字符串,用于根据手头的要求模板化新值。有关 null/空字符串与字符串模板值的更多信息,请参阅上面的两种格式。在上面的示例中,设置了 4 个主机事实

  • my_command - comment 属性的强制转换值

  • other_var - 如果 comment 是 json 字符串,则从其强制转换值创建的字典

  • sid - 从 objectSid 派生的计算机 SID 值(作为字符串)

  • ansible_password - 从 ms-Mcs-AdmPwd 派生的 LAPS 密码强制转换值

# ansible-inventory -i microsoft.ad.ldap.yml --host MYHOST --vars --yaml

ansible_host: MYHOST.domain.com
ansible_password: Password123!
my_comment:
  foo: bar
other_var:
  foo: bar
sid: S-1-5-21-1234-1108
microsoft_ad_distinguished_name: CN=MYHOST,CN=Computers,DC=domain,DC=com

注意

主机事实名称按字面使用,使用此格式时,不会将 - 转换为 _

清单主机名

默认情况下,找到的主机的 inventory_hostname 将基于 name LDAP 属性值。如果为找到的计算机帐户设置了 dNSHostName 属性,则它将被设置为 ansible_host 事实。要定义自定义 inventory_hostnameansible_host,请在 attributescompose 插件选项下设置该键。例如,这将 inventory_hostname 设置为 sAMAccountName 的值,而不带结尾的 $,而不是计算机帐户 LDAP name 属性。

attributes:
  sAMAccountName:
  inventory_hostname: sAMAccountName[:-1]
# ansible-inventory -i microsoft.ad.ldap.yml --host MYHOST --vars --yaml

ansible_host: MYHOST.domain.com
microsoft_ad_distinguished_name: CN=OtherName,CN=Computers,DC=domain,DC=com
sAMAccountName: MYHOST$

也可以在 compose 键下设置 inventory_hostname。以下配置将产生与上述相同的输出。

attributes:
  sAMAccountName:

compose:
  inventory_hostname: sAMAccountName[:-1]

以下示例演示如何设置一个自定义的 ansible_host 变量,该变量用作连接主机,但保留计算机帐户名称的默认 inventory_hostname

attributes:
  sAMAccountName:
  ansible_host: sAMAccountName[:-1]

值类型和模板

每个 LDAP 属性值都存储为字节列表,但 LDAP 数据库中提供的架构可以描述这些原始字节列表如何表示为适当的类型,例如字符串、整数、布尔值等。目前,在强制转换 LDAP 属性值时,仅使用以下四种类型:

  • 布尔值

  • 整数

  • 字节

  • 字符串

布尔值、整数和字符串会被强制转换为这些特定的 Python 类型,而字节会被强制转换为这些字节的 base64 字符串编码。

注意

objectGuidobjectSid 属性始终被强制转换为分别表示安全标识符和 GUID 的字符串。这是除了 LDAP 模式语法之外,唯一具有特殊强制转换规则的属性。

LDAP 属性值也可以标记为单值或多值属性。单值属性只包含强制转换后的值,或者如果未设置则为 None/null,而多值属性将设置为强制转换值的列表。例如,comment 是一个单值字符串,而 servicePrincipalName 是一个多值字符串。使用此清单配置请求 commentservicePrincipalName,我们将获得以下清单主机定义:

plugin: microsoft.ad.ldap

attributes:
  comment:
  servicePrincipalName:
# ansible-inventory -i microsoft.ad.ldap.yml --host MYHOST --vars --yaml

ansible_host: MYHOST.domain.com
comment: test comment
servicePrincipalName:
- WSMAN/MYHOST
- WSMAN/MYHOST.domain.com
- TERMSRV/MYHOST
- TERMSRV/MYHOST.domain.com
- RestrictedKrbHost/MYHOST
- HOST/MYHOST
- RestrictedKrbHost/MYHOST.domain.com
- HOST/MYHOST.domain.com
microsoft_ad_distinguished_name: CN=MYHOST,CN=Computers,DC=domain,DC=com

一些属性(如 pwdLastSet)通常表示为日期时间值,但在内部存储为整数。由于 LDAP 模式中没有元数据将这些整数值表示为日期时间对象,因此默认情况下它们只会强制转换为整数值。

以下过滤器可以作为一种简单的方法,将强制转换的值进一步转换为更易读的内容:

以下示例展示了如何在 attributes 选项中使用这些过滤器:

plugin: microsoft.ad.ldap

attributes:
  pwdLastSet:
    password_last_set_int: this
    password_last_set_datetime: this | microsoft.ad.as_datetime
# ansible-inventory -i microsoft.ad.ldap.yml --host MYHOST --vars --yaml

ansible_host: MYHOST.domain.com
password_last_set_datetime: 2023-02-06T07:39:09.195321+0000
password_last_set_int: 133201427491953218
microsoft_ad_distinguished_name: CN=MYHOST,CN=Computers,DC=domain,DC=com

模板还可以引用集合之外存在的其他过滤器,例如 Ansible 内置的 from_json 等。该值只是在正常模板操作期间放置在 {{ ... }} 内的内容。

注意

查找不能在属性值模板中使用,只能使用过滤器。

attributes 清单选项中使用的每个模板都可以引用以下变量:

  • this

  • raw

  • 任何先前定义的属性

this 变量引用强制转换后的 LDAP 属性值,而 raw 引用表示尚未强制转换的原始 LDAP 值的 base64 编码字符串列表。在处理每个属性主机事实时,它在后续模板中也以该主机事实名称提供。以下是一个更复杂的属性集示例:

plugin: microsoft.ad.ldap

attributes:
  objectSid:
    sid: this
    sid_raw: raw
    sid_raw_filtered: raw | microsoft.ad.as_sid | first
  objectGuid:
  sAMAccountName:
    computer_name:
  comment:
    comment: this
    # Can refer to previously set attributes above
    description: computer_name ~ " - " ~ sid ~ " - " ~ objectGuid ~ " - " ~ this

# Can also be used as a template and refer to the vars retrieved above
compose:
  comment2: comment
# ansible-inventory -i microsoft.ad.ldap.yml --host MYHOST --vars --yaml

ansible_host: MYHOST.domain.com
comment: test comment
comment2: test comment
computer_name: MYHOST$
description: MYHOST$ - S-1-5-21-1234-1108 - 51cc490f-1de0-41ae-98ad-dc065d5b33e2 - test comment
objectGuid: 51cc490f-1de0-41ae-98ad-dc065d5b33e2
sid: S-1-5-21-1234-1108
sid_raw:
- AQMAAAAAAAUVAAAA0gQAAFQEAAA=
sid_raw_filtered: S-1-5-21-1234-1108
microsoft_ad_distinguished_name: CN=MYHOST,CN=Computers,DC=domain,DC=com

LAPS

本地管理员密码解决方案 (LAPS) 可用于自动更改加入域的主机上本地管理员帐户的密码。LDAP 连接插件可以检索 LAPS 管理的值,并将其分配为目标主机的连接密码。

LAPS 可以使用三个不同的属性来存储密码信息:

  • ms-Mcs-AdmPwd - 包含密码的旧版 LAPS 属性

  • msLAPS-Password - 包含用户名和密码的 Windows LAPS 属性

  • msLAPS-EncryptedPassword - 包含加密用户名和密码的 Windows LAPS 属性

如果使用旧版 LAPS 设置,以下配置将检索 LAPS 管理的值并将其分配为连接用户名和密码:

plugin: microsoft.ad.ldap

attributes:
  ms-Mcs-AdmPwd:
    ansible_user: '"Administrator"'
    ansible_password: this
# ansible-inventory -i microsoft.ad.ldap.yml --host MYHOST --vars --yaml

ansible_host: MYHOST.domain.com
ansible_password: aR$lmrqK1l622H
ansible_user: Administrator
microsoft_ad_distinguished_name: CN=MYHOST,CN=Computers,DC=domain,DC=com

注意

旧版 LAPS 不存储用户名,上面的示例硬编码了用户名 Administrator

如果使用未加密的 Windows LAPS,以下配置将 LAPS 管理的值分配为连接用户名和密码:

plugin: microsoft.ad.ldap

attributes:
  msLAPS-Password:
    ansible_user: (this | from_json).n
    ansible_password: (this | from_json).p
    raw_example: raw
    this_example: this
# ansible-inventory -i microsoft.ad.ldap.yml --host MYHOST --vars --yaml

ansible_host: MYHOST.domain.com
ansible_password: AWznso@ZJ+J6p9
ansible_user: Administrator
microsoft_ad_distinguished_name: CN=MYHOST,CN=Computers,DC=domain,DC=com
raw_example:
- eyJuIjoiQWRtaW5pc3RyYXRvciIsInQiOiIxZDk4MmI0MzdiN2E1YzYiLCJwIjoiQVd6bnNvQFpKK0o2cDkifQ==
this_example:
  n: Administrator
  p: AWznso@ZJ+J6p9
  t: 1d982b437b7a5c6

与旧版 LAPS 不同,属性值是一个包含以下键的 JSON 字符串:

  • n - 密码加密所针对的帐户名称

  • p - 帐户的密码

  • t - 密码设置的时间,以 base16 编码的 FILETIME 格式

注意

建议在 this 值上使用 from_json 过滤器(如上述示例所示),以确保在存在或不存在 Jinja2 本机类型支持的情况下保持一致的行为。

获取加密的 Windows LAPS 值需要安装 dpapi-ng Python 库。有关此可选软件包以及如何调试是否已安装的详细信息,请参阅LDAP 连接要求

注意

使用 Windows LAPS 加密密码目前是一项实验性功能。

安装 dpapi-ng 软件包后,经过授权的 LDAP 用户可以解密 LAPS 管理的用户名和密码,并将其分配给目标主机连接,如下所示:

plugin: microsoft.ad.ldap

attributes:
  msLAPS-EncryptedPassword:
    ansible_user: (this.value | from_json).n
    ansible_password: (this.value | from_json).p
    raw_example: raw
    this_example: this
# ansible-inventory -i microsoft.ad.ldap.yml --host MYHOST --vars --yaml

ansible_host: MYHOST.domain.com
ansible_password: 6jr&}yK++{0Q}&
ansible_user: Administrator
microsoft_ad_distinguished_name: CN=MYHOST,CN=Computers,DC=domain,DC=com
raw_example:
- toLZAWR7rgfk...
this_example:
  encrypted_value: MIIETgYJKoZI...
  flags: 0
  info: ''
  update_timestamp: 133281382308674404
  value: '{"n":"Administrator","t":"1d982b607ae7b64","p":"6jr&}yK++{0Q}&"}'

raw 值包含存储在 AD 中的原始 base64 编码值。this 值包含一个带有以下键的字典:

  • encrypted_value:加密的密码 blob,采用 base64 字符串格式

  • flags:设置为按位 int 值的标志,目前 Microsoft 未对此进行记录

  • update_timestamp:密码设置的 FILETIME 值

  • value:解密后的值,其中包含用户名和密码,采用 JSON 字符串格式

  • debug:指示为什么解密值失败的调试信息

只有在解密过程成功时才会显示 value 键。如果失败,则会显示 debug 键,并包含解密失败的原因。

如果未安装 dpapi-ng 库,输出将如下所示:

# ansible-inventory -i microsoft.ad.ldap.yml --host MYHOST --vars --yaml

ansible_host: MYHOST.domain.com
microsoft_ad_distinguished_name: CN=MYHOST,CN=Computers,DC=domain,DC=com
raw_example:
- toLZAWR7rgfk...
this_example:
  debug: Cannot decrypt value as the Python library dpapi-ng is not installed
  encrypted_value: MIIETgYJKoZI...
  flags: 0
  update_timestamp: 133281382308674404

value 键不再存在,debug 包含 dpapi-ng 未安装的消息。

如果安装了 dpapi-ng 库,但连接用户无权解密值,输出将如下所示:

# ansible-inventory -i microsoft.ad.ldap.yml --host MYHOST --vars --yaml

ansible_host: MYHOST.domain.com
microsoft_ad_distinguished_name: CN=MYHOST,CN=Computers,DC=domain,DC=com
raw_example:
- toLZAWR7rgfk...
this_example:
  debug: Failed to decrypt value due to error - ValueError GetKey failed 0x80070005
  encrypted_value: MIIETgYJKoZI...
  flags: 0
  update_timestamp: 133281382308674404

测试连接用户是否能够解密密码的一种简单方法是以该用户的身份在 Windows 主机上运行 Get-LapsADPassword -Identity MYHOST