LDAP user management with RSCT

A customer example

This article describes how to implement high-availability Lightweight Directory Access Protocol (LDAP) user management for IBM® AIX® using Reliable System Cluster Technologies (RSCT). Contrary to the mainstream LDAP recommendations, the goal of this solution is to provide a LDAP registry for all users, including root. The high-availability implementation should guarantee the availability of the root user (and other system users), at all times.

Peter Kes (peter.kes@ubs.com), AIX Solution Architect, UBS AG

Peter Kes started his IT career as a System Engineer at IBM. He developed many customer solutions in the field of IBM AIX and IBM RS/6000 SP, developed and conducted technical classes and wrote a few IBM Redbooks titles about AIX and service pack (SP) system management. After a brief but intensive period at IBM USA (Poughkeepsie), he started working for UBS in a large data warehouse project. He was and still is responsible for writing, designing, and developing advanced technical solutions for security, web deployment, and system management. You can reach him at peter.kes@ubs.com.



16 October 2012

Also available in Chinese

Design challenges

Design a LDAP user-management solution that can satisfy the following requirements:

  • Serve a clustered environment: The environment is heavily dependent on a consistent registry view; all cluster members must be able to access the same registry information at any time.
  • Include all users and group privileges: Typical LDAP solutions separate the login credentials from the actual group membership. This solution needs to be able to manage both.
  • Manage one registry: Typical LDAP solutions separate system users from common users. This is to make sure that when LDAP is not accessible, the system can still be maintained by the root and other functional users. The starting position of this design is to have all the users in one registry.
  • Highly available and able to recover from LDAP failures: As the design goal is to configure all the users in an LDAP registry, the design should include the guarantee that the root and other system users are available at all times.
  • The ultimate goal is to offshore the AIX registry in a central LDAP server farm.

Environmental and product limitations

Up until this date, our clustered user environment was managed with flat files and an rsync mechanism. For batch changes that occur at regular intervals, a flat file-based solution can work well. The problems occur with irregular changes, such as password changes. There will always be a gap between setting the password on the master and distributing the passwords to all the cluster members. During this time, the cluster applications can fail, when (clustered) authentications take place.

Our security infrastructure heavily depends on providing privileges using group assignments. Due to historic heritage and ease of use, these common user groups are also used for providing similar group privileges to system users. Separating common users from system users is possible, however, the management infrastructure needed to keep both the registries in synchronization, becomes complex and is perhaps not as reliable as expected.

This also applies to separating system users from common users. There is always a certain degree of overlap that becomes hard to manage.

Looking at LDAP as a possible networked, user-management registry, it quickly becomes clear that LDAP has a few availability options, but also lacks essential options. The most important feature that LDAP provides is the concept of a peer-to-peer domain setup. This allows for the configuration of two or more LDAP servers, each one updating its neighbors about LDAP changes. However, if the LDAP is corrupt (in maintenance mode or otherwise not accessible), alternate availability features are not available.

Looking at AIX, the SYSTEM (authentication method) parameter does provide the option to support multiple authentication methods, but the registry parameter supports only one registry. So, in order to tell AIX which registry to use, that is the one that is reliably available, is not provided.

AIX is delivered with an availability framework that can fill in these gaps in system and subsystem features: RSCT.


Design overview

The following figure depicts the principle of the LDAP configuration.

Figure 1. LDAP configuration
LDAP configuration
  • At least two LDAP servers, in a peer-to-peer configuration
  • LDAP configured for AIX user management
    • Include all system accounts, including root
    • Use the memberfulldn=yes option
    • user seconds for password lastupdate stamp
  • RSCT monitoring and programs to set the user-management environment
  • Regular extract from LDAP, applying the result in the flat file registry (/etc/passwd)
  • Add an inittab entry to set the registry to files
  • RSCT monitoring detects the availability of LDAP after RSCT is started and configures the registry parameter back to LDAP.

Requirements

This section explains the availability requirements and the product requirements and also the user-management states that the system can be in.

Availability requirements

Guarantee registry availability, at all time:

  • For the boot process
  • In case of LDAP server failures
  • In case of LDAP accessibility problems or schema errors

Product requirements

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

User management states

The following figure depicts the two states that the system can be in.

Figure 2. User-management states
User-management states

If LDAP is working correctly (in this configuration, if the root password can be retrieved from LDAP), the registry is defined as LDAP.

Note: You can define your own rules to determine if LDAP is available. As the root user is the most important user to be available, monitoring availability in this concept is concentrated on finding the root user information in LDAP.

When a system has just booted, when the network is down or if LDAP is not reachable, the registry parameter is switched to files.


Configuration

The LDAP server configuration, including the peer-to-peer setup is based on various documents from the IBM technical web pages and from IBM Redbooks®.

The AIX LDAP configuration is 90 percent standard and adapted in a few areas to meet our requirements.

The RSCT implementation is completely home built. The monitoring scripts are based on our experiences on how to determine if LDAP servers are accessible.

LDAP servers

The LDAP servers are being configured on two hosts / LPARs. In the examples and scripts following, I use the following variables:

  • inst1 LDAP Server1 instance name
  • inst2 LDAP Server2 instance name
  • host1 LDAP Server1 host name
  • host2 LDAP Server2 host name
  • port1 LDAP Server1 port number, default 20389
  • port2 LDAP Server2 port number, default 20389
  • ldp1 DB2 Server1 port number for LDAP1
  • ldp2 DB2 Server2 port number for LDAP2
  • dbp1 DB2 Server1 server port number
  • dbp2 DB2 Server2 server port number
  • $ITDS /opt/IBM/ldap/V6.3
  • $SUFFIX o=ORG,c=US

Create the inst1 instance on host1

Make sure that the instance owner home directory is local, that is /home and note the password: it should be configured the same for the inst2 instance owner user.

  • Prepare two instance owner users with both the same primary group (inst1, inst2 and for example pgrp=dba),
  • Prepare the root user to be part of the primary group of the instance owners.
  • Prepare the root user to be part of the idsldap group.
  • Make sure that the $DB2INSTPATH/default.env file does not contain the instance inst1 or inst2.
  • Create the directories /db2/inst1/NODE0000 and /db2event/inst1 and make sure that /db2/inst1/NODE0000 and /db2event/inst1 are writable by the instance owner's primary group. After creating the instances, you can set this back to normal.
Listing 1. Create the inst1 instance:
$ITDS/sbin/idsicrt -q -n -I inst1-e idsldapinst1002007 -g inst100slapd -l /home/inst1 
-p 20389 -s 21389 -a 22389 -c 23389 -t inst1

Note the encryption string. It should be the same as the encryption string for the inst2 instance.

Listing 2. Set the rootdn password:
$ITDS/sbin/idsdnpw -q -I inst1 -n -u cn=rootdn -p rootdnpwd
Listing 3. Define the LDAP database:
$ITDS/sbin/idscfgdb -q -n -I inst1 -a inst1 -t dbinst1 -w rootdnpwd -l /db2
Listing 4. Configure the base suffix in LDAP:
$ITDS/sbin/idscfgsuf -q -I inst1 -s $SUFFIX

Prepare LDAP for AIX user management

Listing 5. Load the NIS schema
file="etc/security/ldap/nisSchema.ldif"
$ITDS/bin/ldapmodify -c -v -a -h host1 -p 20389 -D cn=rootdn -w rootdnpwd -f $file
Listing 6. Load the AIX security schema
file="etc/security/ldap/sec.ldif"
$ITDS/bin/ldapmodify -c -v -a -h host1 -p 20389 -D cn=rootdn -w rootdnpwd -f $file

Create the inst2 instance on host2

Perform the following steps to create the inst2 instance on host2.

  1. Prepare two instance owner users with both belonging to the same primary group (inst1, inst2, and for example, pgrp=dba).
  2. Prepare the root user to be part of the primary group of the instance owners.
  3. Prepare the root user to be part of the idsldap group.
  4. Make sure that the $DB2INSTPATH/default.env file does not contain the inst1 or inst2 instance.
  5. Create the /db2/inst2/NODE0000 and /db2event/inst2 directories and make sure that /db2/inst2/NODE0000 and /db2event/inst1 are writable by the instance owner's primary group. After creating the instances, you can set this back to normal.
  6. Make sure that the seed algorithm on the two peer-to-peer LDAP servers is the same. This is defined in the -e flag of the idsicrt command.
Listing 7. Create the inst2 instance:
$ITDS/sbin/idsicrt -q -n -I inst2 -e idsldapinst1002007 -g inst100slapd -l /home/inst2 
-p 20389 -s 21389 -a 22389 -c 23389 -t inst2
Listing 8. Set the rootdn password:
$ITDS/sbin/idsdnpw -q -I inst2 -n -u cn=rootdn -p rootdnpwd
Listing 9. Define the LDAP database:
$ITDS/sbin/idscfgdb -q -n -I inst1 -a inst1 -t dbinst1 -w rootdnpwd -l /db2
Listing 10. Configure the base suffix in LDAP:
$ITDS/sbin/idscfgsuf -q -I inst2 -s $SUFFIX

Prepare LDAP for AIX user management

Listing 11. Load the NIS schema
file="etc/security/ldap/nisSchema.ldif"
$ITDS/bin/ldapmodify -c -v -a -h host1 -p 20389 -D cn=rootdn -w rootdnpwd -f $file
Listing 12. Load the AIX security schema
file="etc/security/ldap/sec.ldif"
$ITDS/bin/ldapmodify -c -v -a -h host1 -p 20389 -D cn=rootdn -w rootdnpwd -f $file

Load the /etc/passwd user base on the inst1 and inst2 instances

Prepare a Perl script with the following program sequence:

#!/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");

Setting up peer-to-peer replication between inst1 and inst2

Defining the base replication context

#!/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);

Configuring the master replication 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);

Configuring the replication agreement

The following program configures the replication agreement between inst1 and inst2. This script should be ran on both of the instance hosts. The parameters, such as <serverId-inst2> must be queried from the base instance configuration. Make sure to map the instance and server ID (serverID) correctly. You can get the server ID with the following command:

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

To get the server ID from the inst1 instance on host1.

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

To get the server ID from inst2 on host2.

#!/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

Perform the same sequence on 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

Prepare LDAP client configuration

The server configuration is set up to support the following two minor changes to the default aix2307rfc schema:

  1. Password expiration in seconds, not in days from 1970.
  2. fulldn memberuid, instead of userid membernames for groups
    This setting is particularly useful when using LDAP for other applications, such as IBM WebSphere®, to define user administration authorizations for the IBM WebSphere Console.Default configuration for a group in LDAP:
cn=group1,ou=Groups,ou=SYS1,o=ORG,c=US
cn=group1
objectClass=aixauxgroup
objectClass=posixgroup
objectClass=top
gidnumber=1142
memberuid=user1


Using the memberfulldn=yes option, this looks as shown in the following code.

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

In order to prepare the client for this, use the following program to configure the participating LDAP clients.

#!/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");
Listing 13. Adding the root user to 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");

Extracting users from LDAP on a regular basis

In order to keep an up-to-date set of local, files-based registry, an extract from LDAP can be scheduled to make sure that all the changes occurring in LDAP during a day are saved. Make the following script to be part of crontab or another scheduler. Choose a frequency that fits best to your organization's needs.

Download script

Forcing file registry at boot

In order to make sure that the boot process will always work, no matter if LDAP runs or not, is to tell AIX to always boot in the registry=files mode.

For this, the /etc/inittab file must be updated with:

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

where /opt/usrmgt/bin/setregfiles looks as follows:

#!/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

Activating RSCT monitoring

The last step in the setup is the configuration of RSCT. This configuration monitors the availability of LDAP and if LDAP is not reachable, the registry parameter will be set to files.

The base monitor $MON/checkldap, defined in IBM.Sensor, is specified in Chapter 3.1. The monitoring interval in this example is set to 10 seconds. The predicate String==\"NOK\" is basically a dummy predicate. It is included so that the IBM.Sensor resource monitor subsystem can fill the string with the actual LDAP status information, so that it becomes available in the response script for reference. It is not used for the actual monitoring, for this, the Int32 parameter is used.

The response script $MON/restartldapclient is listed in Chapter 3.2.

#!/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 scripts

RSCT LDAP monitoring script, /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\"");
}

Recovery script after RSCT notification: /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;
}

Comments

developerWorks: Sign in

Required fields are indicated with an asterisk (*).


Need an IBM ID?
Forgot your IBM ID?


Forgot your password?
Change your password

By clicking Submit, you agree to the developerWorks terms of use.

 


The first time you sign into developerWorks, a profile is created for you. Information in your profile (your name, country/region, and company name) is displayed to the public and will accompany any content you post, unless you opt to hide your company name. You may update your IBM account at any time.

All information submitted is secure.

Choose your display name



The first time you sign in to developerWorks, a profile is created for you, so you need to choose a display name. Your display name accompanies the content you post on developerWorks.

Please choose a display name between 3-31 characters. Your display name must be unique in the developerWorks community and should not be your email address for privacy reasons.

Required fields are indicated with an asterisk (*).

(Must be between 3 – 31 characters.)

By clicking Submit, you agree to the developerWorks terms of use.

 


All information submitted is secure.

Dig deeper into AIX and Unix on developerWorks


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=1
Zone=AIX and UNIX
ArticleID=839207
ArticleTitle=LDAP user management with RSCT
publish-date=10162012