둘째 날: use Expect;

저자

@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 권한을 획득하면 사용자의 입력을 받는 간단한 스크립트입니다.

눈여겨볼 함수들은 다음과 같습니다.

그럼 한번 실행해 보겠습니다.

[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 설치

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

blog comments powered by Disqus