Skip to content

Commit b08eece

Browse files
authored
Adding for ActiveDirectory/LDAPS integration for HyperPod (#224)
* Adding for ActiveDirectory/LDAPS integration for HyperPod * Fixed typo * Added copyright description * Use /fsx/%u as the home directory parameter. * Added comment for constants * Added config.py. Moved SSSD configurations to config.py. * Renaming configure_sssd.py -> setup_sssd.py for consistency with other files * Updated comments in config.py
1 parent 250b7d5 commit b08eece

File tree

3 files changed

+305
-0
lines changed

3 files changed

+305
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
2+
# Basic configuration parameters
3+
class Config:
4+
5+
# Set true if you want to install SSSD for ActiveDirectory/LDAP integration.
6+
# You need to configure parameters in SssdConfig as well.
7+
enable_sssd = False
8+
9+
10+
# Configuration parameters for ActiveDirectory/LDAP/SSSD
11+
class SssdConfig:
12+
13+
# Name of domain. Can be default if you are not sure.
14+
domain = "default"
15+
16+
# Comma separated list of LDAP server URIs
17+
ldap_uri = "ldaps://nlb-ds-xyzxyz.elb.us-west-2.amazonaws.com"
18+
19+
# The default base DN to use for performing LDAP user operations
20+
ldap_search_base = "dc=hyperpod,dc=abc123,dc=com"
21+
22+
# The default bind DN to use for performing LDAP operations
23+
ldap_default_bind_dn = "CN=ReadOnly,OU=Users,OU=hyperpod,DC=hyperpod,DC=abc123,DC=com"
24+
25+
# "password" or "obfuscated_password". Obfuscated password is recommended.
26+
ldap_default_authtok_type = "obfuscated_password"
27+
28+
# You need to modify this parameter with the obfuscated password, not plain text password
29+
ldap_default_authtok = "placeholder"
30+
31+
# SSH authentication method - "password" or "publickey"
32+
ssh_auth_method = "publickey"
33+
34+
# Home directory. You can change it to "/home/%u" if your cluster doesn't use FSx volume.
35+
override_homedir = "/fsx/%u"
36+
37+
# Group names to accept SSH login
38+
ssh_allow_groups = {
39+
"controller" : ["ClusterAdmin", "ubuntu"],
40+
"compute" : ["ClusterAdmin", "ClusterDev", "ubuntu"],
41+
"login" : ["ClusterAdmin", "ClusterDev", "ubuntu"],
42+
}
43+
44+
# Group names for sudoers
45+
sudoers_groups = {
46+
"controller" : ["ClusterAdmin", "ClusterDev"],
47+
"compute" : ["ClusterAdmin", "ClusterDev"],
48+
"login" : ["ClusterAdmin", "ClusterDev"],
49+
}

1.architectures/5.sagemaker-hyperpod/LifecycleScripts/base-config/lifecycle_script.py

+6
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@
1010
import time
1111
from typing import Any, Dict, List, Optional, Tuple
1212

13+
from config import Config
14+
1315

1416
SLURM_CONF = os.getenv("SLURM_CONF", "/opt/slurm/etc/slurm.conf")
1517

@@ -169,6 +171,10 @@ def main(args):
169171
# ExecuteBashScript("./utils/install_slurm_exporter.sh").run()
170172
# ExecuteBashScript("./utils/install_prometheus.sh").run()
171173

174+
# Install and configure SSSD for ActiveDirectory/LDAP integration
175+
if Config.enable_sssd:
176+
subprocess.run(["python3", "-u", "setup_sssd.py", "--node-type", node_type], check=True)
177+
172178
print("[INFO]: Success: All provisioning scripts completed")
173179

174180

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,250 @@
1+
# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2+
# SPDX-License-Identifier: MIT-0
3+
4+
import sys
5+
import os
6+
import subprocess
7+
import tempfile
8+
import re
9+
import getpass
10+
import argparse
11+
12+
from config import SssdConfig
13+
14+
15+
# ----------------------------------
16+
# Constants you don't have to modify
17+
18+
if getpass.getuser() == "root":
19+
sudo_command = []
20+
else:
21+
sudo_command = ["sudo","-E"]
22+
23+
packages_to_install = [
24+
"sssd",
25+
"ldap-utils",
26+
"sssd-tools",
27+
]
28+
29+
packages_to_uninstall = [
30+
"ec2-instance-connect",
31+
]
32+
33+
sshd_config_filename = "/etc/ssh/sshd_config"
34+
sssd_config_filename = "/etc/sssd/sssd.conf"
35+
ldap_config_filename = "/etc/ldap/ldap.conf"
36+
sudoers_config_filename = "/etc/sudoers.d/100-ldap-cluster-admin"
37+
38+
cert_filename = "/etc/ldap/ldaps.crt"
39+
cert_filename_src = os.path.join( os.path.dirname(__file__), os.path.basename(cert_filename) )
40+
41+
assert os.path.exists(cert_filename_src), f"Certificate file not found - {cert_filename_src}"
42+
assert SssdConfig.ldap_default_authtok != "placeholder", "You need to configure SssdConfig.ldap_default_authtok. You can use tools/obfuscate_password.py to get obfuscated password"
43+
assert SssdConfig.ssh_auth_method in ["password", "publickey"], f"SssdConfig.ssh_auth_method has to be either 'password' or 'publickey'"
44+
45+
46+
# ---------------------------------
47+
# Templates for configuration files
48+
49+
sssd_conf = f"""
50+
[domain/{SssdConfig.domain}]
51+
id_provider = ldap
52+
cache_credentials = True
53+
ldap_uri = {SssdConfig.ldap_uri}
54+
ldap_search_base = {SssdConfig.ldap_search_base}
55+
ldap_schema = AD
56+
ldap_default_bind_dn = {SssdConfig.ldap_default_bind_dn}
57+
ldap_default_authtok_type = {SssdConfig.ldap_default_authtok_type}
58+
ldap_default_authtok = {SssdConfig.ldap_default_authtok}
59+
ldap_tls_cacert = {cert_filename}
60+
ldap_tls_reqcert = hard
61+
ldap_id_mapping = True
62+
ldap_referrals = False
63+
ldap_user_extra_attrs = altSecurityIdentities:altSecurityIdentities
64+
ldap_user_ssh_public_key = altSecurityIdentities
65+
ldap_use_tokengroups = True
66+
enumerate = False
67+
fallback_homedir = /home/%u
68+
override_homedir = {SssdConfig.override_homedir}
69+
default_shell = /bin/bash
70+
#use_fully_qualified_names = True
71+
#debug_level = 6
72+
73+
[sssd]
74+
config_file_version = 2
75+
domains = {SssdConfig.domain}
76+
services = nss, pam, ssh
77+
#debug_level = 6
78+
79+
[pam]
80+
offline_credentials_expiration = 14
81+
#debug_level = 6
82+
83+
[nss]
84+
filter_users = nobody,root
85+
filter_groups = nobody,root
86+
#debug_level = 6
87+
"""
88+
89+
90+
# ---
91+
92+
def install_apt_packages():
93+
94+
print("---")
95+
print("Updating apt package index")
96+
subprocess.run( [ *sudo_command, "apt", "update" ] )
97+
98+
print("---")
99+
print("Installing packages - ", packages_to_install)
100+
subprocess.run( [ *sudo_command, "apt", "install", "-y", *packages_to_install ] )
101+
102+
103+
def uninstall_apt_packages():
104+
105+
print("---")
106+
print("Uninstalling packages - ", packages_to_uninstall)
107+
subprocess.run( [ *sudo_command, "apt", "remove", "-y", *packages_to_uninstall ] )
108+
109+
110+
def install_ldaps_cert():
111+
112+
print("---")
113+
print("Installing cert for LDAPS - ", cert_filename)
114+
subprocess.run( [ *sudo_command, "cp", cert_filename_src, cert_filename ] )
115+
subprocess.run( [ *sudo_command, "chmod", "644", cert_filename ] )
116+
117+
print("---")
118+
print(f"Updating {ldap_config_filename} ldap utility commands")
119+
with tempfile.TemporaryDirectory() as tmp_dir:
120+
tmp_ldap_config_filename = os.path.join(tmp_dir, os.path.basename(ldap_config_filename))
121+
122+
with open(ldap_config_filename) as fd_src:
123+
d = fd_src.read()
124+
125+
d = re.sub( r"[#\t ]*TLS_CACERT[ \t]+.*$", f"TLS_CACERT {cert_filename}", d, flags=re.MULTILINE )
126+
print(d)
127+
128+
with open(tmp_ldap_config_filename,"w") as fd_dst:
129+
fd_dst.write(d)
130+
131+
subprocess.run( [ *sudo_command, "chmod", "644", tmp_ldap_config_filename ] )
132+
subprocess.run( [ *sudo_command, "chown", "root:root", tmp_ldap_config_filename ] )
133+
subprocess.run( [ *sudo_command, "cp", tmp_ldap_config_filename, ldap_config_filename ] )
134+
135+
136+
def configure_sssd():
137+
138+
print("---")
139+
print("Configuring SSSD")
140+
141+
with tempfile.TemporaryDirectory() as tmp_dir:
142+
tmp_sssd_config_filename = os.path.join(tmp_dir, os.path.basename(sssd_config_filename))
143+
144+
d = sssd_conf.strip()
145+
print(d)
146+
147+
with open(tmp_sssd_config_filename,"w") as fd:
148+
fd.write(d)
149+
150+
subprocess.run( [ *sudo_command, "chmod", "600", tmp_sssd_config_filename ] )
151+
subprocess.run( [ *sudo_command, "chown", "root:root", tmp_sssd_config_filename ] )
152+
subprocess.run( [ *sudo_command, "cp", tmp_sssd_config_filename, sssd_config_filename ] )
153+
154+
155+
def configure_ssh(node_type):
156+
157+
print("---")
158+
print(f"Configuring SSH authentication method to {SssdConfig.ssh_auth_method}")
159+
160+
with tempfile.TemporaryDirectory() as tmp_dir:
161+
tmp_sshd_config_filename = os.path.join(tmp_dir, os.path.basename(sshd_config_filename))
162+
163+
with open(sshd_config_filename) as fd_src:
164+
d = fd_src.read()
165+
166+
if SssdConfig.ssh_auth_method=="password":
167+
d = re.sub( r"[#\t ]*PasswordAuthentication[ \t]+.*$", "PasswordAuthentication yes", d, flags=re.MULTILINE )
168+
elif SssdConfig.ssh_auth_method=="publickey":
169+
d = re.sub( r"[#\t ]*AuthorizedKeysCommand[ \t]+.*$", "AuthorizedKeysCommand /usr/bin/sss_ssh_authorizedkeys", d, flags=re.MULTILINE )
170+
d = re.sub( r"[#\t ]*AuthorizedKeysCommandUser[ \t]+.*$", "AuthorizedKeysCommandUser root", d, flags=re.MULTILINE )
171+
172+
for group_name in SssdConfig.ssh_allow_groups[node_type]:
173+
assert " " not in group_name
174+
175+
joined_group_names = " ".join(SssdConfig.ssh_allow_groups[node_type])
176+
177+
d += (
178+
f"\n"
179+
f"# Allow SSH access only to specific groups\n"
180+
f"AllowGroups {joined_group_names}\n"
181+
)
182+
183+
print(d)
184+
185+
with open(tmp_sshd_config_filename,"w") as fd_dst:
186+
fd_dst.write(d)
187+
188+
subprocess.run( [ *sudo_command, "chmod", "644", tmp_sshd_config_filename ] )
189+
subprocess.run( [ *sudo_command, "chown", "root:root", tmp_sshd_config_filename ] )
190+
subprocess.run( [ *sudo_command, "cp", tmp_sshd_config_filename, sshd_config_filename ] )
191+
192+
193+
def enable_automatic_homedir_creation():
194+
195+
print("---")
196+
print(f"Enabling automatic home directory creation")
197+
198+
subprocess.run( [ *sudo_command, "pam-auth-update", "--enable", "mkhomedir" ] )
199+
200+
201+
def configure_sudoers(node_type):
202+
203+
print("---")
204+
print(f"Configuring sudoers")
205+
206+
with tempfile.TemporaryDirectory() as tmp_dir:
207+
tmp_sudoers_config_filename = os.path.join(tmp_dir, os.path.basename(sudoers_config_filename))
208+
209+
with open(tmp_sudoers_config_filename,"w") as fd:
210+
211+
for group_name in SssdConfig.sudoers_groups[node_type]:
212+
assert " " not in group_name
213+
fd.write(f"%{group_name} ALL=(ALL:ALL) NOPASSWD:ALL\n")
214+
215+
subprocess.run( [ *sudo_command, "chmod", "440", tmp_sudoers_config_filename ] )
216+
subprocess.run( [ *sudo_command, "chown", "root:root", tmp_sudoers_config_filename ] )
217+
subprocess.run( [ *sudo_command, "cp", tmp_sudoers_config_filename, sudoers_config_filename ] )
218+
219+
220+
def restart_services():
221+
222+
print("---")
223+
print("Restarting services")
224+
225+
subprocess.run( [ *sudo_command, "systemctl", "restart", "ssh.service" ] )
226+
subprocess.run( [ *sudo_command, "systemctl", "restart", "sssd.service" ] )
227+
228+
229+
230+
if __name__ == "__main__":
231+
232+
argparser = argparse.ArgumentParser(description="Script to configure ActiveDirectory/LDAP on SageMaker HyperPod")
233+
argparser.add_argument('--node-type', action="store", required=True, help='Node type (controller, login, compute)')
234+
args = argparser.parse_args()
235+
236+
assert args.node_type in ["controller", "login", "compute"]
237+
238+
print("Starting SSSD configuration steps")
239+
240+
install_apt_packages()
241+
uninstall_apt_packages()
242+
install_ldaps_cert()
243+
configure_sssd()
244+
configure_ssh(node_type = args.node_type)
245+
enable_automatic_homedir_creation()
246+
configure_sudoers(node_type = args.node_type)
247+
restart_services()
248+
249+
print("---")
250+
print("Finished SSSD configuration steps")

0 commit comments

Comments
 (0)