LDAP 用户管理与 RSCT

客户示例

本文介绍如何使用可靠的系统集群技术 (RSCT) 实现高度可用的面向 IBM® AIX® 的轻量级目录访问协议 (LDAP) 用户管理。不同于主流 LDAP 建议,该解决方案的目的是为所有用户(包括 root 用户)提供 LDAP 注册表。高可用性的实现应始终保证 root 用户(以及其他系统用户)的可用性。

Peter Kes, AIX 解决方案架构师, UBS AG

Peter Kes 的 IT 职业生涯是从一名 IBM 系统工程师开始的。他开发了许多 IBM AIX 和 IBM RS/6000 SP 领域中的客户解决方案,也开发和教授技术课程,并撰写了一些题为 AIX 和服务包 (SP) 系统管理的 IBM 红皮书。在 IBM 美国(波基普西)经历过一段短暂且集中的学习之后,他开始在瑞士银行从事大型数据仓库项目工作。他一直负责为安全性、Web 部署和系统管理编写、设计和开发高级技术解决方案。



2012 年 12 月 10 日

设计挑战

设计一个 LDAP 用户管理解决方案应满足以下要求:

  • 服务一个集群环境:该环境严重依赖于一个一致的注册表视图;所有集群成员必须在任何时候都能访问相同的注册表信息。
  • 包括所有用户和组特权:典型的 LDAP 解决方案把登录凭证和实际组成员分离开来。该解决方案必须能够管理上述两项。
  • 管理一个注册表:典型的 LDAP 解决方案把系统用户和普通用户分开。这就确保当 LDAP 不可访问时,root 用户和其他功能用户仍然能进行系统维护。该设计的初始目标是使所有用户都在一个注册表中。
  • 高可用性和 LDAP 故障可恢复性:因为设计的目标是要在一个 LDAP 注册表中配置所有用户,所以该设计应包括能够保证 root 用户和其他系统用户始终可用。
  • 最终目标是在中央 LDAP 服务器群中建立 AIX 注册表。

环境和产品限制

在这之前,我们的集群用户环境是用普通文件和 rsync 机制进行管理。对于定期发生的批量更改,基于普通文件的解决方案也能运行得很好。由于会进行毫无规则的更改(如密码更改),所以问题也随之产生。在主机上设置密码和将密码分发给所有集群成员之间总是存在时间差。在此期间,当进行(集群的)身份验证时,集群应用程序会失败。

我们的安全性基础架构严重依赖于使用组分配所提供的特权。由于历史遗产和易用性,这些普通用户组也被用于向系统用户提供相似的组特权。将普通用户和系统用户分开是可行的。但是,用于保持两个注册表同步的管理基础架构就变得复杂了,而且可能不如预期可靠。

这也适用于将系统用户和普通用户分开。总是有一定程度的重叠情况出现,而导致难于管理。

当将 LDAP 视为一个可行的网络化用户管理注册表时,便可以清晰地看到 LDAP 有一些可用性选项,但缺乏基本的选项。LDAP 提供的最重要的特性是对等域设置的概念。这就允许配置两个或多个 LDAP 服务器,每个服务器都可以更新其相邻服务器的 LDAP 更改。但是,如果 LDAP 出现问题(处于维护模式或无法访问),备用可用性特性将不可用。

考虑 AIX 时,这个 SYSTEM(身份验证方法)参数能提供支持多种验证方法的选项,但是 registry 参数只支持一个注册表。因此,并没提供相应的参数告诉 AIX 要使用的那个注册表(也就是确实可用的那个)。

AIX 提供了内置的可用性框架,该框架能填补系统和子系统特性中的空缺:RSCT。


设计概述

下图描述了 LDAP 的配置原理。

图 1. LDAP 配置
LDAP 配置
  • 至少两个 LDAP 服务器,进行对等配置
  • 为 AIX 用户管理配置的 LDAP
    • 包括所有 root 用户在内的所有系统帐户
    • 使用 memberfulldn=yes 选项
    • 用于密码 lastupdate 标记的用户秒数
  • RSCT 监控和程序,以设置用户管理环境
  • 从 LDAP 中定期提取,在普通文件注册表 (/etc/passwd) 中应用最新
  • 添加一个 inittab 入口,设置注册表选项为文件
  • 在启用 RSCT 之后,RSCT 监控就开始检测 LDAP 的可用性并配置返回到 LDAP 的注册表参数。

需求

本小节将介绍可用性需求和产品需求,以及系统所处的用户管理状态。

可用性需求

保证注册表始终可用:

  • 启动进程中
  • LDAP 服务器发生故障的情况下
  • LDAP 可用性问题或模式错误的情况下

产品需求

  • AIX V6.1
  • IBM Tivoli® Directory Server V6.3
  • IBM AIX RSCT V1.7
  • IBM DB2® V9.5

用户管理状态

下图描述了系统所处的两种状态。

图 2. 用户管理状态
用户管理状态

如果 LDAP 正常工作(在这个配置中,如果 root 用户密码可从 LDAP 中检索的话),那么注册表就定义为 LDAP

注意:您可以定义自己的规则来决定 LDAP 是否可用。root 用户是可供使用的最重要的用户,该概念中的监控可用性就是专注于在 LDAP 中查找 root 用户信息。

系统刚刚启动时,如果网络停止运行,或者如果无法获取 LDAP,那么注册表参数会切换到文件(files)


配置

LDAP 服务器配置(包含对等设置)以 IBM 技术网站和 IBM Redbooks® 中的各种文档为基础。

AIX LDAP 配置是 90% 标准,并可在一些领域进行调整来满足我们的需求。

RSCT 实现是完全在内部构建的。监控脚本基于我们对如何确定 LDAP 服务器是否可访问的经验。

LDAP 服务器

LDAP 服务器将在两个主机或 LPAR 上进行配置。在如下示例和脚本中,使用如下变量:

  • inst1 LDAP Server1 实例名
  • inst2 LDAP Server2 实例名
  • host1 LDAP Server1 主机名
  • host2 LDAP Server2 主机名
  • port1 LDAP Server1 端口号,默认为 20389
  • port2 LDAP Server2 端口号,默认为 20389
  • ldp1 用于 LDAP1 的 DB2 Server1 端口号
  • ldp2 用于 LDAP2 的 DB2 Server2 端口号
  • dbp1 DB2 Server1 服务器端口号
  • dbp2 DB2 Server2 服务器端口号
  • $ITDS /opt/IBM/ldap/V6.3
  • $SUFFIX o=ORG,c=US

在 host1 上创建 inst1 实例

确保实例所有者主目录是本地的,即 /home,并注意密码:对于 inst2 实例所有者用户而言,密码配置应相同。

  • 为两个实例所有者用户准备相同的基组(primary group)(例如,inst1、inst2 和 pgrp=dba)。
  • 为 root 用户做准备,使其成为实例所有者的基组的一部分。
  • 为 root 用户做准备,使其成为 idsldap 组的一部分。
  • 确保 $DB2INSTPATH/default.env 文件不包含实例 inst1inst2
  • 创建目录 /db2/inst1/NODE0000/db2event/inst1 并确保 /db2/inst1/NODE0000/db2event/inst1 可由实例所有者的基组编写。实例创建后,可以将其设置回 normal
清单 1. 创建 inst1 实例:
$ITDS/sbin/idsicrt -q -n -I inst1-e idsldapinst1002007 -g inst100slapd -l /home/inst1 
-p 20389 -s 21389 -a 22389 -c 23389 -t inst1

注意加密字符串。它应与 inst2 的加密字符串相同。

清单 2. 设置 rootdn 密码:
$ITDS/sbin/idsdnpw -q -I inst1 -n -u cn=rootdn -p rootdnpwd
清单 3. 定义 LDAP 数据库:
$ITDS/sbin/idscfgdb -q -n -I inst1 -a inst1 -t dbinst1 -w rootdnpwd -l /db2
清单 4. 在 LDAP 中配置基础后缀:
$ITDS/sbin/idscfgsuf -q -I inst1 -s $SUFFIX

为 AIX 用户管理准备 LDAP

清单 5. 加载 NIS 模式
file="etc/security/ldap/nisSchema.ldif"
$ITDS/bin/ldapmodify -c -v -a -h host1 -p 20389 -D cn=rootdn -w rootdnpwd -f $file
清单 6. 加载 AIX 安全模式
file="etc/security/ldap/sec.ldif"
$ITDS/bin/ldapmodify -c -v -a -h host1 -p 20389 -D cn=rootdn -w rootdnpwd -f $file

在 host2 上创建 inst2 实例

执行以下步骤,在 host2 上创建 inst2 实例。

  1. 准备两个实例所有者用户,两个都属于同一个基组(例如,inst1、inst2 和 pgrp=dba)。
  2. 为 root 用户做准备,使其成为实例所有者的基组的一部分。
  3. 为 root 用户做准备,使其成为 idsldap 组的一部分。
  4. 确保 $DB2INSTPATH/default.env 文件不包含 inst1inst2 实例。
  5. 创建目录 /db2/inst1/NODE0000/db2event/inst1 并确保 /db2/inst1/NODE0000/db2event/inst1 可由实例所有者的基组编写。创建实例后,将其设置回 normal
  6. 确保两个对等的 LDAP 服务器上的种子算法(seed algorithm)是相同的。这是在 idsicrt 命令的 -e 标志中定义的。
清单 7. 创建 inst2 实例:
$ITDS/sbin/idsicrt -q -n -I inst2 -e idsldapinst1002007 -g inst100slapd -l /home/inst2 
-p 20389 -s 21389 -a 22389 -c 23389 -t inst2
清单 8. 设置 rootdn 密码:
$ITDS/sbin/idsdnpw -q -I inst2 -n -u cn=rootdn -p rootdnpwd
清单 9. 定义 LDAP 数据库:
$ITDS/sbin/idscfgdb -q -n -I inst1 -a inst1 -t dbinst1 -w rootdnpwd -l /db2
清单 10. 在 LDAP 中配置基础后缀:
$ITDS/sbin/idscfgsuf -q -I inst2 -s $SUFFIX

为 AIX 用户管理准备 LDAP

清单 11. 加载 NIS 模式
file="etc/security/ldap/nisSchema.ldif"
$ITDS/bin/ldapmodify -c -v -a -h host1 -p 20389 -D cn=rootdn -w rootdnpwd -f $file
清单 12. 加载 AIX 安全模式
file="etc/security/ldap/sec.ldif"
$ITDS/bin/ldapmodify -c -v -a -h host1 -p 20389 -D cn=rootdn -w rootdnpwd -f $file

在 inst1 和 inst2 实例上加载 /etc/passwd 用户群

使用以下程序序列准备一个 Perl 脚本:

#!/usr/bin/perl

open(SEC,"< /etc/security/passwd");
@usr = <SEC>;
close(SEC);
chomp(@usr);

foreach $line (@usr) {
if ( $line =~ /(\w):/ ) {
	$line =~ s/://;
	$uid = $line;
}

if ( $line =~ /lastupdate/ ) {
	@row = split("=",$line);
	$row[1] += 0;
	$lastupdate{$uid} = $row[1];
}
}

$file = "/opt/usrmgt/lib/secinit.ldif";
print("Creating $file containing initial user base ...\n")
$cmd = "/usr/sbin/sectoldif -d \"$SUFFIX\" -S rfc2307aix";
print("Command: $cmd\n");

open(SEC,"$cmd |");
@sec = <SEC>;
close(SEC);
chomp(@sec);

open(SEC,"> $file");

foreach $line (@sec) {

@row = split(":",$line);
if ( $line =~ /^uid:/ ) {
	$uid = $row[1];
	$uid =~ s/\s+//;
}

if ( $line =~ /^memberuid/ ) {
	$row[1] =~ s/ //g;

	print(SEC "$row[0]: uid=$row[1],ou=People,$SUFFIX\n");

} elsif ( $line =~ /^shadowlastchange/ ) {
	print(SEC "shadowlastchange: $lastupdate{$uid}\n");
} else {
	print(SEC "$line\n");
}
}

close(SEC);

print("Loading $file in LDAP Directory Server inst1 ...\n");
$baseauth = "-h host1 -p 20389 -D cn=rootdn -w rootdnpwd";
$cmd = "$ITDS/bin/ldapadd -c -v -a $baseauth -f $file";
print("Command: $cmd\n");
system("$cmd");
print("Loading $file in LDAP Directory Server inst2 ...\n");
$baseauth = "-h host2 -p 20389 -D cn=rootdn -w rootdnpwd";
$cmd = "$ITDS/bin/ldapadd -c -v -a $baseauth -f $file";
print("Command: $cmd\n");
system("$cmd");

设置 inst1 和 inst2 之间的对等复制

定义基础复制上下文

#!/usr/bin/perl
$ldif = "/opt/usrmgt/logs/suffix_replication.ldif";
open(LDIF,"> $ldif");
print(LDIF "dn: $SUFFIX\n");
print(LDIF "changetype: modify\n");
print(LDIF "add: objectclass\n");
print(LDIF "objectclass: ibm-replicationContext\n");
close(LDIF);

$baseauth = "-h host1 -p 20389 -D cn=rootdn -w rootdnpwd";
$cmd = "$ITDS/bin/ldapmodify -v $baseauth -k -l -i $ldif";
print("Executing $cmd ...\n");
system($cmd);

$baseauth = "-h host2 -p 20389 -D cn=rootdn -w rootdnpwd";
$cmd = "$ITDS/bin/ldapmodify -v $baseauth -k -l -i $ldif";
print("Executing $cmd ...\n");
system($cmd);

配置主复制 DN

#!/usr/bin/perl

$ldif = "$SOURCE/logs/master_replication_dn.ldif";
open(LDIF,"> $ldif");
print(LDIF "dn: cn=MasterServer,cn=configuration\n");
print(LDIF "cn: MasterServer\n");
print(LDIF "ibm-slapdMasterDN: cn=masterdn\n");
print(LDIF "ibm-slapdMasterPW: masterdnpwd \n");
print(LDIF "objectclass: ibm-slapdReplication\n");
print(LDIF "objectclass: ibm-slapdConfigEntry\n");
print(LDIF "objectclass: top\n");
close(LDIF);

$baseauth = "-h host1 -p 20389 -D cn=rootdn -w rootdnpwd";
$cmd = "$ITDS/bin/idsldapadd $baseauth -i $ldif";
print("Executing $cmd ...\n");
system($cmd);

$baseauth = "-h host2 -p 20389 -D cn=rootdn -w rootdnpwd";
$cmd = "$ITDS/bin/idsldapadd $baseauth -i $ldif";
print("Executing $cmd ...\n");
system($cmd);

配置复制规则

以下程序配置 inst1 和 inst2 之间的复制规则。该脚本应同时在两个实例主机上运行。参数(如 <serverId-inst2>)必须在基础实例配置中查询。确保正确映射实例和服务器 ID (serverID)。您可以使用以下命令获取服务器 ID:

$ITDS/bin/ldapsearch -h host1 -p 20389 -s base objectclass=* ibm-serverId

从 host1 上的 inst1 实例获取服务器 ID。

$ITDS/bin/ldapsearch -h host2 -p 20389 -s base objectclass=* ibm-serverId

从 host2 上的 inst2 实例获取服务器 ID。

#!/usr/bin/perl

$ldif = "$SOURCE/logs/Replication_Agreement.ldif";
open(LDIF,"> $ldif");

print(LDIF "dn: ibm-replicaGroup=default,$SUFFIX\n");
print(LDIF "objectclass: top\n");
print(LDIF "objectclass: ibm-replicaGroup\n");
print(LDIF "ibm-replicaGroup: default\n");
print(LDIF "\n");
print(LDIF "dn: cn=ReplicaBindCredentials,cn=replication,cn=IBMpolicies\n");
print(LDIF "objectclass: ibm-replicationCredentialsSimple\n");
print(LDIF "cn: ReplicaBindCredentials\n");
print(LDIF "replicaBindDN: cn=masterdn\n");
print(LDIF "replicaCredentials: masterdnpwd\n");
print(LDIF "description: Bind Credentials on peer to bind to each other.\n");
print(LDIF "\n");
print(LDIF "dn: ibm-replicaServerId=<serverId-inst2>,ibm-replicaGroup=default,$SUFFIX\n");
print(LDIF "objectclass: top\n");
print(LDIF "objectclass: ibm-replicaSubentry\n");
print(LDIF "ibm-replicaServerId: <serverId-inst2>\n");
print(LDIF "ibm-replicationServerIsMaster: true\n");
print(LDIF "cn: inst2\n");
print(LDIF "description: inst2 ibm-replicaSubentry\n");
print(LDIF "\n");
print(LDIF "dn: ibm-replicaServerId=<serverId-inst1>,ibm-replicaGroup=default,$SUFFIX\n");
print(LDIF "objectclass: top\n");
print(LDIF "objectclass: ibm-replicaSubentry\n");
print(LDIF "ibm-replicaServerId: <serverId-inst1>\n");
print(LDIF "ibm-replicationServerIsMaster: true\n");
print(LDIF "cn: inst1\n");
print(LDIF "description: inst1 ibm-replicaSubentry\n");
print(LDIF "\n");
print(LDIF "dn: cn=inst2,ibm-replicaServerId=<serverId-inst1>,
ibm-replicaGroup=default,$SUFFIX\n");
print(LDIF "objectclass: top\n");
print(LDIF "objectclass: ibm-replicationAgreement\n");
print(LDIF "cn: inst2\n");
print(LDIF "ibm-replicaConsumerId: <serverId-inst2>\n");
print(LDIF "ibm-replicaUrl: ldap://host2:20389\n");
print(LDIF "ibm-replicaCredentialsDN: cn=ReplicaBindCredentials,
cn=replication,cn=IBMpolicies\n");
print(LDIF "description: inst1 to inst2 agreement\n");
print(LDIF "\n");
print(LDIF "dn: cn=inst1,ibm-replicaServerId=<serverId-inst2>,
ibm-replicaGroup=default,$SUFFIX\n");
print(LDIF "objectclass: top\n");
print(LDIF "objectclass: ibm-replicationAgreement\n");
print(LDIF "cn: inst1\n");
print(LDIF "ibm-replicaConsumerId: <serverId-inst1>\n");
print(LDIF "ibm-replicaUrl: ldap://host1:20389\n");
print(LDIF "ibm-replicaCredentialsDN: cn=ReplicaBindCredentials,
cn=replication,cn=IBMpolicies\n");
print(LDIF "description: inst2 to inst1 agreement\n");
print(LDIF "\n");
close(LDIF);

$cmd = "$ITDS/sbin/idsslapd -I inst1 -k"; # stop LDAP
print("Executing $cmd ...\n");
system("$cmd");			# stop LDAP

print("Loading Replication agreement $ldif …\n")
$cmd = "$ITDS/sbin/idsldif2db -r no -i $ldif -I inst1";
print("Executing $cmd ...\n");
system($cmd);

print("Starting LDAP server ...\n");
$cmd = "$ITDS/sbin/idsslapd -I inst1";    # start LDAP
print("Executing $cmd ...\n");
system("$cmd");			# start LDAP

在 host2 上执行同一序列

$cmd = "$ITDS/sbin/idsslapd -I inst2 -k"; # stop LDAP
print("Executing $cmd ...\n");
system("$cmd");			# stop LDAP

print("Loading Replication agreement $ldif …\n")
$cmd = "$ITDS/sbin/idsldif2db -r no -i $ldif -I inst2";
print("Executing $cmd ...\n");
system($cmd);

print("Starting LDAP server ...\n");
$cmd = "$ITDS/sbin/idsslapd -I inst2";    # start LDAP
print("Executing $cmd ...\n");
system("$cmd");			# start LDAP

准备 LDAP 客户端配置

设置服务器配置,支持下列对默认 aix2307rfc 模式的两个小幅更改:

  1. 密码过期不再以从 1970 年开始的天数为单位,而是以秒为单位。
  2. 对于组来说,使用 fulldn memberuid,而不是 userid membernames
    当将 LDAP 用于其他应用程序(如 IBM WebSphere®)时,这种设置对于定义 IBM WebSphere Console 用户管理授权是非常有用的。在 LDAP 中组的配置是默认配置。
cn=group1,ou=Groups,ou=SYS1,o=ORG,c=US
cn=group1
objectClass=aixauxgroup
objectClass=posixgroup
objectClass=top
gidnumber=1142
memberuid=user1


使用 memberfulldn=yes 选项,如下列代码所示。

cn=group1,ou=Groups,ou=SYS1,o=ORG,c=US
cn=group1
objectClass=aixauxgroup
objectClass=posixgroup
objectClass=top
gidnumber=1142
memberuid=uid=user1,ou=People,ou=SYS1,o=UBS,c=COM

要准备客户端,使用下列程序配置所有参与的 LDAP 客户端。

#!/usr/bin/perl
$cfg = "/etc/security/ldap/2307aixuser.map";
open(ETC,"< $cfg");
@etc = <ETC>;
close(ETC);
chomp(@etc);

$updatecfg = 0;
$refreshldap = 0;

foreach $line (@etc) {
if ( $line =~ /^lastupdate/ ) {

	@row = split(" ",$line) ;

	if ( $row[-1] !~ /seconds/ ) {
		$updatecfg = 1;
	}
}
}

if ( $updatecfg ) {
open(ETC,"> $cfg");
foreach $line (@etc) {
	if ( $line =~ /^lastupdate/ ) {
		print("Setting LDAP user mapping to accept seconds ...\n");
		print(ETC "lastupdate	 SEC_INT shadowlastchange s seconds\n");
	} else {
		print(ETC "$line\n");
	}
}
close(ETC);
}

$cfg = "/etc/security/ldap/ldap.cfg";
open(ETC,"< $cfg");
@etc = <ETC>;
close(ETC);
chomp(@etc);

$updatecfg = 0;
foreach $line (@etc) {
if ( $line =~ /^memberfulldn:/ ) {

	@row = split(":",$line) ;

	if ( $row[1] !~ /yes/ ) {
		$updatecfg = 1;
	}
}
}

if ( $updatecfg ) {
open(ETC,"> $cfg");
foreach $line (@etc) {
	if ( $line =~ /^memberfulldn:/ ) {
		print("Setting LDAP configuration to accept fulldn ...\n");
		print(ETC "memberfulldn: yes\n");
	} else {
		print(ETC "$line\n");
	}
}
close(ETC);
}

$masters = "host1,host2";
$cmd = "/usr/sbin/mksecldap -c -a cn=rootdn -p rootdnpwd -d $SUFFIX -h $masters -n 20389";
print("INFO: configuring all nodes to listen to $masters peer-2-peer ...\n");
print("Executing $cmd ...\n");
system("$cmd");
清单 13. 将 root 用户添加到 LDAP
#!/usr/bin/perl
$rootuser = `/usr/sbin/lsuser –R files root`;
chomp($rootuser);

print("ROOTUSER = $rootuser\n");

$rootuser =~ s/^root //;
$rootuser =~ s/SYSTEM=LDAP or compat //;
$rootuser =~ s/SYSTEM=LDAP //;
$rootuser =~ s/SYSTEM=compat //;
$rootuser =~ s/SYSTEM=LDAP or compat//;
$rootuser =~ s/SYSTEM=LDAP//;
$rootuser =~ s/SYSTEM=compat//;
$rootuser =~ s/auth1=(\w+)/auth1=NONE/;
$rootuser =~ s/auth2=(\w+)/auth2=NONE/;
$rootuser =~ s/registry=(\w+)//;
$rootuser =~ s/(\w+)=(\s|$)//g;             # remove all empty attributes
$rootuser =~ s/(\w+)_login=([\w-]+)\s//g;   # remove all session status attributes

print("ROOTUSER = $rootuser\n");

$cmd = "/usr/bin/mkuser –R LDAP $rootuser root";
logger("Creating root: $cmd");
system("$cmd");

从 LDAP 中定期提取用户

为了保存一组最新的、基于文件的本地注册表,要预先安排从 LDAP 中提取以确保保存了一天中对 LDAP 进行的所有更改。使以下脚本成为 crontab 或另一个调度程序的一部分。选择最适合您的组织需求的频率。

Download script

强制启动文件注册表

为了确保启动进程永远运行,不管 LDAP 是否运行,都要告诉 AIX 总是在 registry=files 模式下启动。

为此,/etc/inittab 文件必须使用下列内容更新:

/usr/sbin/mkitab –I rctcpip 
"usrmgt:23456789:wait:/opt/usrmgt/bin/setregfiles >/dev/console 2>&1"

其中,/opt/usrmgt/bin/setregfiles 如下所示:

#!/usr/bin/ksh
		
/usr/bin/chsec -f /etc/security/user -s default -a registry=files
/usr/bin/chsec -f /etc/security/group -s default -a registry=files

激活 RSCT 监控

设置中的最后一步是 RSCT 配置。该配置监控 LDAP 的可用性。如果 LDAP 无法访问,registry 参数将设置为 files

第 3.1 章中指定了在 IBM.Sensor 中定义的基础监控程序 $MON/checkldap。该示例中的监控间隔时间设定为 10 秒。谓词 String==\"NOK\" 基本上是一个虚拟谓词。将该谓词包含在内,以便 IBM.Sensor 资源监控程序子系统能用实际的 LDAP 状态信息来填充字符串,这样可供响应脚本参考。该谓词不用于实际监控,这里使用的是 Int32 参数。

第 3.2 章列出了响应脚本 $MON/restartldapclient

#!/usr/bin/perl
$RSCT = "/usr/sbin/rsct/bin";
$MON = "/opt/usrmgt/mon";
$IV = "Name==\"CheckLDAP\"";
$PRED = "String==\"NOK\" || Int32>0";
system("$RSCT/mksensor -i 10 -e 0 CheckLDAP $MON/checkldap");
system("$RSCT/mkresponse -n 'SetLDAPEnvironment' -s '$MON/restartldapclient' -e b 
RestartLDAPClient");
system("$RSCT/mkcondition -r IBM.Sensor -m l -S c -s '$IV' -e '$PRED' LDAP_PREP");
system("$RSCT/mkcondresp LDAP_PREP RestartLDAPClient");
system("$RSCT/startcondresp LDAP_PREP RestartLDAPClient");

RSCT 脚本

RSCT LDAP 监控脚本 /opt/usrmgt/mon/checkldap

#!/usr/bin/perl

$ITDS = "/opt/IBM/ldap/V6.3";		# LDAP Source Code Directory
$SUFFIX = "o=ORG,c=US";			# Your LDAP Suffix
$SOURCE = "/opt/usrmgt";			# User Management Code directory
$LOG    = "$SOURCE/mon/CheckLDAP.log";	# Monitor Log file

$running = 0;					# initialization of runtime vars
$userregerror = 0;
$usersyserror = 0;
$groupregerror = 0;

$LDAPH1	= "host1";
$LDAPH2	= "host2";
$LDAPI1	= "inst1";
$LDAPI2	= "inst2";
$LDAPP1	= 20389;
$LDAPP2	= 20389;

# Note that coding passwords in scripts is not recommended
# but you could replace this with your own password retrieval method
$pwd = "rootdnpwd";			# LDAP rootdn password

$arg = $ARGV[0];			# Testflag for command line execution

if ( $arg eq "offline" ) {
	$termoutput = 1;
}

#Check status of the LDAP servers and fill the errormessage with the status information
for ( $i=1 ; $i<=2 ; $i++) {
$host = "LDAPH${i}";
$inst = "LDAPI${i}";
$port = "LDAPP${i}";

# Check for a valid root user entry in LDAP
$cmd = "$ITDS/bin/ldapsearch -h $$host -p $$port -D cn=rootdn -w '\?' -b $SUFFIX 
uid=root userpassword";

# Use this method to parse the rootdn password, without risking that the
# password appears in the process list
#print("CMD = $cmd writing to $LOG.$$inst using $pwd\n");
open(IDS,"| $cmd > $LOG.$$inst 2>&1");
print(IDS "$pwd\n");
close(IDS);
open(IDS,"< $LOG.$$inst");
@ids = <IDS>;
close(IDS);
chomp(@ids);
shift(@ids);

#unlink("$LOG");
if ( $#ids == 1 ) {
	foreach $line (@ids) {
	#print("LINE = $line\n");
		if ( $line =~ /userpassword/ ) {
			@row = split("=",$line);
			if ( $row[1] =~ /crypt/ ) {
			$running = 1;
			$errormessage .= "# $$inst,$$port,$$host=1 ";
			} else {
			$errormessage .= "# $$inst,$$port,$$host=2 '";
			}
		}
		}
} else {
	$errormessage .= "# $$inst,$$port,$$host=0 ";
}
}

# Check status of the registry and SYSTEM parameters in /etc/security/user and group
open(SEC,"/usr/bin/lssec -c -f /etc/security/user -s default -a registry -a SYSTEM |");
@sec = <SEC>;
close(SEC);
chomp(@sec);

foreach $line (@sec) {
if ( $line =~ /^default/ ) {
@row = split(":",$line);

# The second field holds the registry value
	if ( $row[1] eq "LDAP" ) {
		$userregldapset = 1;
	} elsif ( $row[1] eq "files" ) {
		$userregldapset = 0;
	} else {
		$userregerror = 1;
	}

	$errormessage .= "# userreg=$row[1];";

# The third field holds the SYSTEM value
	if ( $row[2] eq "\"LDAP or compat\"" ) {
		$usersysldapset = $running;
	} else {
		$usersyserror = 1;
	}

	$row[2] =~ s/"//g;
	$errormessage .= "SYSTEM='$row[2]';";
}
}

open(SEC,"/usr/bin/lssec -c -f /etc/security/group -s default -a registry |");
@sec = <SEC>;
close(SEC);
chomp(@sec);

foreach $line (@sec) {
if ( $line =~ /^default/ ) {
	$line =~ s/"//g;
	@row = split(":",$line);
	if ( $row[1] eq "LDAP" ) {
		$groupregldapset = 1;
	} elsif ( $row[1] eq "files" ) {
		$groupregldapset = 0;
	} else {
		$groupregerror = 1;
	}

	$errormessage .= "groupreg='$row[1]'";

}
}

$action = 0;

if ( $running ) {
if ( not ${userregldapset} || not ${usersysldapset} || not ${groupregldapset} ) {
	$action = 1;
}
} else {
if ( ${userregldapset} || ${usersysldapset} || ${groupregldapset} ) {
	$action = 1;
}
}

if ( $groupregerror || $userregerror ) {
$action = 1;
}

if ( $userregerror || $usersyserror || $groupregerror ) {
$actionstring = "-${running}${userregerror}${usersyserror}${groupregerror}"
} else {
$actionstring = "${running}${userregldapset}${usersysldapset}${groupregldapset}"
}

$errormessage = "$actionstring $errormessage";

if ( $termoutput ) {
print("$running\n");
print("Int32=$action String=\"$errormessage\"\n");
} else {
print("Int32=$action String=\"$errormessage\"");
}

在 RSCT 通知后恢复脚本:/opt/usrmgt/mon/restartldapclient

#!/usr/bin/perl

$reg{0} = "files";              # if $ldap is down
$reg{1} = "LDAP";               # if $ldap is up
$sys{0} = "LDAP or compat";     # if $ldap is down
$sys{1} = "LDAP or compat";     # if $ldap is up

$hostname = `/usr/bin/hostname -s`;
chomp($hostname);

if ( $ENV{ERRM_VALUE} ) {			# Take ERRM_VALUE from notifier
$string = "$ENV{ERRM_VALUE}";
} else {					# else simulate one for test

# Set a string fixed
# String = 0000 # host1-e0 NOT running # host2-e0 NOT running # User: registry is
'files' SYSTEM is 'LDAP or compat' , Group: registry is 'files'

# or get a string from IBM.Sensor
open(LSS,"/usr/sbin/rsct/bin/lssensor CheckLDAP |");
@lss = <LSS>;
close(LSS);
chomp(@lss);

foreach $line (@lss) {
	$line =~ s/ *//;
	if ( $line =~ /^String/ ) {
		$string = $line;
		$string =~ s/String = //;
		print("STRINGLINE = $string\n");
	}
}
}

@row = split("#",$string);
$ldapstatus = "$row[0]";
$ldapstatus =~ s/ //;

$group = "/etc/security/group";
$users = "/etc/security/user";

$debug = 1;

# 1=LDAP,2=UserReg,3=UserSys,4=GroupReg

if ( $ldapstatus =~ /^-/ ) {
$ldapstatus =~ s/^-//;
$ldap = substr($ldapstatus,0,1);
$userreg = substr($ldapstatus,1,1);
$usersys = substr($ldapstatus,2,1);
$groupreg = substr($ldapstatus,3,1);
setuserreg($reg{$ldap}) if ( $userreg );
setusersys($sys{$ldap}) if ( $usersys );
setgroupreg($reg{$ldap}) if ( $groupreg );
} else {
$ldap = substr($ldapstatus,0,1);
$ldap += 0;
$userreg = substr($ldapstatus,1,1);
$userreg += 0;
$usersys = substr($ldapstatus,2,1);
$usersys += 0;
$groupreg = substr($ldapstatus,3,1);
$groupreg += 0;

if ( $ldapstatus eq "0000" || $ldapstatus eq "1111" ) {
	#print("NO CHANGE NEEDED\n");
	$debug = 0;
} else {
	$doit = ${ldap}^${userreg} ;
	setuserreg($reg{$ldap}) if ( $doit );
	$doit = ${ldap}^${usersys} ;
	setusersys($sys{$ldap}) if ( $doit );
	$doit = ${ldap}^${groupreg} ;
	setgroupreg($reg{$ldap}) if ( $doit );
}
}

if ( $userchange || $groupchange ) {
system("/usr/sbin/restart-secldapclntd");
}

sub setuserreg {
my $registry = $_[0];

$cmd = "/usr/bin/chsec -f $users -s default -a registry=$registry";
system("$cmd");
$userchange = 1;
}

sub setusersys {
my $SYSTEM = $_[0];

$cmd = "/usr/bin/chsec -f $users -s default -a SYSTEM=\"$SYSTEM\"";
system("$cmd");
$userchange = 1;
}

sub setgroupreg {
my $registry = $_[0];

$cmd = "/usr/bin/chsec -f $group -s default -a registry=$registry";
system("$cmd");
$groupchange= 1;
}

参考资料

学习

  • AIX and UNIX 专区:developerWorks 的“AIX and UNIX 专区”提供了大量与 AIX 系统管理的所有方面相关的信息,您可以利用它们来扩展自己的 UNIX 技能。
  • AIX and UNIX 新手入门:访问“AIX and UNIX 新手入门”页面可了解更多关于 AIX 和 UNIX 的内容。
  • AIX and UNIX 专题汇总:AIX and UNIX 专区已经为您推出了很多的技术专题,为您总结了很多热门的知识点。我们在后面还会继续推出很多相关的热门专题给您,为了方便您的访问,我们在这里为您把本专区的所有专题进行汇总,让您更方便的找到您需要的内容。
  • AIX and UNIX 下载中心:在这里你可以下载到可以运行在 AIX 或者是 UNIX 系统上的 IBM 服务器软件以及工具,让您可以提前免费试用他们的强大功能。
  • IBM Systems Magazine for AIX 中文版:本杂志的内容更加关注于趋势和企业级架构应用方面的内容,同时对于新兴的技术、产品、应用方式等也有很深入的探讨。IBM Systems Magazine 的内容都是由十分资深的业内人士撰写的,包括 IBM 的合作伙伴、IBM 的主机工程师以及高级管理人员。所以,从这些内容中,您可以了解到更高层次的应用理念,让您在选择和应用 IBM 系统时有一个更好的认识。

讨论

  • 加入 developerWorks 中文社区。查看开发人员推动的博客、论坛、组和维基,并与其他 developerWorks 用户交流。

条评论

developerWorks: 登录

标有星(*)号的字段是必填字段。


需要一个 IBM ID?
忘记 IBM ID?


忘记密码?
更改您的密码

单击提交则表示您同意developerWorks 的条款和条件。 查看条款和条件

 


在您首次登录 developerWorks 时,会为您创建一份个人概要。您的个人概要中的信息(您的姓名、国家/地区,以及公司名称)是公开显示的,而且会随着您发布的任何内容一起显示,除非您选择隐藏您的公司名称。您可以随时更新您的 IBM 帐户。

所有提交的信息确保安全。

选择您的昵称



当您初次登录到 developerWorks 时,将会为您创建一份概要信息,您需要指定一个昵称。您的昵称将和您在 developerWorks 发布的内容显示在一起。

昵称长度在 3 至 31 个字符之间。 您的昵称在 developerWorks 社区中必须是唯一的,并且出于隐私保护的原因,不能是您的电子邮件地址。

标有星(*)号的字段是必填字段。

(昵称长度在 3 至 31 个字符之间)

单击提交则表示您同意developerWorks 的条款和条件。 查看条款和条件.

 


所有提交的信息确保安全。


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=10
Zone=AIX and UNIX
ArticleID=851091
ArticleTitle=LDAP 用户管理与 RSCT
publish-date=12102012