다음 이전 차례

3. Virtuald

3.1 소개

모든 네트워크 연결은 2개의 IP 어드레스/포트 쌍으로 이루어진다. 네트워크 프로그래밍을 위한 API(Applications Program Interface)는 소켓 API로 불린다. 소켓은 마치 열려있는 파일과 같이 작동하기 때문에 네트워크 연결을 통해 데이터를 주고받는 것은 소켓에 쓰고읽는 과정으로 이해된다. 로컬 소켓의 주소를 돌려주는 함수는 getsockname이다. Virtuald는 getsockname을 사용하여 로컬 시스템의 어떤 IP가 접근 가능한지를 판단한다. Virtuald는 설정 파일을 읽어서 해당 IP에 할당된 디렉토리를 돌려받는다. 그리고는 chroot를 통해서 모든 서비스에 대한 제어권을 넘겨준다. Chroot은 루트 디렉토리를 재설정하여 새롭게 지정받은 포인트를 루트로 설정하기 때문에 상위 디렉토리는 실행 프로그램들에 의해 무시된다. 네트워크 프로그램에 대해서는 이 과정은 마치 아무일도 일어나지 않은 것처럼 투명하게 보일 것이다. Virtuald는 inetd와 같은 프로그램과 연결되어 어떤 서비스라도 가상적으로 만들 수 있다.

3.2 Inetd

Inetd는 외부에서 네트워크를 통해 연결을 시도할 때(예를 들면 POP서버의 요청) 여러 개의 포트를 감시하고 있다가 요청에 대한 적당한 연결을 구현해주는 네트워크 super server이다. Inetd는 네트워크를 전반적으로 관리하면서 특정 프로그램에 대해 네트워크를 연결해 주는 일을 한다. 이런 과정을 통해 어떤 서비스가 네트워크 연결이 필요 없을 때 불필요하게 실행되는 것을 막을 수 있다.

표준적인 /etc/inetd.conf 파일은 다음과 같다:

ftp stream tcp nowait root /usr/sbin/tcpd \
        wu.ftpd -l -a
pop-3 stream tcp nowait root /usr/sbin/tcpd \
        in.qpop -s

가상 서비스를 할 때 /etc/inetd.conf 파일은 다음과 같이 된다:

ftp stream tcp nowait root /usr/local/bin/virtuald \
        virtuald /virtual/conf.ftp wu.ftpd -l -a
pop-3 stream tcp nowait root /usr/local/bin/virtuald \
        virtuald /virtual/conf.pop in.qpop -s

3.3 설정 파일

각각의 서비스들은 어떤 IP들과 디렉토리들을 사용하고 관리할 것인지에 대한 정보를 설정 파일에서 얻게 된다. 당신은 하나의 전체 설정 파일 (master config file)을 갖던지 아니면 몇몇의 설정 파일들을 통해서 각각의 서비스에서 도메인의 다른 리스트를 얻게 할 수 있다. 설정 파일은 다음과 같다:

# This is a comment and so are blank lines

# Format IP SPACE dir NOSPACES
10.10.10.129 /virtual/domain1.com
10.10.10.130 /virtual/domain2.com
10.10.10.157 /virtual/domain3.com

# Default option for all other IPs
default /

3.4 소스코드

아래는 virtuald 프로그램에 대한 C 소스 코드이다. 이것을 컴파일하고 /usr/local/bin 아래에 0755의 퍼미션으로 설치하라.(소유자와 그룹은 모두 root로 한다.) 컴파일시에는 VERBOSELOG가 옵션으로 사용될 수 있는데 (오직 이 옵션만이 존재한다), 이것은 연결시 기록을 남길것인지를 정한다.

#include <netinet/in.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <stdarg.h>
#include <unistd.h>
#include <string.h>
#include <syslog.h>
#include <stdio.h>

#undef VERBOSELOG

#define BUFSIZE 8192

int getipaddr(char **ipaddr)
{
        struct sockaddr_in virtual_addr;
        static char ipaddrbuf[BUFSIZE];
        int virtual_len;
        char *ipptr;

        virtual_len=sizeof(virtual_addr);
        if (getsockname(0,(struct sockaddr *)&virtual_addr,&virtual_len)<0)
        {
                syslog(LOG_ERR,"getipaddr: getsockname failed: %m");
                return -1;
        }
        if (!(ipptr=inet_ntoa(virtual_addr.sin_addr)))
        {
                syslog(LOG_ERR,"getipaddr: inet_ntoa failed: %m");
                return -1;
        }
        strncpy(ipaddrbuf,ipptr,sizeof(ipaddrbuf)-1);
        *ipaddr=ipaddrbuf;
        return 0;
}

int iptodir(char **dir,char *ipaddr,char *filename)
{
        char buffer[BUFSIZE],*bufptr;
        static char dirbuf[BUFSIZE];
        FILE *fp;

        if (!(fp=fopen(filename,"r")))
        {
                syslog(LOG_ERR,"iptodir: fopen failed: %m");
                return -1;
        }
        *dir=NULL;
        while(fgets(buffer,BUFSIZE,fp))
        {
                buffer[strlen(buffer)-1]=0;
                if (*buffer=='#' || *buffer==0)
                        continue;
                if (!(bufptr=strchr(buffer,' ')))
                {
                        syslog(LOG_ERR,"iptodir: strchr failed");
                        return -1;
                }
                *bufptr++=0;
                if (!strcmp(buffer,ipaddr))
                {
                        strncpy(dirbuf,bufptr,sizeof(dirbuf)-1);
                        *dir=dirbuf;
                        break;
                }
                if (!strcmp(buffer,"default"))
                {
                        strncpy(dirbuf,bufptr,sizeof(dirbuf)-1);
                        *dir=dirbuf;
                        break;
                }
        }
        if (fclose(fp)==EOF)
        {
                syslog(LOG_ERR,"iptodir: fclose failed: %m");
                return -1;
        }
        if (!*dir)
        {
                syslog(LOG_ERR,"iptodir: ip not found in conf file");
                return -1;
        }
        return 0;
}

int main(int argc,char **argv)
{
        char *ipaddr,*dir;

        openlog("virtuald",LOG_PID,LOG_DAEMON);

#ifdef VERBOSELOG
        syslog(LOG_ERR,"Virtuald Starting: $Revision: 1.1.1.1 $");
#endif
        if (!argv[1])
        {
                syslog(LOG_ERR,"invalid arguments: no conf file");
                exit(0);
        }
        if (!argv[2])
        {
                syslog(LOG_ERR,"invalid arguments: no program to run");
                exit(0);
        }
        if (getipaddr(&ipaddr))
        {
                syslog(LOG_ERR,"getipaddr failed");
                exit(0);
        }
#ifdef VERBOSELOG
        syslog(LOG_ERR,"Incoming ip: %s",ipaddr);
#endif
        if (iptodir(&dir,ipaddr,argv[1]))
        {
                syslog(LOG_ERR,"iptodir failed");
                exit(0);
        }
        if (chroot(dir)<0)
        {
                syslog(LOG_ERR,"chroot failed: %m");
                exit(0);
        }
#ifdef VERBOSELOG
        syslog(LOG_ERR,"Chroot dir: %s",dir);
#endif
        if (chdir("/")<0)
        {
                syslog(LOG_ERR,"chdir failed: %m");
                exit(0);
        }
        if (execvp(argv[2],argv+2)<0)
        {
                syslog(LOG_ERR,"execvp failed: %m");
                exit(0);
        }

        closelog();

        exit(0);
}


다음 이전 차례