@luzlunz - C, Perl 엔지니어. 근래엔 Ops에 더 가까운 DevOps. PostgreSQL 성능 최적화의 공동 역자, luzluna at gmail.com
원격지의 서버에서 루트권한을 얻어서 무언가 작업을 하려면 상당히 귀찮은 반복 노가다를 하게되곤 합니다.
sudo
가 세팅되어있는 서버라면 -S
옵션을 이용해 간단히 처리할 수 있겠지만,
보안 정책상 이마저도 허락되어있지 않았다면 곤란해지게 됩니다.
이런 경우 사용자의 입력을 그대로 대신해주는 Expect 모듈이 큰 빛을 발합니다.
필요한 모듈은 다음과 같습니다.
직접 CPAN을 이용해서 설치한다면 다음 명령을 이용해서 모듈을 설치합니다.
$ sudo cpan Expect
사용자 계정으로 모듈을 설치하는 방법을 정확하게 알고 있거나 perlbrew를 이용해서 자신만의 Perl을 사용하고 있다면 다음 명령을 이용해서 모듈을 설치합니다.
$ cpan Expect
간단히 su
를 입력하여 root
비밀번호를 자동으로 입력하는 예제를 만들어 보겠습니다.
#!/usr/bin/env perl use strict; use warnings; use Expect; my $rootpass = "mypassword"; my $exp = Expect->new; $exp->spawn("su"); $exp->expect( 10, [ qr/Password:/ => sub { my $exp = shift; $exp->send($rootpass."\n"); exp_continue; } ], [ qr/root.*#/ => sub { my $exp = shift; $exp->interact(); } ], );
Expect
객체를 만든 뒤 su
명령을 실행하고,
Password:
가 나타나면 비밀번호를 입력하고,
root
권한을 획득하면 사용자의 입력을 받는 간단한 스크립트입니다.
눈여겨볼 함수들은 다음과 같습니다.
spawn()
: system()
혹은 qx||
명령을 실행하는것과 비슷하지만 입출력을 Expect
가 처리하도록 한다는 점이 다릅니다.expect()
: spawn으로 획득한 실행 결과의 출력에 따라 자동으로 반응하도록 명령을 등록합니다.send()
: spawn으로 획득한 실행 결과에 특정 문자열을 입력으로 넣어줍니다.interact()
: 이후부터는 사용자가 직접 입출력을 제어하도록 넘겨줍니다.그럼 한번 실행해 보겠습니다.
[test@cent5 ~]$ ./expect.pl Password: [root@cent5 test]# IO::Stty not installed, cannot change mode at ./expect.pl line 12. IO::Stty not installed, cannot change mode at ./expect.pl line 12.
비밀번호를 획득한 뒤 root
프롬프트를 잘 획득했지만 이상한 경고 문구도 나왔습니다.
친절하게도 IO::Stty 모듈이 설치되지 않았다고 알려주네요.
IO::Stty 모듈 모듈을 설치해 봅시다.
직접 CPAN을 이용해서 설치한다면 다음 명령을 이용해서 모듈을 설치합니다.
$ sudo cpan IO::Stty
사용자 계정으로 모듈을 설치하는 방법을 정확하게 알고 있거나 perlbrew를 이용해서 자신만의 Perl을 사용하고 있다면 다음 명령을 이용해서 모듈을 설치합니다.
$ cpan IO::Stty
다시 실행해보죠.
[test@cent5 ~]$ ./expect.pl Password: [root@cent5 test]#
경고 메시지 없이 깔끔하게 잘 실행 되는군요. :)
로컬호스트에서 su
는 그냥 한번 치면 끝나는 일이니까
굳이 이런 모듈이 필요하지는 않겠죠.
하지만 관리해야 할 원격 호스트의 개수가 세 자리 숫자에 육박한다면 어떨까요?
#!/usr/bin/env perl use strict; use warnings; use Expect; my $userid = "bilbo"; my $userpass = "userpass"; my $rootpass = "rootpass"; my $exp = Expect->new; $exp->spawn("ssh", "localhost"); $exp->expect( 10, [ qr/password:/ => sub { my $exp = shift; $exp->send($userpass."\n"); exp_continue; } ], [ qr/$userid.*\$/ => sub { my $exp = shift; $exp->send("su\n"); exp_continue; } ], [ qr/Password:/ => sub { my $exp = shift; $exp->send($rootpass."\n"); exp_continue; } ], [ qr/root.*#/ => sub { my $exp = shift; $exp->interact(); } ], ); print "$0 exit\n";
서비스를 운영하다보면 처한 상황에 따라 열악한 상황에서 많은 서버를 다뤄야할 경우도 가끔 발생하곤 합니다. 마음이야 우리도 Vagrant로 스테이징하고, Chef로 배포하고 싶지만, 레거시(legacy)라는게 그렇게 만만하지 않기 마련이죠. 이럴 때 간단히 Expect 모듈을 이용하면 난관을 쉽게 넘을 수 있는 경우도 있답니다.
앞의 소스를 조금 변형해 10대의 서버의 motd에 한 줄을 추가하는 코드를 작성해보세요.
EOT
X-mas tree
& Llama
ASCII Art by ASCII Art Farts.
Computer ASCII Art by Chris.com.
Font ASCII Art by TAAG.
Artwork by
Inkyung Park
& Keedi Kim.
Designed by
Hojung Youn
& Keedi Kim.
Articles by
Seoul Perl Mongers.
Edited by
Luzluna Park
& Keedi Kim.
Hosting generously sponsored by
Yuni Kim.
Sponsored by
SILEX.
.-''' __ __ / \/ \/ \ =-_- | \. -____- / \ // /|| '' //| //|| == = == ==