| |||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
D.M.Z
CONTENT
PRE
NEXT
9.1 The inetd Super-Server IP와 resolver를 성공적으로 셋업하고 나면, 이제 네트웍을 통해 제공하고자 하는 서비스 쪽으로 눈을 돌리자. 이 장에선 몇가지 단순한 네트웍 어플리케이션, 가령 inetd 서버라든가 rlogin 족(族) 프로그램 같은 것들의 설정에 대해 다룬다. Network File System (NFS)와 Network Information System(NIS) 같은 서비스의 기반이 되는 Remote Procedure Call 인터페이스로 간략하게나마 다룰 것이다. 그러나, NFS와 NIS의 설정은 좀 더 얘기 할 것이 많으므로, 별도의 장에서 설명한다. Electronic mail과 netnews의 경우도 마찬가지이다. 물론, 이 문서에서 모든 네트웍 어플리케이션을 설명할 수는 없는 일이다. 만약 이 책에서 설명하기 않는 talk, gopher, Xmosaic을 설치하고자 한다면 그것들의 매뉴얼 페이지를 참고하길 바란다.
흔히, 서비스는 데몬(daemon)이라는 것을 통해 행해진다. 데몬이란 특정 포트를 열어, 커넬션이 들어오길 기다리는 프로그램이다. 만약, 커넥션이 들어온다면, 자식 프로세스에게 커넥션을 넘겨주고, 자신은 계속 또다른 요청을 listen한다. 이 개념대로다면, 모든 제공되는 서비스마다 하나씩의 데몬을 돌려 커넥션이 일어나는지 listen하여야 한다는 (일반적으로 이는 스왑공간 등의 시스템 리소스 낭비를 의미한다) 단점이 있다. 그리하여 거의 모든 UN*X는, 서비스의 수 만큼 소켓을 생성하고 select(2) 시스템 콜을 사용하여 그를 모두에 대해 listening 하는 "super-server"를 돌린다. 리모트 호스트가 그 서비스들 중 하나를 요구할 때, super-server는 이를 인지하여 이 포트에 지정된 서버를 실행 시킨다. 보통 사용하는 super-server는 inetd, Internet Daemon이다. 그것은 시스템 부팅시에 구동되고, /etc/inetd.conf 파일을 그 때 읽어 서비스의 목록을 얻는다. 서버가 불러들인 서비스 외에도, 인터널 서비스(internal services)라는, inet 자신 스스로 수행하는 몇가지 사소한 서비스도 있는데, 단순히 문자열을 발생시키는 chargen을 비롯하여, 그 시스템의 시간을 알려주는 daytime등이 그것이다. 이 inet.conf 파일에서의 각 엔트리는 다음의 필드로 이루어진다.
service type protocol wait user server cmdline 각 필드의 의미는 다음과 같다.
# # inetd services ftp stream tcp nowait root /usr/sbin/ftpd in.ftpd -l telnet stream tcp nowait root /usr/sbin/telnetd in.telnetd -b/etc/issue #finger stream tcp nowait bin /usr/sbin/fingerd in.fingerd #tftp dgram udp wait nobody /usr/sbin/tftpd in.tftpd #tftp dgram udp wait nobody /usr/sbin/tftpd in.tftpd /boot/diskless login stream tcp nowait root /usr/sbin/rlogind in.rlogind shell stream tcp nowait root /usr/sbin/rshd in.rshd exec stream tcp nowait root /usr/sbin/rexecd in.rexecd # # inetd internal services # daytime stream tcp nowait root internal daytime dgram udp nowait root internal time stream tcp nowait root internal time dgram udp nowait root internal echo stream tcp nowait root internal echo dgram udp nowait root internal discard stream tcp nowait root internal discard dgram udp nowait root internal chargen stream tcp nowait root internal chargen dgram udp nowait root internal 그림 9.1은 inetd.conf파일이다. finger 서비스는 주석처리되어 있어, 아용 가능하지 않다 보안상의 이유로 이렇게 하는데, 왜냐하면 attacker가 시스템의 유저 네임을 얻는데 사용할 수도 있기 때문이다. tftp도 역시나 주석처리 되어 있다. tftp, 즉 Primitive File Transfer Protocol은 패스워드 검증 없이도 시스템내의 world-readable한 파일을 전송해 준다. 이는 특히 /etc/passwd 파일을 전송해 줄 수 있어 위험한 것이고, shadow 패스워드를 사용하지 않을 경우 더욱 그렇다. TFTP는 디스크 없는 클라이언트나 X 터미널이 부트 서버에서 자신의 코드(커널)를 다운로드 하는데 사용하는 것이다. 만약 이러한 이유로 tftp를 돌리고자 한다면, 파일을 얻을 디렉토리를 tftp의 커맨드라인에 추가해 주어 그것의 한계를 그 디렉토리에만 제한 되도록 만들어야한다. 예제에서 두번째 tftp 라인이 그와 같다.
9.2 The tftpd access control facility 컴퓨터를 인터넷 억세스에 열어 놓는다는 것이 많은 위험을 끌어들이는 일이기 때문에, 어플리케이션은 몇가지 타입의 어택을 막을 수 있도록 설계되었다. 그러나 이 중 어떤것엔 약점이 있거나(RTM 인터넷 웜이 이를 아주 잘 증명한다), 특정 서비스 요구를 받아들이거나 거부해야할, secure 호스트와 insecure 호스트를 구분하지 몰하기도 한다. 우리는 이미 finger나 tftp 서비스에 대해 위에서 얘기한 바 있다. 그리하여 이러한 서비스를 "trusted 호스트"에만 국한 시키고자 한다면, 이는 보통의 셋업으로는 볼가능하고, 별도의 조치가 필요하다. 왜냐하면 inetd는 이 서비스를 모든 클라이언트에 제공하거나 전혀 하지 않거나 그 둘 중의 하나이기 때문이다. 이를 위한 유용한 툴이 바로 daemon wrapper라 불리는 tcpd이다. 당신이 모니터하거나 보호 해야한다고 판단되는 TCP 서비스가 있다면 서버 프로그램 대신에 그것을 적어두자. tcpd는 request가 있으면 syslog 데몬에 로그를 남기고, 리모트 호스트가 그 서비스를 사용하도록 허용되어 있을 때에만 실제 서버 프로그램을 실행시킨다. 이는 UDP 기반 서비스에선 동작하지 않는다는데 주의하라. 예를 들어, finger 데몬을 wrap한다면 inetd.conf에서 그에 대한 라인을 다음과 같이 고쳐주어야 한다.
#wrap finger daemon finger stream tcp nowait root /usr/sbin/tcpd in.fingerd 억세스 컨트롤을 추가하지 않는다면, 이는 syslog의 auth 기능에 로그를 보낸다는 것 외에는 보통의 finger 셋업과 같다. 억세느 컨트롤은 /etc/hosts.allow와 /etc/hosts.deny라는 두개의 파일에 의해 실행된다. 그것들은 특정 서비스와 호스트에 대해 각각 허용하는, 그리고 거부하는 억세스의 엔트리를 갖고 있다. tcpd가 가령 biff.foobar.com이라는 클라이언트 호스트에서 온 finger와 같은 서비스 요청을 다루고 있을 때, 그것은 hosts.allow와 hosts.deny를 (순서대로) 검사하여, 그 서비스와 클라이언트 호스트 양쪽 모두 일치하는 엔트리가 있는지 살펴본다. 만약 일치하는 엔트리가 hosts.allow에 일다면 hosts.deny에 어떤 엔트리가 있든지 상관 없이 억세스를 받아들인다. hosts.deny에서 그것이 발견되면 커넥션을 끊음으로써 그 요청을 거부한다. 일치하는 것을 찾지 못했을 때엔 그 요청을 받아들인다. 억세스 파일에서 각 엔트리는 다음과 같은 형식을 따른다.
servicelist: hostlist [:shellcmd]servicelist는 /etc/service의 서비스 네임의 목록 또는 ALL 키워드이다. finger와 tftp를 제외한 모든 서비스를 의미하고자 한다면, "ALL EXCEPT finger, tftp"를 사용하라. hostlist엔 IP 주소또는 호스트네임의 목록, 또는 ALL, LOCAL, UNKNOWN중의 하나가 들어간다. ALL은 모든 호스트에 해당되며, LOCAL은 같은 점을 포함하지 않는(로컬도메인 상의) 호스트네임에, UNKNOWN은 호스트네임이나 주소를 검색할 수 없는 어떤 호스트를 의미한다. 점으로 시작되는 이름은 이 이름과 동일한 도메인의 모든 호스트에 대응된다. 예를 들어, .foobar.com은 biff.foobar.com에 일치한다. 역시나 IP 네트웍 주소와 서브넷도 이와 같은 방법으로 가능하다. 좀더 세부적인 사항은 hosts_access(5) 매뉴얼 페이지를 참고하기 바란다. 로컬 호스트외에는 finger와 tftp를 거부하도록 하기 위해선, /etc/hosts.deny에 다음의 라인을 추가하고, /etc/hosts.deny는 비워두라.
in.ftpd: ALL EXCEPT LOCAL, .vbrew.com : \ echo "request from %d@%h" >> /var/log/finger.log: \ if [%h != "vlarger.vbrew.com" ]; then \ finger -l @%h >> /var/log/finger.log \ fi%h와 %d 인자는 tcpd에 의해 각각 클라이언트 호스트와 서비스 명칭으로 확장된다. 세부항목은 hosts_access(5) 매뉴얼 페이지에서 설명하고 있다.
9.3 The service and protocol Files 특정 "표준" 서비스에 부여된 포트번호는 "Assigned Numbers" RFC 에서 정의하고 있다. 서버와 클라이언트가 서비스 네임을 이 번호로 변환할 수 있도록하기 위해선, 적어도 목록의 일부분이라도 각 호스트에 지니고 있어야 한다. 그것은 /etc/services라는 파일에 저정되며, 그 엔트리의 형태는 다음과 같다.
services port/protocol [aliases] services엔 서비스 명칭을 지정하고, port엔 서비스가 제공되는 포트를 정의하며, protocol은 어떤 전송 프로토콜이 사용되는지 나타내는데, 보통 이는 udp이거나 tcp이다. 또한, 한 서비스가 하나 이상의 프로토콜을 제공하거나, 다른 프로토콜을 사용하는 다른 종류의 서비스들이 동일한 포트를 사용하는 것이 가능하다. aliases필드는 서비스에 대한 별명을 지정하는데 쓰인다. 일반적인 경우, 당신의 리눅스 시스템의 네트웍 소프트웨어에 딸려오는 서비스 파일을 변경할 필요가 없으나, 아래에 약간이나마 발췌해 보여주고자 한다.
# The services file: # # well-known services echo 7/tcp # Echo echo 7/udp # discard 9/tcp sink null # Discard discard 9/udp sink null # daytime 13/tcp # Daytime daytime 13/udp # chargen 19/tcp ttytst source # Character Generator chargen 19/udp ttytst source # ftp-data 20/tcp # File Transfer Protocol (Data) ftp 21/tcp # File Transfer Protocol (Contr telnet 23/tcp # Virtual Terminal Protocol smtp 25/tcp # Simple Mail Transfer Protocol nntp 119/tcp readnews # Network News Transfer Protoco # UNIX services exec 512/tcp # BSD rexecd biff 512/udp comsat # mail notification login 513/tcp # remote login who 513/udp whod # remote who and uptime shell 514/tcp cmd # remote command, no passwd use syslog 514/udp # remote system logging printer 515/tcp spooler # remote print spooling route 520/udp router routed # routing information protocol 예를 들어, echo 서비스는 포트 7에서 TCP와 UDP로 모두 제공되고, 포트 512는 UDP를 사용하는 COMSAT 데몬 (유저에게 새 메일의 도착을 알리는 프로그램, xbiff(1x)를 보라)과 TCP를 사용하는 원격실행(rexec(1))의 서로 다른 두 서비스에 의해 사용된다. 서비스 파일과 비슷하게, "네트워킹 라이브러리엔 (서비스 파일에서 사용되는) 프로토콜 명을 다른 호스트의 IP 레이어가 해석할 수 있게끔 프로토콜로 변환할 방법이 필요하다. 이는 /etc/protocol 파일내의 이름을 검색함으로써 이루어진다. 그 파일에는 각 라인마다, 프로토콜 명과 그에 대응되는 숫자를 지니는 엔트리가 들어있다. 이 파일은 /etc/services 파일을 고치는 일보다 보다 더 필요가 없다. 아래는 예제 파일이다
# # Internet (IP) protocol # ip 0 IP # internet protocol, pseudo protocol number icmp 1 ICMP # internet control message protocol igmp 2 IGMP # internet group multicast protocol tcp 6 TCP # transmission control protocol udp 17 UDP # user datagram protocol raw 255 RAW # RAW IP interface
클라이언트- 서버 어플리케이션의 아주 일반적인 메카니즘은 RPC(Remote Procedure Call 패키지)에 의해 제공되었다. RPC는 Sun Microsystems에 의해 개발된 툴과 라이브러리 함수의 모음이다. RPC 성공의 발판이 된 주요 어플리케이션은 NFS(Network Filesystem)과 NIS(Network Information System)으로, 이 둘에대해선 나중에 따로 소개할 것이다. RPC 서버는, 클라이언트가 프로시저 파라미터를 통해 서버에게로 RPC request를 보냄으로써 call할 수 있는, 프로시저의 모음으로 구성되었다. 서버는 클라이언트를 대신해 지시된 프로시저를 실행하고, 리턴 값이 있을 경우 그것을 되돌려준다. machine-independent하기 위해, 클라이언트와 서버간에 교환되는 모든 데이터는 External Data Representation 포맷 (XDR)이란 것으로 송신측에 의해 변환되고, 수신측에서는 이를 다시 machine-local representation으로 변환한다. 때때로, 개선된 RPC 어플리케이션은 프로시저 콜 인터페이스에서, 비호환적인 변경점을 소개한다. 물론 단순히 서버를 변경하는 것도, 이전의 동작을 기대하는 모든 어플리케이션이 제대로 돌아가지 않게한다. 그리하여, RPC 프로그램에는 버전번호가 부여되어 있으며, 이는 보통, 1에서 시작하여 각 RPC인터페이스의 새 버전에선 이 수가 올라간다. 일반적으로, 서버는 여러개의 버전을 동시에 제공하며, 클라이언트는 request내의 버전번호를 통해 자신이 사용하고자 하는 서비스의 implementation이 무엇인지를 가리킨다. RPC 서버와 클라이언트간의 네트웍 통신은 다소 묘한데가 있다. RPC 서버는 하나 이상의 프로시저 모음을 제공하고, 각 세트는 프로그램(program)이라 불리며, 각 프로그램은 프로그램 번호(program number)에 의해 구별된다. 서비스 네임을 프로그램 번호로 매핑한 목록은 보통, /etc/rpc에 들어 있으며, 그림 9.2는 그 일부를 발췌한 것이다.
# # /etc/rpc - miscellaenous RPC-based services # portmapper 100000 portmap sunrpc rstatd 100001 rstat rstat_svc rup perfmeter rusersd 100002 rusers nfs 100003 nfsprog ypserv 100004 ypprog mountd 100005 mount showmount ypbind 100007 walld 100008 rwall shutdown yppasswdd 100009 yppasswd bootparam 100026 ypupdated 100028 ypupdate
이전에 RPC의 제작자는 TCP/IP 네트웍 내에서 프로그램 번호를 일반 네트웍 서비스로 매핑하는데 대한 문제로 고심하였다. 결국 그들은 각 서버가 각각의 프로그램과 버전에 대해 TCP와 UDP 포트를 모두 제공하도록 만드는 일을 선택했다. 일반적으로, RPC 어플리케이션은 데이터를 보낼 때 UDP를 사용하고, 전송될 데이터그램이 단일 UDP 데이터그램에 들어가지 않을 때에만 TCP를 사용한다. 물론, 클라이언트 프로그램도 프로그램 번호가 매핑된 포트가 어떤 것인지 찾아낼 방법을 가지고 있어야한다. 이에 설정파일을 사용하는 것은 너무도 융통성 없는 방법일 것이다. RPC 어플리케이션이 예약된 포트를 사용할 수 없기 때문에, 원래 데이터베이스 어플리케이션에 사용되어야할 포트가 어떤 다른 프로세스에 의해 사용되지 않는다는 보장이 없는 것이다. 그리하여, RPC 어플리케이션은 얻을 수 있는 아무 포트나 찍어서, 일명 partmapper daemon이라는 것에 그것을 등록한다. portmapper 데몬은 머신상에 돌아가는 모든 RPC 서비스에 대한 서비스 브로커처럼 동작하는 것으로, 클라이언트가 프로그램 번호를 주어 서비스에 접속하고자 할 때, 가장 먼저 서버의 portmapper에 쿼리한다. 그러면, 서비스를 할 수 있는 TCP와 UDP 포트번호를 되돌려 준다. 이 방법은, 표준 Berkely 서비스에 대해 inetd 데몬이 하는 것과 아주 유사하게, 실패에대한 단편적인 부분밖에 알려주지 못하는 데다가, 심지어 그보다 더 조악하기까지 하다. 왜냐하면 portmapper가 죽었을 때 모든 RPC 정보를 잃어 버리기 때문으로, 이는 보통 모든 RPC서버를 수동으로 재시동하든지 전체 머신을 리부트 시켜야 한다는 것을 의미한다. 리눅스 상에서 portmapper는 rpc.portmap의 이름을 가지고 있으며, /usr/bin에 들어있다. portmapper를 구동 시키는 구절이 rc.inet2에 있는지 확인만 하면 portmapper설정은 끝난다.
9.5 Configuring the r Commands 리모트 상에서 커맨드를 실행시킬수 있는 커맨드로 rlogin, rsh, rpc, rcmd가 있다. 이것들은 모두 리모트 호스트에서 쉘을 띄워 유저가 커맨드를 실행할 수 있게한다. 물론, 클라이언트는 커맨드를 실행할 호스트에 계정을 갖고 있어야한다. 따라서 이들 커맨드는 모두 인증 절차를 수행하는데, 보통 클라이언트가 서버에 유저의 로그인 네임을 말하면 서버는 패스워드를 요구한다. 그러나 이따금씩 특정 사용자에 대해서 조금 느슨한 인증을 원하기도 하는데, 예를들어, 당신이 같은 LAN 상에 존재하는 다른 머신으로 이따금씩 들어가고자 할 때, 매번 패스워드를 쳐 넣지 않았으면 할 것이다. 인증을 하지 않은 일은, 동기화된 패스워드 데이터베이스를 가진 몇몇의 호스트에서나, 또는 관리상의 이유로 수많은 머신에 억세스할 필요가 있는 특권 유저에 대해서나 이용하길 권한다. 사람들이 로그인 id나 패스워드를 지정해 주지 않고서도 언제나 당신의 호스트로 로그인 해 들어 올 수 있게 하고자 한다면, 어떤사람이 우연히 억세스한 것을 받아들이지 않도록 확인해야 한다. r 커맨드의 인증 체크를 꺼두는데는 두가지 방법이 있다. 한가지는 슈퍼유저가 특정 또는 모든 호스트 상의 특정 또는 모든 유저에 대해 패스워드를 묻는 일 없이 로그인해 들어올 수 있도록 하는 방법으로(모든 호스트의 유저에 대해 허용하는 것은 정말로 좋지 않은 생각이다), 이 억세스는 /etc/hosts.equiv라는 파일에서 조정할 수 있다. 그 파일엔 호스트와 유저 네임의 목록이 들어 있으며 이는 로컬 호스트상의 유저들과 동등한 것으로 간주된다. 또다른 방법으로는 사용자가 특정 호스트의 또다른 유저 계정으로 자신의 계정에 억세스 하게끔 만드는 것이며, 이는 홈 디렉토리의 .rhosts 파일에 나열된다. 보안상의 이유로 이 파일은 반드시 유저나 슈퍼유저의 소유여야 하며, 심볼릭 링크여서는 안된다. 그렇지 않을 경우 그것은 무시된다. 클라이언트가 r 서비스를 요청할 때, 그의 호스트와 유저네임을 /etc/hosts.equiv에서 찾고, 그리고 그 권한으로 로그인 해 들어가고자 하는 유저의 .rhosts파일에서 그것을 찾는다. 예를 들어, janet은 gauss에 있고, euler의 joe 계정으로 로그인해 들어가고자 한다고 가정하자. 이후로 Janet은 클라이언트 유저로, Joe는 로컬 유저라고 칭한다. 그러면 이제, Janet이 다음을 gauss에 쳐 넣을 때,
$rlogin -l joe euler 서버는 먼저 Janet이 자유롭게 억세스하게끔 되어 있는지 아닌지를 알기 위해 hosts.equiv를 체크한다. 만약 여기에서 실패한다면, 그것은 Joe의 홈 디렉토리에 있는 .rhost에서 그녀가 있는지 검색한다. euler의 hosts.equiv파일은 다음과 같다.
gauss euler -public quark.physics.groucho.edu andres 하나의 엔트리는 호스트 네임과 부가적은 유저네임으로 이루어진다. 만약 호스트네임만이 있아면, 그 호스트의 모든 유저가 어떠한 체크 없이도 로컬 계정으로 들어올 수 있게 된다. 워의 예제에서, Janet은 gauss에서 들어온다면 그녀의 계정은 janet으로 로그인해 들어갈 수 있고, root를 제외한 다른 유저의 경우로 이와 마찬가지이다. 그러나, 만약 Janet이 joe의 계정으로 로그인하려 한다면, 보통의 경우와 마찬가지로 패스워드 프롬프트를 받을 것이다. 만약 위 예제파일의 마지막 라인의 경우처럼 호스트네임 뒤에 유저네임이 있다면, 이 유저는 root 계정을 제외한 모든 계정에 대해 패스워드 없이 억세스 할 수 있다. "-public" 처럼 마이너스 기호가 앞에 붙은 호스트네임은, 개별 유저들이 .rhosts파일을 어떻게 설정했는지에 상관없이 public의 모든 유저에 대해 인증을 요구하게 만든다. .rhosts 파일의 포맷은 hosts.equiv의 그것과 동일하나, 의미는 약간 다르다. euler 상에서 Joe의 .rhosts 파일이 다음과 같다고 간주하자.
chomp.cs.groucho.edu gauss janet 첫번째 엔트리는 chomp.cs.groucho.edu에서 로그인 해 들어올 때 패스워드 없이 joe에 억세스하게끔 하나, euler나 chomp의 다른 계정의 권한에는 영향을 미치지 않는다. 두번째 엔트리는 이와 약간 달리, janet이 gauss에서 로그인 할 때엔 Joe의 계정에 자유로이 억세스할 수 있게 한다. 주의할 것은, 클라이언트의 호스트 네임을 얻을때, 호출자의 주소를 네임으로 역 매핑한다는 것으로, 이에 따라 resolver에 unknown한 호스트에서는 이러한 기능을 사용할 수 없다. 클라이언트의 호스트네임이 호스트파일의 이름과 일치하는 것으로 간주하는 경우는 다음 중의 하나이다.
|