使用 chpasswd 和 pwgen维护密码

On 2010年06月18日, in linux, by netoearth

在迁移用户时,必须提供新的初始密码。这可以使用 chpasswd 来完成。用户常常用词典单词作为密码,但是这种密码安全性不好。可以使用密码生成器 pwgen 方便地解决这个问题。

在迁移用户时,应该提供初始密码。然后,用户通常通过电子邮件或电话得知新密码。如果启用了安全密码策略规则,当用户登录时,会提示他们修改密码。 还可以通过特殊请求要求用户在本地或远程重新设置账户密码。完成这些密码更改要花费不少时间,而且是重复性的。但是,通过使用 AIX 实用程序 chpasswd(Linux 系统也附带这个工具),可以简便地重复执行这些任务。

但是,选择良好的密码是另一个问题。把多个密码同时更改或重新设置为词典单词甚至用户名很容易,但这不是良好的密码策略。密码应该很容易记 住,用户不需要把它写下来。一些人认为对于普通用户账户使用随机密码是良好的安全策略。我认为,只有在用户能够记住自己的密码的前提下,这才是好策略。如 果用户把密码写在便条上,他们很可能把便条放在便于拿到的地方,比如贴在桌子下面或放在抽屉里面。潜在的入侵者很容易在这些地方找到用户的密码。

对于系统管理员或希望生成密码的用户来说,可以使用实用程序 pwgen 生成可发音和不可发音的密码。pwgen 可以生成一个或多个密码,可以指定不同的密码长度,可以指定必须包含大写字母或数字。

在本文中,我将演示 chpasswd 和 pwgen 的用法,以及如何以交互或批处理方式使用它们处理密码更改。我是在几年前把用户迁移到 LDAP 环境时首次接触到 chpasswd 的。现在,我经常使用它以非交互方式更改本地或远程账户密码。下载 pwgen 的二进制代码或源代码的地址参见 参 考资料

使用 chpasswd

可以使用 chpasswd 方便地更改单个或多个账户密码。这意味着不需要像平时一样在命令行上重复输入密码。尽管可以以交互方式使用 chpasswd,但是我建议以非交互方式使用它。使用 chpasswd 是一种快速更改密码的方法。

使用 chpasswd 的格式是:

chpasswd  -f <pwdadm flags> -c

其中:
-f 可以解析 pwdadm 标志
-c 清除密码标志

从标准输入读取以下格式的用户名和密码:

user_name:user_password

假设我们希望创建三个用户账户,用户名分别是 alpha、bravo 和 charlie。

这些账户上还没有设置密码,可以通过查询 /etc/passwd 文件和执行 pwdadm 命令看出这一点:

# tail /etc/passwd
alpha:*:209:1:alpha.apps:/home/alpha:/usr/bin/ksh
bravo:*:210:1:bravo.suppt:/home/bravo:/usr/bin/ksh
charlie:*:211:1:charlie.suppt:/home/charlie:/usr/bin/ksh

注意,passwd 文件中的第二栏是 ‘*’,这表明还没有设置密码。通过用 pwdadm 命令查询用户也可以确认这一情况:

 # pwdadm -q alpha
alpha:

因为没有设置密码,所以没有产生输出。如果有密码,’lastupdate’ 字段的值应该是设置密码时的 UTC 时间戳。

我将演示使用 chpasswd 为这些用户设置初始密码的两种方法。在下面的示例中,在命令行上回显用户名 alpha 和密码 mypasswd,然后通过管道连接到 chpasswd:

echo "alpha:mypasswd" | chpasswd

还可以把登录名和密码信息包含在一个字符串中,然后通过管道连接到 chpasswd,如下所示:

# detail="charlie:charpw"
echo $detail | chpasswd

现在,通过 pwdadm 查询用户 alpha,可以看到已经设置了标志值 ADMCHG,这是重新设置密码时 chpasswd 的默认设置:

# pwdadm -q alpha
alpha:
        lastupdate = 1265765265
        flags = ADMCHG

ADMCHG 表示当用户 alpha 下一次尝试用设置的初始密码 (mypasswd) 登录时,会强迫用户 alpha 更改密码。这也适用于设置了 ADMCHG 标志的其他账户。

还可以用保存在文件中的信息更改密码。例如,考虑 pass 文件中的以下内容:

# cat pass
bravo:bravpass
charlie:charpass

在前面的 pass 文件中,用户 bravo 将把密码改为 bravpass,用户 charlie 将把密码改为 charpass。要想执行密码更改,只需对这个文件执行 cat 并通过管道连接到 chpasswd,如下所示:

# cat pass | chpasswd

还可以把文件重定向到 chpasswd 进行处理。在这个示例中,通过指定 ‘c-‘ 选项(清除密码标志),指定用户 bravo 和 charlie 不必更改密码,如下所示:

# chpasswd -c < pass

使用 pwdadm 查询用户 charlie,会产生以下输出:

# pwdadm -q charlie
charlie:
        lastupdate = 1265853052

注意 chpasswd 命令中的清除标志选项;它清除字段中的所有标志值。

pwdadm 输出中的 lastupdate 值表示最近设置或更改密码的时间。以秒为单位的 UTC 时间戳需要转换为更有意义的日期时间戳。

下面两个命令都返回最近更新或初始设置密码的时间(如果有密码的话)。在这个示例中,查询用户 alpha 最近的密码更新:

# lssec -f /etc/security/passwd -s alpha -a lastupdate
alpha lastupdate=1265940457

# pwdadm -q alpha
alpha:
       lastupdate = 1265940457
       flags = ADMCHG

可以使用 perl 或 gawk 把 UTC 转换为日期时间戳,下面两个示例产生相同的结果:

# perl -e 'use POSIX;print ctime(1265940457)'
Thu Feb 11 20:07:37 2010


# gawk 'BEGIN {print strftime("%c",1265940457)}'
Thu Feb 11 20:07:37 GMT 2010

安装 pwgen

pwgen 源代码的当前版本是 2.0.6。二进制版本是 2.0.5。在这里,我将使用源代码。

下载源代码之后,解压并编译它:

# gunzip pwgen-2.06.tar.gz
# tar -xvf pwgen-2.06.tar
# cd pwgen-2.06
#./configure
# make
# make install

pwgen 二进制代码将安装在 /usr/local/bin 中。


使用 pwgen

pwgen 可以生成难以记住的随机密码或容易记住的密码。可以以交互方式使用它,也可以通过脚本以批模式使用它。

在默认情况下,pwgen 向标准输出发送许多密码。一般来说,我们不需要这种结果;但是,如果希望选择要手工输入的一次性密码,这种输出是有用的。在生成密码时,pwgen 在默认情况下尝试在密码中包含数字和大写字母。

格式是:

pwgen <options> <password_length> <number_of_passwords>

常用选项是:

-1 每 行输出一个密码
-c 必须包含大写字母
-n 必 须包含数字
-s 随机密码

以下命令输出一个 8 字符的密码:

# pwgen 8 1
eej3eeZu

以下命令输出三个 7 字符的密码并要求必须使用一个大写字母:

# pwgen -c 7 3
ohw4Aj7 Pei0obe gaw4De4

以下命令输出 10 个 8 字符的密码并要求必须使用一个大写字母和数字:

# pwgen -c -n 8 10
zum5Shei Choo6Eih Ub5uagei Ooxu6ohs Eix9xeip iV4yoeph Io3aeGhe taiTh6ia
cuere1AW phai9Pai

pwgen 会判断您是否通过 tty 执行它。如果不是,它在默认情况下(不传递选项)只生成一个密码。这便于脚本把值存储在变量中,如下所示:

# pass=$(pwgen)
# echo $pass
ohtherah

如果愿意,可以使用反撇号进行命令替换。以下命令产生相同的结果:

                pass=`pwgen`
                # echo $pass
                oowahxei

当然,可以包含任意数量的密码。在下面的示例中,生成三个密码:

# pass=$(pwgen -c -n 8 3)
# echo $pass
EluBie0z thohku0W Ail3fu3z

下面的命令生成一个真正随机的 8 字符密码:

# pwgen -s 8 1
9bTzZxt9

在使用随机选项时(像前一个示例一样),应该注意一点:这些密码很难记住,如果把它们分配给用户,用户很可能会把它们写下来。根据我的经验, 应该只对应用程序所有者账户使用随机生成的密码。


结合使用

既然我们已经看到了 chpasswd 和 pwgen 的效果,现在就开始为用户设置初始密码。这显然必须通过 清 单 1 这样的脚本来完成。首先,获取需要设置密码的用户的列表;这个列表可以包含在一个文件中(但是这里把用户包含在字符串 $list 中,其中包含三个用户)。在循环处理每个用户时,使用以下命令为每个用户生成一个 8 字符的密码:

pwgen 8 1

第一个检查确认 AIX 主机上是否存在这个用户账户。如果存在,就按照 chpasswd 所需的格式把用户名和生成的密码追加到 passfile 文件中。处理完所有用户之后,通过管道把这个文件连接到 chpasswd。如果用户在 /etc/passwd 中不存在,passfile 中就没有此用户的用户名/密码条目。
清单 1. setpass

				
#!/bin/sh
# setpass

passfile=/home/dxtans/passfile
>$passfile

list="alpha bravo charlie"

for user in $list
 do
   if  ! grep -w ^$user /etc/passwd > /dev/null
   then
    echo "user NOT present: $user"
  else
   echo "user present: $user"
  pass=$(pwgen 8 1)
  echo "$user:$pass">>$passfile
 fi
done
cat $passfile | chpasswd

运行脚本 setpass 之后,生成包含以下内容的 passfile 文件:

# cat passfile
alpha:jiebuzio
bravo:oegaeyay
charlie:ooweipoa

在下一个示例中(见 清 单 2),创建用户 foxtrot 和初始密码并修改登录属性。create_user 脚本演示使用 pwgen 设置密码的一种方式。在这个示例中,密码设置为 8 个字符,包含至少一个大写字母和一个数字:

pwgen -c -n 8 1

首先,创建用户 foxtrot 并把 su 设置为 false。然后,为这个用户账户设置密码,清除所有密码限制标志(这意味着用户 foxtrot 在登录时不会提示他更改密码)。然后设置 gecos 字段,把变量 $user 展开成 foxtrot,在展开的变量后面添加 “apps”。把 maxage 设置为 5,这意味着在前一次更改或设置密码 5 周之后,会迫使用户 foxtrot 更改密码。把 minage 设置为 1,这意味着在一周内用户 foxtrot 不能再次更改密码。最后,设置组成员,即主组为 staff(这是 AIX 的默认设置)。
清单 2. create_user

				
#!/bin/sh
# create_user

user="foxtrot"
pass=$(pwgen -c -n 8 1)
echo "the passwd for $user is: $pass"

echo  "creating user $user..creating password"
mkuser su=false $user
 if [ $? = 0 ]
  then
   echo "$user:$pass" | chpasswd -c
  else
   echo "error: unable to create user $user"
   exit 1
 fi
echo "changing $user attributes..."
  chuser gecos="${user}.apps" $user
  chuser maxage=5 $user
  chuser minage=1 $user

执行脚本 create_user 时,输出是:

# create_user
the passwd for foxtrot is: oiN2hi9r
creating user foxtrot..creating password
changing foxtrot attributes...

通过使用 pwdadm 命令,可以看出密码标志已经清除了:

# pwdadm -q foxtrot
foxtrot:
        lastupdate = 1266174412

应该经常更改根密码,这是一项审计要求。按我的观点,最好的方式是作为根用户通过 ssh 连接每个主机,然后更改密码。但是注意,我们绝不希望忘记这个密码。因此,最好先在本地用 pwgen 生成这个密码,在脚本中硬编码它,执行脚本并把密码保存在安全的物理位置(比如保险柜)之后从脚本中删除密码。不要忘了将新密码告诉其他系统管理员。

一种好做法是把要远程连接的所有主机包含在一个文件中。采用这种方法意味着其他脚本可以读取这个文件,不需要在脚本中手工输入它们。包含主机 的典型文件可以采用以下格式:

# cat all_hosts
host1
host2
host3
host..

清 单 3 展示了一种简便的方法。已经用 pwgen 生成了密码,密码是 ‘tu8ahLae’。对于要连接的每个主机(从 all_hosts 文件读取),使用 here document 方法。这意味着包含在单词 ‘mayday’ 之间的所有命令在远程主机上被读取为标准输入。字符串 root:tu8ahLae 通过管道传递给 chpasswd,如下所示:

echo "root:tu8ahLae" | chpasswd 

清单 3. chpw_root

				
#!/bin/sh
cat all_hosts | while read host
do
echo "[$host]″

ssh -T -t -l root $host<<'mayday'
hostname

echo "root:tu8ahLae" | chpasswd 
 if [ $? != 0 ]
then
echo " password of root change failed $host"
 else
echo " password of root change OK"
fi
mayday
done

chpw_root 脚本假设 ssh 密钥已经从远程主机交换到了驻留脚本的主机。


通知用户

清 单 2 中的脚本创建一个用户并设置初始密码,但是将新密码通知给用户是个手工任务。这常常需要把身份验证信息复制并粘贴到电子邮件中,然后向用户发送电子邮件, 或者通过电话通知他们。更自动化的方法是直接从脚本向用户发送电子邮件。为此,必须建立一个把用户与电子邮件地址关联起来的过程。可以考虑采用的一种方法 是使用一个包含用户 ID 和相应电子邮件地址的文件。另一种方法是把用户的电子邮件地址放在 /etc/password 文件中的 gecos 字段中,采用 <first_name>.<last_name>@<domain> 形式。

dxtans:!:203:1:david.tansley@btinternet.com:/home/dxtans:/usr/bin/ksh

但是,这种方法会增加输入量,尤其是在有许多 AIX 系统的情况下,所以应该只在管理少量系统的情况下考虑采用它。

另一种方法是使用全局的查找文件(我认为这种方法更方便,而且不需要管理 /etc/passwd 文件)。这个文件包含所有可登录用户及其电子邮件地址的列表,如下所示:

#  cat email_lookup
alpha   alpha.apps@mycompany.com
bravo   bravo.suppt@mycompany.com
charlie charlie.suppt@mycompany.com

全局文件 email_lookup 包含所有用户名和电子邮件账户,可以把它传输到所有系统上。只更新一个全局文件方便得多,只需在添加或删除用户时更新它,然后再次通过 scp 把它传输到所有系统。如果在放置 email_lookup 文件的远程系统上没有某个用户,也没关系,因为既然这个用户不存在,在更改密码时就不会查找他。

清 单 4 中的脚本使用前面的 email_lookup 文件寻找用户名和相应的电子邮件地址,然后通知用户账户的更改情况。它从前面生成的 passfile 文件读取用户名和密码,然后通过检查 /etc/passwd 确认这是有效的账户。如果一切正常,那么从 email_lookup 文件中提取出用户的电子邮件地址。脚本假设已经运行了 chpasswd,登录名和密码已经保存在 passfile 文件中了。理想情况下,脚本还应该包含 chpasswd 和 pwgen 例程,以便方便地更改密码。目前,清单 4 中的脚本说明在重新设置或更改密码之后如何自动地通过电子邮件通知用户。
清单 4. email_user

				
#!/bin/sh
passfile=/home/dxtans/passfile
email_lookup=/home/dxtans/email_lookup

IFS=":"
cat $passfile | while read user pass
do
   if grep -w  ^$user /etc/passwd >/dev/null
     then
       echo "$user - found"
        email_name=$(grep -w  ^$user $email_lookup | awk '{print $2}')
          if [ "$email_name" = ""  ]
           then
            echo "lookup failed for this user:$user"
          fi
        mail -s "`hostname` account change" $email_name<<mayday
your account: $user
new password: $pass
mayday

   else
       echo "$user - NOT found"
fi
 done

结束语

系统管理员可以通过结合使用 chpasswd 和 pwgen 自动地设置用户密码。pwgen 生成的密码是不容易猜测的单词,因此确保新密码的安全性更好。我还推荐了两种通知用户密码更改的方法。

Tagged with:  

Comments are closed.