本页面介绍如何将 ONTAP 模式池加入 Active Directory 域。
ONTAP 模式下的 Flex Unified 存储池不使用 Active Directory 政策。如需将 Active Directory 与 SMB 卷搭配使用,请通过创建 CIFS 服务器将池中的 ONTAP 存储虚拟机 (SVM) 加入 Windows 域。
如需了解 Active Directory 先决条件和防火墙规则,请参阅 Google Cloud NetApp Volumes Active Directory 集成。
准备工作
完成以下先决条件:
确保存储池网络可以连接到网域控制器和 DNS 服务器。
配置 Cloud DNS,以将 Windows 域的 DNS 请求转发到 Windows DNS 服务器。
完成准备工作中的先决条件。
收集以下信息:
AD_FQDN:完全限定域名,例如 corp.example.com。AD_DNS_SERVERS:Active Directory DNS 服务器的一到三个以逗号分隔的 IPv4 地址。AD_USERNAME和AD_PASSWORD:可以创建计算机账号的网域账号,其 SAM 账号名称不超过 20 个字符。COMPUTER_NAME:网域中计算机账号的唯一 NetBIOS 名称,最多 15 个字符。OU:可选:目标组织部门 (OU) 的标识名,例如 OU=Computers,DC=corp,DC=example,DC=com。
概览
如需将 ONTAP 模式池加入 Active Directory 域,请完成以下步骤:
查找池资源
查找存储池的以下资源:
SVM 名称
如需查找 SVM 名称,请执行以下操作:
ontap_execute "vserver show -fields vserver,operational-state"
记下 Vserver 名称,例如 gcnv-7cf6ee41c1a94f0-svm-01。在后续步骤中,请将此名称用作 SVM_NAME。
数据 LIF
在加入网域之前,请验证数据 LIF 是否可以成功访问 DNS 服务器和网域控制器。
ontap_execute "network interface show -role data"
如需测试与 DNS 服务器或网域控制器的连接,请使用带有 -lif 标志的 SAN LIF。ONTAP 模式池会公开名为
SVM_NAME-san-1、SVM_NAME-san-2、
的 SAN LIF,并根据需要公开其他 LIF:
ontap_execute "network ping -vserver SVM_NAME -destination DNS_OR_DC_IP -lif SVM_NAME-san-1"
如果 ping 失败,则存储池无法通过网络连接到 DNS 服务器或网域控制器。VPC 中的虚拟机 (VM) 可能仍然能够连接到这些主机。如需详细了解如何排查 连接问题,请参阅为什么 NetApp Volumes 无法连接到 Active Directory 和 VPC 后面的其他网络。
在 SVM 上配置 DNS
如需在 SVM 上配置 DNS,请执行以下操作:
ontap_execute "vserver services name-service dns create -vserver SVM_NAME -domains AD_FQDN -name-servers AD_DNS_SERVERS"
如果主机名称服务开关不包含 DNS,请添加它:
ontap_execute "vserver services name-service ns-switch modify -vserver SVM_NAME -database hosts -sources dns,files"
验证配置:
ontap_execute "vserver services name-service dns show -vserver SVM_NAME"
加入网域
本部分介绍 ONTAP CLI 命令、CLI 代理的限制以及如何使用 Python 加入网域。
ONTAP CLI 命令
在具有直接 ONTAP CLI 访问权限的系统上,运行以下命令:
vserver cifs create -vserver SVM_NAME -cifs-server COMPUTER_NAME -domain AD_FQDN
如需添加可选参数,请使用 -ou OU
和 -default-site SITE。ONTAP 会提示您输入网域密码,并且该命令可能需要几分钟时间。
CLI 代理的限制
ONTAP CLI 代理不是交互式的。由于 vserver cifs create 命令会提示输入密码,因此您无法通过代理完成加入网域的操作。
请改用 REST API。API 等效项为 POST /protocols/cifs/services。
该脚本仅针对与 Active Directory 的 Kerberos 通信宣传 AES-256。这是现代网域的推荐设置。如需同时宣传 AES-128 或 DES,请在运行脚本之前向脚本中的 advertised_kdc_encryptions 数组添加值,或者在加入网域后使用 vserver cifs security modify -advertised-enc-types 修改 CIFS 服务器。不建议使用 DES,因为它使用弱加密。
有效的加密类型值为 aes-256、aes-128、rc4 和 des。如果在加入网域后更改宣传的加密类型,ONTAP 会重置机器账号密码并提示输入 Active Directory 凭据。
使用 Python 加入网域
将以下脚本另存为 join_ad.py,并使用 Python 3.8 或更高版本运行该脚本。
该脚本仅使用 Python 标准库。
#!/usr/bin/env python3
"""Join an ONTAP-mode SVM to Active Directory by creating a CIFS server."""
import json
import os
import subprocess
import sys
import time
import urllib.error
import urllib.request
# Required environment variables.
REQUIRED = (
"PROJECT_ID", "LOCATION", "POOL_NAME", "SVM_NAME",
"AD_FQDN", "COMPUTER_NAME", "AD_USERNAME", "AD_PASSWORD",
)
missing = [name for name in REQUIRED if not os.environ.get(name)]
if missing:
sys.exit(f"Set required environment variables: {', '.join(missing)}")
PROJECT_ID = os.environ["PROJECT_ID"]
LOCATION = os.environ["LOCATION"]
POOL_NAME = os.environ["POOL_NAME"]
SVM_NAME = os.environ["SVM_NAME"]
AD_FQDN = os.environ["AD_FQDN"]
COMPUTER_NAME = os.environ["COMPUTER_NAME"]
AD_USERNAME = os.environ["AD_USERNAME"]
AD_PASSWORD = os.environ["AD_PASSWORD"]
# Optional environment variables.
OU = os.environ.get("OU", "")
AD_SITE = os.environ.get("AD_SITE", "")
POLL_INTERVAL = int(os.environ.get("POLL_INTERVAL", "10"))
# Kerberos encryption types advertised to Active Directory. AES-256 only by default.
# To add aes-128 or des (not recommended), extend this list before you run the script.
ADVERTISED_KDC_ENCRYPTIONS = ["aes-256"]
API_ROOT = (
f"https://netapp.googleapis.com/v1/projects/{PROJECT_ID}"
f"/locations/{LOCATION}/storagePools/{POOL_NAME}/ontap/api"
)
def get_access_token() -> str:
return subprocess.check_output(
["gcloud", "auth", "print-access-token"], text=True
).strip()
def api_request(method: str, path: str, body=None) -> dict:
headers = {
"Authorization": f"Bearer {get_access_token()}",
"Content-Type": "application/json",
}
data = json.dumps(body).encode() if body is not None else None
req = urllib.request.Request(f"{API_ROOT}{path}", data=data, headers=headers, method=method)
try:
with urllib.request.urlopen(req) as resp:
return json.loads(resp.read())
except urllib.error.HTTPError as exc:
detail = exc.read().decode()
sys.exit(f"API request failed ({exc.code}): {detail}")
def job_record(response: dict) -> dict:
return response.get("rawResponse") or response.get("body") or response
def job_state(response: dict) -> str:
return job_record(response).get("state", "")
def format_job_error(response: dict) -> str:
record = job_record(response)
message = record.get("message") or record.get("error", {}).get("message")
if message:
return message
return json.dumps(response, indent=2)
def wait_for_job(job_uuid: str) -> None:
print(f"ONTAP job UUID: {job_uuid}")
while True:
response = api_request("GET", f"/cluster/jobs/{job_uuid}")
state = job_state(response)
print(f" Job state: {state}")
if state == "success":
print("Domain join completed.")
return
if state in ("failure", "error"):
sys.exit(f"Job failed: {format_job_error(response)}")
time.sleep(POLL_INTERVAL)
def main() -> None:
ad_domain = {
"fqdn": AD_FQDN,
"user": AD_USERNAME,
"password": AD_PASSWORD,
}
if OU:
ad_domain["organizational_unit"] = OU
if AD_SITE:
ad_domain["default_site"] = AD_SITE
body = {
"body": {
"svm": {"name": SVM_NAME},
"name": COMPUTER_NAME,
"ad_domain": ad_domain,
"security": {
"advertised_kdc_encryptions": ADVERTISED_KDC_ENCRYPTIONS,
},
}
}
print(f"Creating CIFS server and joining {AD_FQDN}...")
response = api_request("POST", "/protocols/cifs/services?return_records=true", body)
job = (response.get("body") or {}).get("job") or response.get("job")
if not job or not job.get("uuid"):
print(json.dumps(response, indent=2))
return
wait_for_job(job["uuid"])
if __name__ == "__main__":
main()
配置 SMB 访问权限
向网域用户或群组授予本地管理员权限:
ontap_execute "vserver cifs users-and-groups local-group add-members -vserver SVM_NAME -group-name BUILTIN\\Administrators -member-names NETBIOS_DOMAIN\\username"
将 NETBIOS_DOMAIN 替换为 NetBIOS 域名,而不是 DNS 名称。
如果您使用与 Active Directory 集成的 DNS,请启用安全动态 DNS 更新。
您必须将 -vserver-fqdn 标志设置为 CIFS 服务器 FQDN,例如
COMPUTER_NAME.AD_FQDN:
ontap_execute "vserver services name-service dns dynamic-update modify -vserver SVM_NAME -is-enabled true -use-secure true -vserver-fqdn COMPUTER_NAME.AD_FQDN"
验证网域加入情况
如需验证网域加入情况,请执行以下操作:
ontap_execute "vserver cifs show -vserver SVM_NAME"
确认 CIFS 服务器处于在线状态并已加入预期网域。
移除 CIFS 服务器
如需删除 CIFS 服务器并重新加入网域,请完成以下步骤:
停用安全动态 DNS 更新:
ontap_execute "vserver services name-service dns dynamic-update modify -vserver SVM_NAME -is-enabled false -use-secure false"
在启用安全 DDNS 时,ONTAP 会阻止删除 CIFS。
从 Active Directory 中移除计算机账号,或使用有权在目标 OU 中删除计算机账号的网域账号。
在 ONTAP 上删除 CIFS 服务器:
ontap_execute "vserver cifs delete -vserver SVM_NAME -skip-ad-account-delete true"
如果计算机账号已从 Active Directory 中移除,或者用于删除的账号没有移除计算机对象的权限,请使用
-skip-ad-account-delete true。在这种情况下,ONTAP 仅移除本地 CIFS 配置,而不会尝试删除 Active Directory 对象。默认情况下,
vserver cifs delete还会从 Active Directory 中移除计算机账号,并提示输入网域管理员凭据。此提示无法通过 ONTAP CLI 代理运行。