由于安全策略的限制,公司所有的linux服务器都设置了sshd_config中的PermitRootLogin no来禁止root用户直接登陆,但同时又由于安全需要,需要定期修改root密码,于是问题来了。在root无法登陆的情况下只能由普通用户登陆后su - 到root然后执行密码修改。于是我想到了如下方案:
for i in `cat server_list`
do ssh user1@$i "su -c 'echo \"newpasswd\" |passwd root --stdin'"
done
但运行提示standard in must be a tty, 通过搜索发现su 所需要标准输入的无法通过管道来获取,必须通过tty及终端。我们有数百台服务器需要这样的操作,每台手工登陆然后切换用户反复输入密码显然不可取。于是我想到了expect,网络上的关于expect的文档相当很少,而且由于这里需要输入两次密码(一次普通用户密码,一次跳转到目标服务器后输入的root用户密码),我尝试了多种expect脚本似乎都很难实现,expect会由于当前shell环境跳转到目标服务器而陷入等待,而设置的timeout值是在当前服务器上似乎无法生效。于是我想到了perl的expect模块,脚本如下。
使用方法:for i in `cat server_list`;do ./expect_login.pl -h $i;done
#!/usr/bin/perl -w
# Author: Ken Zhang @2013-12-29
# Filename: expect_login.pl
use Getopt::Std;
use vars qw/%opt/;
use Expect;
sub init(){
my $opt_string="u:h:p:";
getopts("$opt_string",\%opt) or usage();
#usage() if @ARGV==0;
}
sub usage(){
print STDERR << "EOF"
usage: $0 [-u username] [-p password] [-h hostname]
example: $0 -u root -p password -h localhost.localdomain
EOF
}
sub connect(){
my $password = 'ken.zhang@2014';
my $username = 'ken.zhang';
my $hostname = 'localhost';
my $port='22';
$password = $opt{p} if $opt{p};
$username = $opt{u} if $opt{u};
$hostname = $opt{h} if $opt{h};
my $rootpass='RootP@aaA'; # default password for all servers.
$newrootpass='new_non-prod_password' if $hostname =~ /^b/i; #new root password for non-prod servers
$newrootpass='new_prod_root_password' if $hostname =~ /^a|prd/i; #new root password for prod servers, server name start with 'a' or contains 'prd'
my $cmd="ssh";
my @params=($hostname,'-l'.$username,'-p'.$port);
$ENV{TERM} = "xterm";
my $exp = Expect->new;
$exp = Expect->spawn($cmd,@params);
$exp->log_file("output$$.log","w");
# enable debug information if uncomment below
#$exp->exp_internal(1);
# disable output to stdout if uncomment below. all outputs will go to logfile "output$$.log"
# $exp->log_stdout(1);
$exp->expect(5,
[qr/password/i => sub {my $self=shift; $self->send("$password\n");} ],
[qr/\(yes\/no\)\?/ , sub {my $self=shift; $self->send("yes\n");exp_continue} ]
);
$exp->expect(5,'$');
$exp->send("su -c \"echo $newrootpass|passwd root --stdin\"\n");
$exp->expect(5,'[Pp]assword:');
$exp->send("$rootpass\n");
$exp->send("exit\n") if($exp->expect(5,'$'));
}
&init();
&connect();