长话短说:大公司,大量的UNIX / Linux服务器。
我inheritance了几年前留下的一些脚本的责任。 其中之一就是一个脚本,每运行几十个月就可以全局更新所有服务器的root密码。
这个脚本是一团糟的Shell脚本和Expect,它在所有服务器和一个中央命令和控制服务器之间build立的SSH信任上起作用。
问题是,剧本是一个巨大的混乱。 Expect命令试图说明存在于任何UNIX / Linux盒子上的每个可能的“passwd”版本 – 它们有很大不同。
由于我们正在扩大和升级很多基础设施,因此脚本变得非常难以pipe理。
我的问题是:有没有更好的方法来做到这一点? 假设已经build立了SSH信任关系,那么在3000多台服务器上同时更改root密码的最好方法是什么?
使用木偶 。
木偶非常灵活,易于维护和使用SSL。 可能听起来有点矫枉过正,你将不得不花费额外的精力来构buildPuppet系统。
但。 很可能,这不是您将要对这些机器进行的最后一次大规模更新。 当实际的任何大规模更新程序开始和脚本是非常可读/可重用的时候,Puppet会为你节省很多时间。
至less这几年回来,我仍然能够重新使用一些木偶食谱(又名脚本)。 我也用它在一个较小的环境中,确保每台机器实际上都有一个已知的状态 。
我已经多次certificate(在许多公司),所有定制的部署脚本经过一段时间之后,或者下一个人步入时都会变得痛苦。只要你携带手机,旧脚本就会困扰你。
如果你认为这听起来不错,这里有一个很棒的Puppet教程,附带虚拟环境让你开始。
我在Solaris上使用了Perl模块Authen :: PAM,并取得了巨大的成功。 这是一个示例脚本:
#!/usr/bin/perl use Authen::PAM; my $username = 'root'; my $password = '1234567'; die qq{Error: Unknown user\n} unless getpwnam($username); die qq{Error: You must run this as root.\n} unless ($> == 0); my $pamh; sub my_conv_func { my @res; while ( @_ ) { my $code = shift; my $msg = shift; my $ans = ""; if ($code == PAM_PROMPT_ECHO_OFF() ) { if (($msg =~ /^New Password:/i) or ($msg =~ /^Re-enter new Password:/i)) { $ans = $password; } else { die qq{Unknown message: $msg\n}; } } else { print qq{$msg\n}; } push @res, (PAM_SUCCESS(), $ans); } push @res, PAM_SUCCESS(); return @res; } ref($pamh = new Authen::PAM("passwd", $username, \&my_conv_func)) || die "Error code $pamh during PAM init!"; my $res = $pamh->pam_chauthtok; print $pamh->pam_strerror($res),"\n" unless $res == PAM_SUCCESS(); exit 0;
如果你可以编写Perl, Net :: OpenSSH :: Parallel模块允许编写通过SSH很容易地在远程主机上并行执行动作的脚本。
它包含一个示例脚本,用于更改可用作基础的密码。 看起来你有一个异构的环境,你想按types分组主机,并为每一个使用不同的对话处理子。
我不知道“最好”,是否所有的非Linux * nix机器都可以使用,但是你是否看过puppet或cfengine这种活动?
另外还有用于身份pipe理的商业(非常昂贵的)工具,以前我见过/使用过的两个工具是Oracle身份pipe理器和Novel等效工具。
在继续研究这个之后,我学到了一些东西
首先,这是一个非常烦人的任务,特别是在许多不同的环境中自动化。 这个问题最正确的答案可能是@ tomi's:使用Puppet。
最后,我希望让Puppet能够pipe理基础设施,但现在部署到整个企业的UNIX服务器上进行root密码更改并不是一种可行的select。
在阅读了很多手册和大量的Google-fu之后,我设法创build了一个脚本,循环访问目标服务器列表,打开一个SSH连接,然后运行以下任一项:
# Solaris # Generate new pass via crypt(newpass,salt) and insert it into /etc/shadow # AIX $ echo "root:newpass" | chpasswd -f NOCHECK # Linux $ echo "newpass" | passwd root --stdin # IBM VIO (Virtual I/O) $ echo "echo \"padmin:newpass\" | chpasswd -f NOCHECK" | oem_setup_env # IBM HMCs (Hardware Management Consoles) $ chhmcusr -u hscroot -t passwd -v "newpass"
它不仅仅是运行这些命令,而且还有很多function。
我找不到任何简单的方法在Solaris上非迭代地更改密码 – 所以我们要求即时修改/etc/shadow 。
最近我做了这个使用Bash脚本..
#!/usr/bin/env bash # Change Password of Remote Server Using SSH #-------------------------------------------- # Define User which you want to # Change Password in remote server #-------------------------------------------- Uname="root" # Create Password in Encrpyted Form Using below command, # and store in this script # perl -e'print crypt("YourPassword", "salt")' ; echo -e # then copy and past in following varible, # password should be single qouted* Password='safv8d8ESMmWk' Update_Pass() { ssh $ruser@$Server_ip -p $port "echo ${Uname}:${Password} | chpasswd -e" } Show_Help(){ cat <<_EOF Usage $0 [OPTION].. Mandatory arguments to long options are mandatory for short options too. -i, --ip <IP_ADDR_OF_SREVER> IP Address Of Remote Server -u, --user <Username> Username Of Remote Server <Default User is root> -p, --port <port> Port Of Remote Server <Default is 22> Note:- For Security Reason Do Not Provide Password to the script, because it will get save in history, so do not provide it, script will prompt for password Report $0 bugs to [email protected] _EOF } Main() { case $1 in -i|--ip) Server_ip=$2; ruser="$4"; [[ -z $ruser ]] && ruser=root port="$6"; [[ -z $port ]] && port=22 Update_Pass ;; *) Show_Help ;; esac } Main $*
这是迄今为止我的解决scheme。 仍然需要看看它是否适用于多个系统
#!/usr/bin/env bash ChangePassword() { echo "changing password for server $ServerIp" ssh root@$ServerIp "echo root:${NewPassword} | chpasswd" < /dev/null } CreatePassword() { while true; do echo "Please enter the new password :" read -s NewPassword <&0 echo "Confirm the password :" read -s ConfirmPassword <&0 # /proc/${PPID}/fd/0 if [ "$NewPassword" == "$ConfirmPassword" ] then break else echo "Passwords do not match" echo "Try again..." fi done ChangePassword echo "end of createpassword" } SetUpPasswordlessSSH() { echo "enter the old password from server $ServerIp when asked" ssh root@$ServerIp mkdir -p .ssh cat .ssh/id_rsa.pub | ssh root@$ServerIp 'cat >> .ssh/authorized_keys' echo "Passwordless SSH is now available" echo "Now you can change the password" CreatePassword } NoSSH() { echo "Passwordless SSH for this server with ip $ServerIp is not yet set up." read -p "Do you want to set it up now? " -n 1 -r <&0 echo "" if [[ ! $REPLY =~ ^[Yy]$ ]] then break else SetUpPasswordlessSSH fi } AcceptHostKey() { read -p "Do you trust the server? " -n 1 -r <&1 echo "" if [[ ! $REPLY =~ ^[Yy]$ ]] then break else SetUpPasswordlessSSH fi } Main() { while read -r ServerIp <&9 do echo "Server $ServerIp ..." status=$(ssh -o BatchMode=yes -o ConnectTimeout=5 $ServerIp echo ok 2>&1) if [[ $status == ok ]] then echo "creating password" CreatePassword echo "password changed" elif [[ $status == "Permission denied"* ]] then NoSSH elif [[ $status == "Host key verification failed"* ]] then echo "Error: $status" AcceptHostKey else echo "ERROR OCCURED FOR SERVER WITH IP: $ServerIp" echo "Error: $status" fi done 9< servers.txt history -cw clear } Main $*
您可以使用pdsh在多个主机上同时执行您的命令。
除puppet之外:SaltStack另一种方法 – 使用SSH库来自动执行,使用Fabric http://docs.fabfile.org/en/1.6/,Capistrano或类似的,不需要花费太多时间/精力部署的SSH库。
我认为/etc/shadow文件的格式在Linux发行版中是相当标准的。 你可以写一个简单的awk脚本来更新密码。
cat /etc/shadow| awk -v pass='NEWPASSHASH' -v now=`date '+%s'` 'BEGIN{ OFS=FS=":"} /^root/ {$2=pass; $3=now} {print}' > /tmp/shadow && mv /tmp/shadow /etc/shadow
我会确保testing它,所以你不要locking自己;)