6.5. Sendmail의 Virtual Domain 설정

하나의 호스트에 연결된 도메인 nobreak.com 과 kr.freebsd.org 에 대해, 일반적으로 nobreak@nobreak.com 과 nobreak@kr.freebsd.org 의 수신자는 같다. 여기에서는 각각의 도메인별로 aliases 테이블을 갖는 것과 같이, 도메인에 따라 수신되는 편지를 다르게 처리하는 방법에 대해 알아본다. 본 글이 Sendmail을 주제로 하지는 않기에, Sendmail을 어느 정도 알고 있다는 가정으로 골자내용만을 다루도록 하겠다. Sendmail에 익숙치 않다면 내용을 이해하기 위해 RuleSet, m4, aliases, dbm/btree/hash 등에 대한 사전학습이 필요할 수도 있다.

설명하고자 하는 방법은 Sendmail 8.8 이상에서 가능하므로,먼저 다음과 같이 설치된 버젼을 확인한다.

$ telnet localhost smtp
Trying 127.0.0.1...
220 kfug2.kr.freebsd.org ESMTP Sendmail 8.9.3/8.9.3

버젼이 8.8보다 낮다면, ftp://ftp.sendmail.org/pub/sendmail/ 에서 최신버젼을 구하기 바란다. Sendmail 8.2 이상에서는 domaintable Feature를 사용해 구현될 수 있지만, 여기서 제시하는 Sendmail 8.8 이상의 virtusertable Feature로 대체되는 추세이므로 여기에서는 다루지 않겠다.

6.5.1. m4를 사용한 sendmail.cf 생성

먼저 sendmail-VERSION/cf/cf/에서 시스템에 적절한 mc 파일을 복사한 후, Virtual Domain 설정에 필요한 virtusertable, genericstable Feature를 추가한다. 작성된 mc 파일은 일반적으로 다음과 같이 보인다.

* generic-bsd4.4.mc 파일을 사용해 작성한 freebsd.mc 파일
divert(0)dnl
VERSIONID(`@(#)generic-bsd4.4.mc        8.7 (Berkeley) 5/19/1998')
OSTYPE(bsd4.4)dnl
DOMAIN(generic)dnl
MAILER(local)dnl
MAILER(smtp)dnl
FEATURE(virtusertable, btree /etc/domainaliases.db)dnl
FEATURE(genericstable, btree /etc/useraliases.db)dnl

virtusertable Feature는 수신(in-bound)되는 편지를 로컬 사용자로 매핑하는 RuleSet을 생성하고, genericstable Feature는 반대로 송신(out-bound)되는 편지에 대해 로컬 유저에 대한 메일주소(메일 헤더상의 From: 필드)를 매핑하는 RuleSet을 생성한다. DNS의 Forward Zone과 Reverse Zone을 생각하면 되겠다.

btree 는 해당 테이블(domainaliases.db)이 B-Tree(Berkeley DB) 데이터베이스 형태로 저장됨을 의미한다. 가능한 다른 유형으로는 hash(Berkeley DB)와 dbm(NDBM)이 있는데, 테이블이 방대할 경우 효율은 btree(Berkeley DB) > hash(Berkeley DB) > dbm(NDBM) 순이다. 속도가 빠른 btree와 hash를 사용하기 위해서는 Berkeley DB(FreeBSD를 비롯해 몇몇 Unix에는 기본으로 포함된다)가 설치되어 있어야 하며, sendmailmakemap 컴파일시 -DNEWDB 플래그(Berkeley DB가 설치되어 있다면 자동으로 -DNEWDB 플래그가 사용된다)를 사용해야 한다.

Berkeley DB가 설치되어 있지 않을 경우엔 -DNDBM 플래그(NDBM 라이브러리는 대부분의 Unix에 기본으로 포함된다)로 컴파일 되는데 이때에는 DBM을 사용하여야 한다. 리눅스와 같이 Berkeley DB의 GNU판인 GDBM이 사용되는 경우엔 별도의 노력없이 세 종류의 DB를 모두 이용할 수 있다.

작성된 freebsd.mc 파일은 다음과 같이 m4(Macro Language Processor)를 통해 sendmail.cf로 변환한다.

# cd sendmail-VERSION/cf/cf
# m4 ../m4/cf.m4 freebsd.mc > freebsd.cf
# cp freebsd.cf /etc/sendmail.cf

만약 기존의 sendmail.cf가 유지되어야 하는 상황이라면, 실제 본 두 Feature가 생성하는 다음의 RuleSet을 참고하여 sendmail.cf를 직접 수정하여도 좋다. (RuleSet이 삽입되는 위치를 파악하기 위해 m4로 sendmail.cf를 생성하여 비교하기 바란다)

* FEATURE(virtusertable, btree /etc/domainaliases.db)dnl
# Virtual user table (maps incoming users)
Kvirtuser btree /etc/domainaliases.db

# handle virtual users (RuleSet 0의 Parse1 아래에 위치)
R$+ < @ $=w . >         $: < $(virtuser $1 @ $2 $@ $1 $: @ $) > $1 < @ $2 . >
R<@> $+ + $* < @ $* . >
                        $: < $(virtuser $1 + * @ $3 $@ $1 $: @ $) > $1 + $2 < @ $3 . >
R<@> $+ + $* < @ $* . >
                        $: < $(virtuser $1 @ $3 $@ $1 $: @ $) > $1 + $2 < @ $3 . >
R<@> $+ < @ $+ . >      $: < $(virtuser @ $2 $@ $1 $: @ $) > $1 < @ $2 . >
R<@> $+                 $: $1
R< error : $- $+ > $*   $#error $@ $(dequote $1 $) $: $2
R< $+ > $+ < @ $+ >     $: $>97 $1
* FEATURE(genericstable, btree /etc/useraliases.db)dnl
# Generics table (mapping outgoing addresses)
Kgenerics btree /etc/useraliases.db

# handle generics database (RuleSet 93 아래에 위치)
R$+ < @ $=G . > $: < $1@$2 > $1 < @ $2 . > @    mark
R$+ < @ *LOCAL* >       $: < $1@$j > $1 < @ *LOCAL* > @ mark
R< $+ > $+ < $* > @     $: < $(generics $1 $: $) > $2 < $3 >
R< > $+ < @ $+ >        $: < $(generics $1 $: $) > $1 < @ $2 >
R< $* @ $* > $* < $* >  $@ $>3 $1 @ $2                  found qualified
R< $+ > $* < $* >       $: $>3 $1 @ *LOCAL*             found unqualified
R< > $*                 $: $1                           not found

6.5.2. Forward-map 테이블 작성

먼저, 해당 호스트가 수신하는 도메인을 클래스 w(sendmail.cw)에 나열하여야 한다.

* /etc/sendmail.cw 파일
nobreak.com
kr.freebsd.org

domainaliases 파일을 편집기로 열어 다음과 같이 Virtual User Table을 작성한다.

* /etc/domainaliases 파일
webmaster@nobreak.com           dipper
moonhunt@nobreak.com            moonhunt@ieee.org
cjh@nobreak.com                 error:nouser Unknown User
@nobreak.com                    nobreak
@kr.freebsd.org                 %1@xfree86.org

메일주소 webmaster@nobreak.com 는 로컬 사용자 dipper로 매핑되고, moonhunt@nobreak.com 로 배달되는 편지는 moonhunt@ieee.org 로 포워딩 된다. cjh@nobreak.com 으로 배달되는 편지에 대해서는 에러메시지 'Unknown User'로 답하며, 그외 nobreak.com 으로 수신되는 모든 편지는 로컬 사용자 nobreak로 전달된다. 또한, kr.freebsd.org 로 수신되는 모든 편지는 xfree86.org 의 같은 사용자에게 포워딩된다. 다음과 같이 aliases 파일과 연동하여 다수의 사용자를 매핑하는것 또한 가능하다.

* /etc/domainaliases 파일
announce@kr.freebsd.org         announce.ml
admin@kr.freebsd.org            admin.ml
* /etc/aliases 파일
announce.ml:            :include:/var/ml/announce.ml
admin.ml:               nobreak, moonhunt@ieee.org

6.5.3. Reverse-map 테이블 작성

호스트명이 kfug2.kr.freebsd.org 일때, 시스템에서 out-bound되는 편지는 기본적으로 user@kfug2.kr.freebsd.org 를 From으로 갖는다. 다음의 Reverse-map 테이블은 로컬 사용자의 메일주소를 변경한다.

* /etc/useraliases 파일
nobreak                 nobreak@nobreak.com
cjh                     cjh@kr.freebsd.org

6.5.4. 테이블 변환 및 실험

작성된 테이블은 makemap을 사용해 dbm/btree/hash 형태로 변환되어야 한다. 여기서는 btree를 사용하였으므로 다음과 같이한다.

# makemap btree /etc/domainaliases.db < /etc/domainaliases
# makemap btree /etc/useraliases.db < /etc/useraliases
# ls /etc/domainaliases* /etc/useraliases*
/etc/domainaliases      /etc/useraliases
/etc/domainaliases.db   /etc/useraliases.db

이제 Sendmail을 행업하고 동작을 확인하자.

# kill -HUP `cat /var/run/sendmail.pid`

# sendmail -bv webmaster@nobreak.com
webmaster@nobreak.com... deliverable: mailer local, user dipper

# sendmail -bv cjh@nobreak.com
cjh@nobreak.com... Unknown User

# sendmail -bv anybody@kr.freebsd.org
anybody@kr.freebsd.org... deliverable: mailer esmtp,
host xfree86.org., user anybody@xfree86.org