안녕하세요
뚱보 프로그래머입니다.
설명절이라 다들 좋은 연휴 보내고 계실텐데요
접근제어입니다.
접근 제어(tcpd)
1) tcp_wrapper
finger라는 프로그램은 특정 호스트에 존재하는 사용자에 대한 정보를 얻을 수 있는 프로그램이고, tftp라는 프로그램은 인증이라는 과정없이 바로 호스트에 접속해서 ASCII 파일을 다운로드할 수 있다. 이런 서비스들에 대해서는 완전히 서비스 자체를 차단하든지 아니면 접근이 허용된 호스트로부터의 접근만을 허용해야 한다. 그러나 단순히 inetd를 단순히 설치하고 설정하는 것만으로는 완전한 접근 제어가 불가능하다. 이를 위해서 제공되는 프로그램이 tcp_wrapper이다. 레드햇을 설치하면 기본적으로 설치되는 프로그램이 있는데, /usr/sbin/tcpd라는 프로그램으로, 이 프로그램이 일종의 tcp_wrapper이다. 만약 최신의 버전을 다운로드 받아 설치하고 싶다면, 공식 홈페이지인 http://cvsweb.pld.org.pl/tcpd로 접근을 해보면 될 것이다. 단순히 tcpd.c 파일과 tcpd_check.c 파일로 구성되어 있다.
tcpd는 호스트별로 접근을 제어할 수 있는 유용한 도구로, 모니터링을 원하거나 접근 제어를 원하는 서버 프로그램이 있는 경우에는 해당 서버를 바로 호출하는 것이 아니고, tcpd를 호출하도록 설계함으로써 소기의 목적을 달성할 수 있다. 뿐만 아니라 tcpd는 syslog 데몬을 호출하여 접근하고자 하는 호스트에 대해서 로그를 남길 수도 있다. 주의할 것은 UDP 기반의 서비스에 대해서는 아무런 효과를 기대할 수 없다.
2) tcpd의 사용 예
finger 서비스에 대해 접근을 제어할 경우 먼저 /etc/inetd.conf 파일을 열어 다음을 확인한다.
finger stream tcp nowait bin /usr/sbin/fingerd in.fingerd |
위 설정 부분을 다음과 같이 설정을 변경한다.
finger stream tcp nowait bin /usr/sbin/tcpd in.fingerd |
위와 같이 설정을 변경하게 되면 finger 데몬에 대해서 tcp_wrapper가 적용되었고, 그 결과 클라이언트의 요청은 syslog에 의해 로깅된다. 위와 같이 설정을 변경했다해서 설정이 적용되는 것은 아니다.
/etc/hosts.allow 파일과 /etc/hosts.deny 파일은 실제 접근에 대해서 제어를 하게 되는데, 어떤 호스트와 어떤 서비스에 대해서 접근을 허용할 것인지를 지정하게 된다. tcpd가 test.domain.com으로부터 finger 서비스에 대한 요청을 받게 되면 hosts.allow 파일과 hosts.deny 파일을 차례로 검사하여 해당 서비스와 클라이언트의 주소가 일치하는지를 확인한다. 만약 hosts.allow 파일 내에 클라이언트의 호스트 이름과 일치하는 항목이 존재한다면 접근은 허용되고, tcpd는 hosts.deny 파일을 검사하지 않게 된다. 만약 hosts.allow 파일에서 일치하는 정보를 찾지 못하고, hosts.deny 파일에서 클라이언트 이름이 동일한 것을 발견했다면 클라이언트의 요청은 서버 측에서 연결을 끊음으로써 연결이 거절된다. 두 파일 모두에서 일치하는 내용을 찾을 수 없다면 클라이언트의 요청은 허용된다.
/etc/hosts.[deny, allow] 파일의 형식은 다음과 같다.
serverlist:hostlist[:shell_command] |
serverlist 항목은 /etc/services 파일에 지정된 서비스의 이름이나 모든 서비스를 의미하는 ALL이라는 키워드가 올 수 있다. 만약 telnet과 ftp를 제외한 모든 서비스를 의미하고자 할 경우에는 ALL EXCEPT telnet, ftp라고 사용하면 된다.
hostlist 항목은 호스트 이름이나 IP 주소들, ALL, LOCAL, UNKNOWN, PARANOID 등이 올 수 있다. ALL은 지구상의 모든 호스트을 의미하는 것이고, LOCAL은 동일한 도메인 내에 포함된 호스트들을 의미하며, UNKNOWN은 호스트의 이름이나 주소에 대해서 DNS 해석이 실패한 호스트들을 의미한다. PARANOID는 이름해석(호스트 이름으로 IP 주소를 찾는 것)이 불가능한 호스트를 대표한다. 예를 들어 “.domain.com”처럼 점(.)으로 시작하는 도메인 이름이 hostlist에 사용되었다면 domain.com 도메인에 포함된 모든 호스트를 지칭하고, “222.222.”와 같이 점(.)으로 끝나는 IP 주소가 hostlist에 사용되었다면 해당 주소(222.222.xxx.yyy)를 가지는 모든 호스트를 의미한다. 뿐 만 아니라 “IP address/subnet mask” 형식으로 사용된 주소 체계도 사용될 수 있다. 또한 호스트 리스트를 포함하고 있는 파일을 지정할 수도 있는데, 호스트 리스트 파일을 지정하기 위해서는 “/”로 시작하는 파일의 절대 경로를 입력하면 된다. 예를 들어 로컬 네트워크(edu00.net 도메인)에 포함된 호스트 이외에서는 finger와 tftp 서비스에 접근하지 못하게 지정하려면 다음과 같은 내용을 /etc/hosts.deny 파일에 기록하고, /etc/hosts.allow 파일은 빈채로 두면 된다.
in.tftpd, in.fingerd: ALL EXCEPT LOCAL, .edu00.net |
마지막으로 shell_command 항목은 사용해도 되고, 하지 않아도 되는 항목으로, 해당 리스트가 매치될 때 실행할 쉘 명령을 지정하는 항목이다. 다음의 예는 호스트가 test.edu00.net이 아니라면 연결된 호스트와 사용자에 대해서 로그 파일을 만들도록 지정하는 내용이다.
in.tftpd, in.fingerd:ALL EXCEPT LOCAL, .edu00.net:\ echo “request from %d @%h: >> /var/log/finger.log;\ if [ %h != “test.edu00.net;” ]; then\ finger –l @%h >> /var/log/finger.log\ fi |
예에서 %h와 %d는 클라이언트의 호스트 이름과 서비스 이름을 각각 의미한다.
3) xinetd
커널 버전 2.2.17부터 추가된 xinetd(extended inetd)가 inetd가 이 역할을 대신한다.
1] xinetd 개요
xinetd란 외부로부터의 침범이나 Denial of Services(DoS) 공격의 위험을 줄일 수 있도록 보안을 강화시킨 인터넷 데몬이다. 즉, 이전 버전의 inetd와 tcp_wrapper를 합쳐놓은 것이라고 보면 된다. 물론 이 둘을 단순히 합친 것 이상의 일들을 감당하고 있다.
이전의 inetd는 컴퓨터에 연결되는 네트워크 연결을 제어하는데 상당한 도움을 준다. 예를 들어 inetd에 의해 제어되는 포트를 통해 요청이 들어왔을 경우 inetd는 tcpd(tcp_wrapper의 일종)라 불리는 프로그램에게 사용자의 요청을 전달하게 된다. tcpd는 /etc/hosts.allow와 /etc/hosts.deny 파일들에 정의된 규칙에 따라 사용자의 요청을 허락할 것인지 무시할 것인지를 결정한다. 적합한 요청이 들어온 경우에만 해당 서버 프로세스가 가동된다.
xinetd는 tcp_wrapper가 제공하는 접근 제어와 비슷한 기능을 제공하지만, 그 능력은 tcp_wrapper를 능가하며, 다음과 같은 기능을 제공한다.
(1) TCP, UDP, RPC 서비스에 대한 접근 제어 기능
(2) 시간을 기준으로 한 접근 제어 기능
(3) 성공하거나 실패하거나, 어떤 연결에 대해서도 로깅할 수 있는 기능
(4) Denial of Services(Dos:자원을 소모시킴으로써 시스템의 기능을 막는 공격) 공격을 막을 수 있는 기능
(5) 동시에 동일한 형태의 서버 실행 개수를 제한할 수 있는 기능
(6) 서버들의 전체 개수를 제한할 수 있는 기능
(7) 로그 파일의 크기를 제한할 수 있는 기능
(8) 특정 네트워크 카드에 특정 서비스를 바인딩할 수 있는 기능(인트라넷 전용 서비스에 대한 외부 접근 제한)
(9) 다른 시스템에 proxy로 동작할 수 있는 기능, 이는 내부 네트워크를 위해 IP 매스커레이딩이나 NAT와 함께 사용할 때 유용
2] 컴파일과 설치
xinetd의 공식 홈페이지는 http://www.xinetd.org이므로 소스를 다운로드 받아서 설치할 수 있다. 만약 컴파일에 자신이 없거나 C 라이브러리나 gcc 등이 설치되어 있지 않은 사람들은 http://rpmfind.net에 접속을 해서 해당 패키지를 다운로드 받아 설치한다.
(1) 환경 설정
#./configure –prefix=/usr/sbin
환경 설정을 위한 configure 명령은 –prefix 옵션이외에도 다음과 같은 옵션들이 있다.
with-libwrap
이 옵션은 tcpd의 설정 파일들(/etc/hosts.allow, /etc/hosts.deny)을 검사하고, 접근이 받아들여지면, 자체의 제어 루틴을 사용하도록 한다. 이 옵션이 동작하도록 하려면, tcp_wrapper 관련 라이브러리가 해당 시스템에 설치되어 있어야 한다. 하지만 wrapper가 하는 일을 xinetd가 수행하기 때문에 설정이 중복되어 서비스가 느려질 수 있기 때문에 사용하지 않는 것이 좋다.)
with-loadavg
이 옵션은 max_load 설정 옵션의 사용을 가능하도록 하는 것으로, 시스템에 부하가 많을 경우 특정 서비스를 잠시 중단시키도록 한다. DoS 공격을 막기 위해서는 필수적이다.
with-inet6
IPv6를 사용하기 원한다면 이 옵션을 사용한다. IPv4와 IPv6 연결에 대해서 관리가 가능하지만 IPv4가 점차 IPv6로 이동하고 있다.
위의 명령을 실행시키면 각종 환경을 자동적으로 검사해서 Makefile을 생성해준다.
(2) 컴파일
#make
make 명령은 생성된 Makefile을 이용해서 컴파일을 하게 된다.
(3) 바이너리 이동
#make install
아무런 오류없이 컴파일이 성공했다면 make install 명령을 이용해서 컴파일된 바이너리들을 앞에서 지정한 디렉토리(/usr/sbin)으로 복사를 한다.
3] xinetd 설정(/etc/xinetd.conf)
xinetd를 시작하기 전에 inetd를 중지시킬 필요는 없다. 만약 inetd를 중지시키게 되면 두 데몬이 예상치 못했던 행동을 할 수도 있다. xinetd의 동작을 변경하기 위해서는 다음과 같은 시그널이 필요하다.
(1) 동작 변경 시그널
[1] SIGUSR1
소프트웨어 재설정, 설정 파일이 다시 읽혀지며 또한 서비스 파라미터가 변경된다.
[2] SIGUSR2
하드웨어 재설정, SIGUSR1의 동작에다 날짜가 지난 데몬을 종료시키는 기능을 한다.
[3] SIGTERM
xinetd와 xinetd가 생성한 데몬을 종료한다.
위의 세 개의 시그널은 start, stop, restart, soft(SIGUSR1), hard(SIGUSR2) 옵션을 가진 조그만 스크립트로 쉽게 관리가 가능하다.
(2) 기본 영역 설정
설정 파일은 기본 영역부터 시작되는데, 이 영역의 속성은 xinetd가 관장하는 모든 서비스에 의해 사용된다. 기본 영역 이후에는 서비스만큼 많은 영역을 찾아볼 수 있을 것이며, 이들 영역에 정의된 옵션들은 기본 영역과의 관계를 생각해서 다시 정의할 수 있다.
기본 영역은 다음과 같은 형식을 취한다.
Defaults { attribute operator value(s) ……. } |
기본 영역에 정의된 각각의 속성값은 이후에 정의된 모든 서비스에 대해서 영향을 미치게 된다. 그래서 only_from 속성의 경우 리눅스 시스템에 연결할 수 있는 인증된 주소를 나열할 수도 있다.
only_from = 192.168.1.0/24 192.168.5.0/24 192.168.10.17 |
그러면 접근이 허용된 서비스에 대해서 위의 주소를 가진 호스트로부터의 접근은 완전히 허용된다. 하지만 위의 설정 값은 기본 값으로서 서비스별로 변경이 가능하다. 그럼에도 불구하고, 이 프로세스는 위험 요소를 안고 있다. 왜냐하면 단순하면서 잘 적용된 보안을 유지하기 위해서는 기본 값을 정의하지 않고, 서비스 내에서 그 값을 변경하는 것이 옳기 때문이다. 예를 들어 접근 권한에 있어서 가장 간단한 보안 정책은 모든 사람의 접근을 차단하고 난 후 정말로 필요로 하는 사람에게 접근을 허락하는 것이다. 즉, tcp_wrapper에서 hosts.deny 파일에 ALL:ALL@ALL을 설정하고, hosts.allow 파일에 승인된 서비스와 주소를 지정함으로써 구현된다.
(3) 서비스 영역
설정 파일에는 각 영역이 서비스에 대해 서술하고 있다.
Service Service_Name { attribute operator value(s) …… } |
operator로 가능한 것은 “=”, “+=”, “-=”이며, 대부분의 속성(attribute)은 “=” 연산자(operator)만을 지원하고 있다. “=” 연산자는 속성에 고정된 값(value)을 부여하며, “+=” 연산자는 속성에 특정 값(value)을 추가하는 연산자이며, “-=” 연산자는 속성에서 특정 값을 제거하는 기능을 한다.
(4) 속성들
속성 |
값과 설명 |
flags |
-IDONLY : identd 서버를 가진 클라이언트로부터의 접속만 허용 -NORETRY : 실패할 경우 새로운 프로세스의 생성을 막음 -NAMEINARGS : server_args 속성의 첫번째 인자를 지칭하는 것으로 tcpd의 사용을 허용한다. |
log_types |
xinetd는 기본적으로 syslogd와 daemon.info 선택자를 기본적으로 사용하게 된다. -SYSLOG 선택자[레벨] : 이 항목은 syslogd에서 사용되는 daemon, auth, user, local0-7 중에서 하나를 선택하도록 한다. -FILE[max_size [absolute_max_size]] : 파일의 크기를 지정하는 것으로, 첫번째 크기 제한에 도달하면 syslogd에 메시지를 보내고, 두번째 크기 제한에 도달하면 logging을 중단한다. |
log_on_success |
서버가 시작할 때 다른 정보를 저장할 수 있다. PID : 서버 프로세스의 PID(xinetd의 내부 서비스의 경우에는 0이 기록됨) -HOST : 클라이언트의 주소가 기록됨 -USERID : 원격 사용자의 정보가 저장되는데, identd에 의한 정보를 기록 -EXIT : 프로세스가 종료된 상태 -DURATION : 세션이 연결된 시간 |
log_on_failure |
자원의 부족이나 접근 룰에 위배된 경우 등으로 인해 서버가 시작되지 못한 경우에 저장되는 정보들을 지정 -HOST, USERID : log_on_success와 동일 -ATTEMPT : 접근 시도에 대해서 기록 -RECORD : 클라이언트에 대해 알 수 있는 모든 정보를 기록 |
nice |
nice 명령과 같이 서버의 스케쥴링 우선순위를 변경할 수 있다. |
no_access |
해당 서비스에 접속할 수 없는 클라이언트 목록 |
only_from |
허용된 클라이언트 목록을 지정하는 것으로, 이 속성에 아무런 값이 없다면 어느 누구도 서비스에 접속할 수 없다. |
port |
서비스와 연관된 포트 번호를 지정하는 것으로, 만약 /etc/services 파일에도 해당 서비스에 대한 포트가 지정되어 있다면 이 둘은 반드시 동일해야 한다. |
port |
/etc/protocols 파일에 지정된 프로토콜을 설정하는 것으로, 만약 특정 프로토콜 이름이 주어지지 않는다면 default로 설정된 것이 사용된다. |
server |
서버 프로그램의 경로를 지정 |
server_args |
서버 프로그램에 주어질 옵션들을 지정 |
socket_type |
stream(TCP), dgram(UDP), raw(IP), seqpacket 중 하나 |
type |
xinetd는 3가지 형태의 서비스를 다룰 수 있다. 1. RPC : /etc/rpc 파일에 정의된 것들로, 잘 동작하지는 않는다. 2. INTERNAL : xinetd에 의해 제어되는 서비스(echo, time, daytime, chargen, discard 등) 3. UNLISTED : /etc/rpc나 /etc/services 파일에 정의되지 않은 서비스 |
wait |
서비스가 쓰레드(thread)에 대해서 어떻게 동작할지를 결정하는 것으로 두개의 값이 올 수 있다. -yes : 서비스가 하나의 쓰레드만 사용하도록 지정. 즉, 다른 사용자가 서비스를 요구할 경우에 기다려야 한다. -no : 지속적으로 접속해 오는 사용자에 대해 계속 서비스 서버를 실행한다. |
cps |
접속자 수를 제한한다. 첫번째 인자는 제한할 접속자 수를 의미하고, 두번째 인자는 서비스를 활성화시킬 시간(초단위)이다. 만약 제한된 숫자 이상으로 접속해 올 경우 일정한 시간동안 서비스 비활성화 |
instances |
동시에 동일한 서버가 실행될 수 있는 최대 수를 지정 |
max_load |
서버에 대한 최대 부하를 지저(2, 2.5와 같이). 제한을 넘어선 클라이언트의 요구는 무시된다. |
per_source |
정수값 혹은 UNLIMITED가 올 수 있으며, 동일한 호스트로부터의 접속 수를 제한 |
4] xinetd를 사용한 예
(1) 접근 제어
IP 주소를 이용해서 리눅스 시스템에 접근하는 것을 차단할 수 있다. 하지만 xinetd를 사용할 경우 IP 주소를 이용한 차단 이상의 기능들을 가져올 수 있다.
첫째는 호스트 이름(hostname) 해석을 이용해서 접근을 제어할 수 있다. 즉, xinetd를 이용하면 지정된 혹은 모든 연결에 대해서 호스트 이름을 검사해서 접근을 제어할 수 있다.
둘째는 domain.com 형식으로 도메인 이름 단위별로 접근을 제한할 수 있다. 클라이언트가 접근을 시도할 때 xinetd는 클라이언트의 주소 역변환(IP 주소를 이용해 호스트 이름을 해석)을 이용해서 특정 도메인에 소속된 것인지를 검사하게 된다.
하지만 위의 두 경우보다는 IP 주소를 이용하는 것이 더 나은 성능을 보장한다. 이유는 모든 접속에 대해서 DNS 검색을 하지 않아도 되기 때문이다. 하지만 호스트 이름을 기반으로 해서 접근을 제어하고자 할 경우에는 로컬 호스트에 네임 서버의 캐쉬를 설정해두면 빠르게 검색할 수 있다.
(2) defaults 설정
/etc/xinetd.conf 파일의 defaults 영역에는 여러 가지 속성들에 대해서 설정할 수 있다. 기본적으로 모든 호스트로부터의 접근을 차단하는 것이 신뢰할 수 있는 보안을 위한 첫걸음이다. 다음으로 서비스별로 접근을 허용할 호스트를 나열하는 것이 좋다. IP 주소를 기반으로 하는 only_from과 no_access을 이용하면 다음과 같이 설정할 수 있다.
no_access = 0.0.0.0/0 |
먼저 모든 호스트로부터의 접근을 차단하는 것이다. 위와 같이 설정하게 되면 모든 호스트로부터의 접근이 차단되기 때문에 ping 명령도 이용할 수 없게 된다. 만약 ping 명령이 사용가능하도록 설정을 하려면 /etc/xinetd.d/echo 서비스에 대해서는 다음과 같이 설정한다.
only_from = 0.0.0.0/0 |
결론적으로 접근 제어는 위의 두 속성을 비교함으로써 이루어진다. 클라이언트의 주소가 두 속성에서 지정한 것과 일치한다면 권한이 적은 쪽이 적용된다(보안측면에서 유리). 만약 위와 같이 동일한 권한을 가지는 경우에는 xinetd는 어느 것을 선택해야 할지 모르기 때문에 무작정 클라이언트의 접근을 거부하게 된다. 위와 같은 모호함을 없애기 위해 다음과 같이 명확하게 작성하는 것이 좋다.
only_from = 192.0.0.0/8 |
특별히 모든 호스트로부터의 접근을 차단하고자 한다면 only_from 속성에 아무런 값도 입력하지 않으면 된다(다른 서비스 항목들에 대해서도 동일하다). 그러므로 접근 차단 룰이 없다고 하여 해당 서비스에 대한 모든 접근이 허용된다고 생각해서는 안된다.
다음의 하나의 예시이다.
defaults { instances = 15 log_type = FILE /var/log/servicelog log_on_success = HOST PID USERID DURATION EXIT log_on_failure = HOST USERID RECORD only_from = per_source = 5
disabled = shell login exec comsat disabled = telnet ftp disabled = name uucp tftp disabled = finger systat netstat
#INTERNAL disabled = time daytime chargen servers services xadmin
#RPC disabled = rstatd rquotad rusersd sprayd walld } |
(3) 서비스에 대한 설정
서비스를 설정하기 위해 특별한 것이 필요한 것은 아니다. 즉, 기본값만으로 사용이 얼마든지 가능하기 때문이다. 하지만 좀 더 세밀하게 각각의 서비스에 대해서 조절을 하고 싶다면 각각의 서비스별로 특정한 속성과 값을 사용하는 것이 좋을 것이다. 서비스의 유형(INTETNAL, UNLISTED, RPC 등)에 따라 반드시 지정해야 하는 속성들이 있는데, 이는 다음과 같다.
속성 |
설명 |
socket_type |
모든 서비스에 사용되어야 한다. |
user |
INTERNAL 서비스가 아닌 서비스에 대해서는 사용해야 한다. |
server |
INTERNAL 서비스가 아닌 서비스에 대해서는 사용해야 한다. |
wait |
모든 서비스에 대해서 지정되어야 한다. |
protocol |
모든 RPC 서비스와 /etc/services에 정의되지 않은 포트를 사용하는 서비스에 대해서 사용해야 한다. |
rpc_version |
모든 RPC 서비스에 대해서 사용되어야 한다. |
rpc_number |
/etc/rpc 파일에 정의되지 않은 모든 RPC 서비스에 대해서 사용되어야 한다. |
port |
RPC 서비스가 아닌 모든 서비스에 대해서, /etc/services 파일에 정의되지 않은 모든 포트에 대해서 사용해야 한다. |
(4) 서비스 설정 예
service ntalk { socket_type = dgram wait = yes user = nobody server = /usr/sbin/in.ntalkd only_from = 192.168.1.0/24 }
service ftp { socket_type = stream wait = no user = root server = /usr/sbin/in.ftpd server_args = -1 instances = 4 access_times = 07:00-12:30 13:00-21:00 nice = 10 only_from = 192.168.1.0/24 } |
위의 두 서비스(ntalkd, ftp)는 로컬 네트워크(192.168.1.0/24)에 대해서만 접속을 허용하고 있다. FTP 서비스에 대해서는 추가적으로 몇 개의 조건이 더 있는데, 특정 시간대에 대해서만 접속을 허용하고, 동시에 4개의 FTP 데몬을 가동시킬 수 있기 때문에 동시에 최대 4명만 접속을 할 수 있다.
하나의 시스템에 둘 이상의 네트워크 카드가 있을 경우에는 bind라는 속성을 이용하는데, 이 속성을 이용하면 특정 IP 주소에 원하는 서비스를 연동시킬 수 있다. 예를 들어 특정 FTP 서버를 설치할 때 내부 네트워크용 서버와 외부 네트워크용 서버를 구축할 경우 bind라는 속성을 이용하여 내부 사용자용과 외부 사용자용으로 나누어 서비스할 수 있다. 하지만 이 둘을 구분하기 위해 xinetd는 id라는 속성을 함께 사용해야 한다. id라는 속성이 사용되지 않는다면 동일한 두 서비스를 구분할 길이 없게 된다.
service ftp { id = ftp-public wait = no user = root server = /usr/sbin/in.ftpd server_args = -1 instances = 4 nice = 10 only_from = 0.0.0.0/0 #모든 클라이언트로부터의 접속을 허용 bind = 211.170.43.70 # 공인 IP 주소 } service ftp { id = ftp-internal socket_type = stream wait = no user = root server = /usr/sbin/in.ftpd server_args = -1 only_from = 192.168.1.0/24 # 단지 내부적으로 사용하기 위한 IP 주소 bind = 192.168.1.1 # 로컬 IP 주소 } |
위와 같이 bind 속성을 사용하게 되면 접속하는 사용자의 IP 주소를 확인해서 적절한 데몬을 호출하게 된다. 그러므로 내부 네트워크에 접속하기 위해서는 반드시 내부 IP 주소를 이용해서 접근을 해야 한다.
(5) 다른 시스템으로 재지향
xinetd는 redirect라는 속성을 이용해서 프록시(proxy)로서의 역할을 수행할 수도 있다. 즉, 특정 서비스를 요구하는 요청을 다른 장비의 특정 포트로 옮기게 할 수 있다.
service telnet { flags = REUSE socket_type = stream wait = no user = root server = /usr/sbin/in.telnetd only_from = 192.168.1.0/24 redirect = 192.168.1.15 23 } |
위의 설정은 처음 사용자가 접속을 할 때 해당 리눅스 시스템이 접속을 받겠지만 바로 192.168.1.15로 클라이언트를 넘겨주게 된다. 편리한 기능이지만 보안의 관점에서는 위험한 설정이다.
4) services 파일과 protocols 파일
1] /etc/services
표준 인터넷 서비스에 대해서 포트를 지정하는 파일로, 서버나 클라이언트 프로그램이 서비스 이름을 해당 포트 번호로 변경하기 위해서는 각각의 호스트에 참조할 수 있는 파일이 있어야 한다. 이 파일을 /etc/services 파일이라고 부르며, 이 파일의 형식은 다음과 같다.
service port/protocol [aliases] |
service는 해당 서비스의 이름을 의미하며, port는 서비스가 사용할 포트 번호를 지정한다. protocol은 해당 서비스가 사용할 프로토콜을 지정하는데, 일반적으로 tcp나 udp를 설정한다. 사용하는 프로토콜이 다르다면 동일한 포트 번호를 이용해서 다른 서비스를 할 수 있으며, 동일한 포트와 동일한 서비스를 둘 이상의 다른 프로토콜을 이용해서 서비스할 수도 있다. 마지막으로 aliases 항목은 서비스의 다른 이름을 지정하는데 사용된다.
일반적으로 시스템을 설치할 때 자동적으로 설치되는 /etc/services 파일은 변경할 필요가 거의 없다. 네트워크 응용 프로그램을 특별히 제작한 경우라면 별도의 포트 번호와 서비스 이름을 부여하여 사용할 필요가 있겠지만 그 외는 거의 수정을 하지 않는다.
2] /etc/protocols
/etc/services 파일과 마찬가지로 네트워크 응용 프로그램은 프로토콜 이름에 대한 해석을 필요로 한다. 즉, 컴퓨터는 숫자 이외에는 알아들을 수 있는 것이 아무것도 없다. 그러므로 사람이 일일이 해당 프로그램을 숫자로 변환해주어야 한다. 하지만 이 과정이 장시간에 걸쳐 반복되는 것이기 때문에 이러한 정보를 별도의 파일로 제작해서 보관하고 있다. 이 파일이 바로 /etc/protocols 파일이다.
5) RPC(Remote Procedure Call)
클라이언트/서버 형식의 응용 프로그램에서 사용하는 것은 대부분 RPC이다. 즉, 원격의 함수 혹은 procedure를 호출해서 특정 작업을 진행하는 것이다. RPC는 Sun에서 개발되었으며, 다양한 툴과 라이브러리를 제공하고 있다. RPC를 이용한 대표적 응용 프로그램으로는 NIS와 NFS가 있다.
RPC 서버는 클라이언트가 RPC 호출을 각종 파라미터들과 함께 서버에 전송함으로써 실행되는 각종 procedure들의 집합이라고 할 수 있다. 서버는 클라이언트를 위해서 지정된 프로시져를 호출하고, 결과값이 있다면 그 값을 클라이언트에게 되돌려준다. 네트워크에 연결될 수 있는 컴퓨터는 수없이 많다. 그렇게 많은 컴퓨터들 사이에 모든 데이터 전환 작업을 하기에는 너무 어려운 작업이기 때문에 클라이언트와 서버 사이에 전송되는 데이터들은 기계에 무관한 형태인 XDR(External Data Representation) 포맷으로 변경되어 전송된다. 그리고 그 데이터를 받는 쪽에서 로컬 시스템에 적합한 형식의 데이터로 벼환한다. RPC가 XDR 포맷의 데이터를 원격 호스트로 정확하게 전송하기 위해서는 표준 UDP와 TCP를 사용해야 하는데, 어떤 경우에는 RPC 응용 프로그램의 성능을 향상시키기 위해 다른 것과 호환이 되지 않도록 변경하는 경우도 있다. 이렇게 약간이라도 변경을 하게 되면 순수한 RPC 결과를 원하는 클라이언트와는 완전한 단절이 일어나게 된다. 그러므로 RPC 프로그램은 자신이 사용하는 RPC 버전을 명시해야 한다. RPC 버전은 보통 1로 시작하고, 새로운 버전을 의미하는 번호가 뒤따르게 된다. 종종 하나의 서버가 여러 개의 버전을 동시에 제공하기도 하는데, 이런 경우 클라이언트는 자신이 원하는 서비스에 대해 버전을 명시하여 RPC 호출을 하게 된다.
RPC 서버와 클라이언트 간에 통신을 할 때 RPC 서버는 “프로그램”이라고 불리는 하나 이상의 프로시져 집합을 제공하고, 이 집합을 “프로그램 번호”라는 것에 의해 구분한다. 각각의 프로그램 번호는 /etc/rpc라는 파일에 저장되어 있다.
다음은 /etc/rpc 파일의 일부분이다.
portmapper 100000 portmap sunrpc rpcbind rstatd 100001 rstat rup perfmeter rstat_svc rusersd 100002 rusers nfs 100003 nfsprog ypserv 100004 ypprog mountd 100005 mount showmount ypbind 100007 walld 100008 rwall shutdown yppasswdd 100009 yppasswd etherstatd 100010 etherstat rquotad 100011 rquotaprog quota rquota sprayd 100012 spray 3270_mapper 100013 rje_mapper 100014 |
TCP/IP 네트워크에서 RPC를 프로그램하는 사람들은 프로그램 번호와 일반적인 네트워크 서비스간에 연관을 맺는네 어려움을 겪는데, 이를 해결하기 위해서 각 서버는 각각의 프로그램 버전에 대해 TCP와 UDP 포트를 모두 지원하기로 했다. 일반적으로 RPC 응용 프로그램은 데이터를 전송할 때 UDP를 사용하며, 데이터가 하나의 UDP 데이터그램에 포함되지 않을 때 TCP를 이용해서 데이터를 전송하게 된다.
물론 클라이언트 프로그램도 서버와 통신을 하기 위해서는 어떤 프로그램 번호를 이용해야 할지를 알아야 한다. 클라이언트가 프로그램 번호를 알게 하기 위해서 별도의 설정 파일을 사용하는 것은 효율적이지 못한데, RPC 응용 프로그램은 예약된 포트를 사용하지 않기 때문이다. 그러므로 RPC 응용 프로그램은 portmapper 데몬이라고 불리는 특별한 프로그램을 이용해서 임의의 포트 번호를 사용하게 된다. portmapper는 RPC 서버에서 실행이 되며, 서비스 중계자로서 역할을 하게 된다. 그러므로 서버와 접속하여 서비스를 원하는 클라이언트는 먼저 해당 서버의 portmapper에게 요청을 하게 되고, portmapper는 서비스를 제공할 수 있는 TCP와 UDP 포트 번호를 부여하게 된다.
만약 portmapper가 다운된다면 모든 RPC 포트에 대한 정보가 사라지기 때문에 모든 RPC 서버를 수동으로 실행하거나 시스템 자체를 리부팅해야 한다. 그러므로 시스템이 시작할 때 반드시 portmapper가 실행되도록 부트 스크립트를 생성해야 한다. 리눅스에서는 portmapper가 /sbin/portmap 프로그램에 의해서 가동되거나 /usr/sbin/rpc.portmap에 의해서 가동된다.