在具有多个AIX系统的环境中,通常在不同系统之间的UID(用户ID)和GID(组ID)编号不一致。 由于多种原因,这可能是有问题的,并且是一个值得解决的问题。 本文介绍了AIX上的UID和GID号以及在多个服务器之间存在上述不一致的一些具体问题。 还将解决在AIX上手动更改UID和GID号以创建一致性的问题,以及可以自动执行任务以大规模标准化UID和GID的脚本。
AIX中的UID和GID编号概述
在诸如AIX之类的UNIX系统上,操作系统代表具有UID编号的用户和代表GID编号的组。 您可以通过运行id
命令轻松地在AIX帐户上查看您的UID和GID号。
清单1. id输出
$ id
uid=404(brian) gid=402(testgroup) groups=1(staff)
在上面的清单1中 ,您可以看到brian帐户的UID号是404。该用户还具有一个GID为402的主要组(测试组),并且也是GID 1的一个成员(职员)。
磁盘上存储的每个文件都有一个所有者和一个组,但是用户和组的实际名称未存储在磁盘上。 而是存储代表用户和组的UID和GID号。 您可以通过在文件上运行istat
命令来查看此信息。
清单2. istat输出
$ istat testfile
Inode 131073 on device 10/5 File
Protection: rwxr-----
Owner: 404(brian) Group: 402(testgroup)
Link count: 1 Length 291 bytes
Last updated: Tue Apr 8 09:04:17 MDT 2008
Last modified: Wed Feb 7 13:22:36 MST 2007
Last accessed: Thu Sep 30 12:36:59 MDT 2010
在上面的清单2中 ,您可以看到文件的所有者是UID 404(布莱恩),组是402(testgroup)。
系统上的其他项目(例如包含正在运行的进程的进程表)通过跟踪UID和GID号而不是实际使用用户名和组名来跟踪谁拥有该进程。
问题
如果您有多台服务器,则UID和GID号在服务器之间可能不一致。 默认情况下,当您在AIX中创建用户或组时,它只是为其分配下一个可用的UID或GID号。 如果您的环境中有多台服务器,则服务器之间的UID和GID号可能会很快变得不一致。 这意味着“布莱恩”用户在Server1上的UID为404,在Server2上的UID为406,在Server3上的UID为402。
这可能有两个原因。 在所有服务器上统一一致的UID和GID号的最大原因之一是,您可以移至中央身份验证系统,例如LDAP。 中央认证系统(例如LDAP)通常要求启用LDAP的用户和组在所有LDAP连接的服务器上具有一致的UID和GID。
即使您不希望使用诸如LDAP之类的中央身份验证,您仍然会遇到UID和GID编号不一致的问题。 例如,假设您有一个映射到ServerA的SAN LUN。 该LUN上可能存储了数千个文件。 LUN上存储的每个文件都有文件所有者和组,分别存储为UID和GID号。 因此,如果您使用此LUN并将其从ServerA取消映射并映射到ServerB,则在ServerA和ServerB之间的UID和GID号不一致时,您将遇到问题。 在这种情况下,您可能会遇到一些问题。 如果用户brian在ServerA上是UID 404,而用户bob在ServerB上是UID 404,则在移动LUN之后,用户bob现在拥有所有用户brian的文件。 如果ServerB上没有UID 404,则该文件在ServerB上没有所有者,当您运行ls –al
命令时,您只是将“ 404”作为所有者。
在服务器之间导出NFS共享时,服务器之间的UID / GID编号不一致也可能会引起问题。
手动更改GID和UID号的步骤
在此示例中,您将为用户(布莱恩)更改单个UID,为组(测试组)更改GID。 “布莱恩”用户的原始UID为404,新的UID将为3504。“测试组”将原始GID分组为402,新的GID将为5001。更改GID或UID号是一个多步骤过程。
步骤1.停止应用程序并让用户注销
在更改GID或UID编号之前,重要的是停止所有正在运行的应用程序并使所有用户注销服务器。 进程表根据UID和GID编号跟踪正在运行的进程。 因此,如果在更改这些数字的同时用户正在运行进程,则会发生无法预测的结果。 同样,在以后的步骤中确定文件所有权之前,用户将暂时失去对其文件的访问权限。
步骤2.查找以该组为主要组的用户
系统上的每个用户都有一个在/ etc / passwd文件中定义的主要组。 当您在步骤3中更改GID编号时,AIX会显示一条警告消息,指出它不会使用新的GID更新/ etc / passwd文件。 因此,在更改GID之前,首先要获得将要更改的组作为其主要组的所有用户的列表。 可能没有任何用户以此为主要组,或者可能有多个用户。 要找出答案,请运行下面清单3中的命令:
清单3. lsuser输出
# lsuser -a pgrp ALL | grep pgrp=testgroup
brian pgrp=testgroup
此命令显示系统上有一个以testgroup为主要用户的用户(布莱恩)。 记录此命令显示的用户,因为您需要在以后的步骤中运行命令来修复它们。
步骤3.更改GID号
使用chgroup
命令将testgroup的GID更改为5001(最初是GID 402)。
清单4. chgroup输出
# chgroup id=5001 testgroup
3004-719 Warning: /usr/bin/chgroup does not update /etc/passwd with the new gid.
chgroup打印警告消息,让您知道它没有使用新的GID更新/ etc / passwd。 此警告适用于以该组为主要组的所有用户。 您在步骤2中收集了这些用户的列表,然后在下一步中将解决此问题。
步骤4.修复用户主要组
对于在步骤2中记下的每个用户,运行以下命令来修复其主要组。 请注意,可能没有任何用户将该组作为其主要组,或者可能有多个用户。
清单5. chuser命令更新主组
# chuser pgrp=testgroup brian
此chuser
命令使用测试组的新GID号为brian用户更新/ etc / passwd文件。
步骤5.更改UID号
使用chuser
命令将用户brian的UID更改为UID 3504(最初是404)。
清单6.更改UID的chuser命令
# chuser id=3504 brian
此时,系统上已更改了brian UID和测试组GID。 您可以运行id brian
命令查看新的UID和GID。
清单7. id brian输出
# id brian
uid=3504(brian) gid=5001(testgroup) groups=1(staff)
您尚未完成。 如果在用户主目录中运行ls –al
命令,您很快就会发现问题。
清单8.用户主目录的ls输出
# ls -al /home/brian
drwxr-xr-x 5 404 402 4096 2009-04-08 09:12 .
drwxr-xr-x 10 bin bin 256 2007-03-01 15:06 ..
-rw-r--r-- 1 404 402 10 2007-02-07 13:22 .kshrc
-rwxr----- 1 404 402 291 2007-02-07 13:22 .profile
-rw------- 1 404 402 438 2010-11-10 11:40 .sh_history
如您所见,在正常情况下显示用户和组的位置,现在您只看到先前显示的UID和GID号(404和402)。 如果用户brian登录到系统,则他将不再是这些文件的所有者。 这是因为系统将所有者和组的UID和GID号存储在每个文件上,而不是存储用户名或组名。 在下一步中,您将解决此问题。
步骤6.修复系统上所有文件的用户和组所有权
在此步骤中,您将修复系统上所有文件的用户和组所有权。 这是通过运行两个查找命令来完成的,这些命令搜索具有先前UID和GID的所有文件。 对于找到的满足这些条件之一的每个文件,将使用新的用户和组所有权更新文件。
清单9.查找修复所有权的命令
# find / -group 402 -exec chgrp -h testgroup {} \;
# find / -user 404 -exec chown -h brian {} \;
完成这些命令后,将更正系统上所有针对testgroup组和brian用户的文件的用户和组所有权。 如果在brian用户的主目录上运行ls –al
命令,则可以确认。
流程自动化
前面的步骤需要花费一些时间才能完成,并且您仅更改了单个用户和组的UID和GID。 如果您的环境中有数十台AIX服务器,并且每台服务器有数十个用户和组,那么很明显,手动更改所有UID和GID不切实际。
我编写了一个Perl脚本来自动执行此过程。 您为脚本提供了两个输入文件:一个文件包含更新的UID信息,一个文件包含更新的GID信息。
在UID文件中,每行列出两列信息。 第一列具有要设置的新UID,第二列具有帐户名。 GID文件与此类似。 第一列具有要设置的新GID,第二列具有组名。
下面的清单10显示了这些文件的内容。
清单10. UID和GID文件的内容
# cat uid.txt
3500 megan
3501 todd
3502 app_user
#
# cat gid.txt
5000 app_group
如果脚本在UID或GID文件中为系统上不存在的用户或组找到一行,则仅跳过该行。 这样就可以轻松创建所有系统中所有用户和组的列表,并创建单个UID和GID文件,该文件可用于标准化任何系统上的UID和GID,即使每个系统都没有相同的用户或组。 同样,如果脚本运行并检测到用户或组上系统上的当前UID或GID与输入文件中所需的UID / GID相同,则仅跳过这些行。 这样,即使在某些用户帐户已经具有标准化UID和GID的系统上,也可以使用输入文件的主UID / GID列表运行脚本。
当脚本运行时,它不会更改系统上的任何内容。 它只是从系统中收集用户和组信息,并在屏幕上显示标准化UID和GID号所需的命令。
要运行该脚本,请使用类似于下面清单11的命令。
清单11.运行fix_gid_uid.pl脚本
# ./fix_gid_uid.pl --uidfile uid.txt --gidfile gid.txt
### Commands to update Groups ###
chgroup id=5000 app_group
chuser pgrp=app_group todd
chuser pgrp=app_group megan
chuser pgrp=app_group app_user
chuser pgrp=app_group app_2
chuser pgrp=app_group app_3
chuser pgrp=app_group app_4
find / -group 14 -exec chgrp -h app_group {} \;
### Commands to update Users ###
chuser id=3500 megan
find / -user 406 -exec chown -h megan {} \;
chuser id=3501 todd
find / -user 402 -exec chown -h todd {} \;
chuser id=3502 app_user
find / -user 409 -exec chown -h app_user {} \;
该脚本在屏幕上显示将该系统上的UID和GID标准化为uid.txt和gid.txt输入文件中列出的新UID和GID所需的所有命令。
请注意,输出显示在app_group组的GID更改后,六个用户需要更改其主要组。 这包括uid.txt文件中未包含的用户。 这样做的原因是,一旦组GID更改,所有将该组作为主要组的用户都需要更新其主要组,即使这些用户没有更改其UID。
一旦运行了脚本并验证了输出是正确的,就可以简单地运行命令。 为此,请再次运行脚本并将输出重定向到文件。 使输出文件可执行,然后运行它。
清单12.实际上在系统上进行更改
# ./fix_gid_uid.pl --uidfile uid.txt --gidfile gid.txt > commands
# chmod +x commands
# ./commands
运行时间取决于要更改的组和用户数量以及系统上存在的文件数量。 根据这些因素,时间范围可能在一分钟到一个小时以上不等。 如果您担心运行时间,可以将命令文件分成较小的部分,然后分别运行。 如果维护窗口即将结束,则可以让当前部分结束。 当您有另一个维护窗口时,只需再次运行fix_gid_uid.pl脚本。 它将生成您需要运行的新命令,并且先前已修复的UID和GID将不再在输出中列出。
清单13. fix_gid_uid.pl脚本
#!/usr/bin/perl
# This is unsupported code. This script is provided "as is" without warranty of
# any kind, expressed or implied, including, but not limited to, the implied
# warranty of merchantability or fitness for a particular purpose.
# Use at your own risk.
#
use Getopt::Long;
use User::pwent;
use User::grent;
my ($uid_file, $gid_file);
GetOptions("uidfile=s" => \$uid_file, "gidfile=s" => \$gid_file);
if (!((defined $gid_file) || (defined $uid_file))){
print "Specify at least one arguments: --uidfile <filename> ";
print "AND/OR --gidfile <filename>\n\n";
print "Example: $0 --uidfile uid.txt --gidfile gid.txt\n\n";
print "Format of UID and GID files should be: \n";
print "<Desired GID/UID#> <User/Group Name>\n\n";
print "Example:\n";
print "3000 user1\n";
print "3001 user2\n\n";
exit 1;
}
if (defined $gid_file){
open GIDFILE, "<$gid_file" or die $!;
while (<GIDFILE>){
my $line = $_;
if ($line =~ /(\S+)\s+(\S+)\s*/){
my $gid = $1;
my $group = $2;
while($ent = getgrent()){
my $ent_name = $ent->name;
my $ent_gid = $ent->gid;
if ($ent_gid == $gid){
if ($ent_name ne $group){
print "### Error, Group in $gid_file file ($group, GID: $gid)";
print " conflicts with group on system ($ent_name, GID: $ent_gid)\n";
print "### Exiting program, please fix and rerun\n";
exit 2;
}
}
}
endgrent();
}
}
close GIDFILE;
}
if (defined $uid_file){
open UIDFILE, "<$uid_file" or die $!;
while (<UIDFILE>){
my $line = $_;
if ($line =~ /(\S+)\s+(\S+)\s*/){
my $uid = $1;
my $name = $2;
while($ent = getpwent()){
my $ent_name = $ent->name;
my $ent_uid = $ent->uid;
if ($ent_uid == $uid){
if ($ent_name ne $name){
print "### Error, User in $uid_file file ($name, UID: $uid)";
print "conflicts with user on system ($ent_name, UID: $ent_uid)\n";
print "### Exiting program, please fix and rerun\n";
exit 2;
}
}
}
endpwent();
}
}
close UIDFILE;
}
if (defined $gid_file){
print "\n### Commands to update Groups ###\n\n";
open GIDFILE, "<$gid_file" or die $!;
while (<GIDFILE>){
my $line = $_;
chomp($line);
if ($line =~ /(\S+)\s+(\S+)\s*/){
my $newgid = $1;
my $group = $2;
my $return = system("lsgroup $group >/dev/null 2>&1");
if ($return == 0) {
my $oldgid = `lsgroup -a id $group | cut -f 2 -d =`;
chomp($oldgid);
if ($oldgid != $newgid){
print "chgroup id=$newgid $group\n";
while($ent = getpwent()){
my $ent_user = $ent->name;
my $ent_gid = $ent->gid;
if ($ent_gid == $oldgid){
print "chuser pgrp=$group $ent_user \n";
}
}
endpwent();
print "find / -group $oldgid -exec chgrp -h $group {} \\;\n";
}
}
}
}
close GIDFILE;
}
if (defined $uid_file){
print "\n### Commands to update Users ###\n\n";
open UIDFILE, "<$uid_file" or die $!;
while (<UIDFILE>){
my $line = $_;
chomp($line);
if ($line =~ /(\S+)\s+(\S+)\s*/){
my $newuid = $1;
my $user = $2;
my $return = system("lsuser $user >/dev/null 2>&1");
if ($return == 0) {
my $olduid = `lsuser -a id $user | cut -f 2 -d =`;
chomp($olduid);
if ($olduid != $newuid){
print "chuser id=$newuid $user\n";
print "find / -user $olduid -exec chown -h $user {} \\;\n";
}
}
}
}
close UIDFILE;
}
结论
跨多个AIX服务器具有一致的UID和GID号被认为是最佳实践,并且如本文所证明,这是可以实现的目标。 通过遵循本文中概述的过程,系统管理员可能会通过标准化UID和GID编号来防止将来发生问题。
翻译自: https://www.ibm.com/developerworks/aix/library/au-standardUID/index.html