본문 바로가기

운영체제/리눅스

[처음부터 다시 배우는 리눅스] ② 고급 명령과 시스템 관리

출처 : http://blog.naver.com/mybrainz/140007538295
[처음부터 다시 배우는 리눅스] ② 고급 명령과 시스템 관리


연재순서
1회.
개발자를 위한 리눅스 설치와 기본 명령어
2회. 고급 리눅스 명령어와 중요 시스템 관리
3회. 개발자를 위한 vim 편집기 사용법
4회. 오픈소스 프로젝트의 필수 개발툴 활용 <끝>


지난 호에 이어 이번 호에서는 리눅스를 설치 한 다음 숙지해야 할 리눅스 시스템의 특성을 살펴보기로 한다. 필자 개인적인 경험으로는 리눅스 시스템의 여러 토픽들 중에서 리눅스를 많이 써 보지 않은 사람들이 조금 당혹할 수 있는 부분이 리눅스의 보안 시스템이라고 생각한다. 실제, 윈도우는 최근 윈도우 2000, XP에 와서야 멀티유저의 개념이 제대로 정착되어 나가고 있는 셈이지만 리눅스는 애초부터 멀티유저의 개념에서 설계가 되었고 또 그렇게 운영되는 시스템이다.

멀티유저 운영체제에서는 자연스럽게 사용자간의 파일 접근 권한이라든가 보안 문제가 부상하게 된다. 대부분의 리눅스 사용자들이 처음에는 윈도우를 쓰는 경우가 많은 까닭에 멀티유저 시스템은 낯설게 느껴질 수밖에 없으며 웹 서버 프로그래밍이 많은 요즈음의 특성상 보안의 기초가 되는 리눅스의 파일 접근 권한(permission)과 소유권(ownership)에 대한 지식은 단단히 다져놓고 넘어가도록 하자.

파일 접근 권한과 소유권
유닉스의 심플한 설계 철학은 파일 소유권에도 반영되어 있는데 우선 유닉스의 파일은 읽기(read), 쓰기(write), 실행하기(execute) 세 가지의 기능을 수행할 수 있다. 여기서 잠깐 주의할 것은 cd 명령으로 디렉토리를 바꾸어 들어갈 수 있는 권한은 실행하기에 해당한다는 것이다.

유닉스 쪽의 파일 소유권은 우선 사용자(user), 그룹(group), 그리고 그 외의 사람들(others)의 관점으로 나누어진다. 즉, 파일 하나에 대해서 사용자, 그룹, 그리고 그 외의 사람들은 각기 다른 접근 권한을 가질 수 있도록 설계되어 있다. 다음을 보자.


r w x   r w x   r w x
4 2 1   4 2 1   4 2 1   (2^2, 2^1, 2^0)
O O O | O O O | O O O
user    group   others


유닉스의 파일 소유권은 이와 같이 간단히 도식화 해 볼 수 있다. 사용자, 그룹, 그 외의 사람들마다 3비트로 각각 read, write, execute의 스위치를 on/off 할 수 있다. 3비트는 숫자로 표현해보면 0에서 7까지의 숫자로 나타낼 수 있다. 예를 들어, 어떤 파일의 소유권자에게 읽기와 쓰기 권한을 주고 싶으면 r 스위치와 w 스위치를 올리면 되니 이것을 숫자로 표현해보면 4+2+0=6이 된다. 마찬가지로 읽기와 실행 권한만 주고 싶다면 r 스위치와 x 스위치를 올려서 4+0+1=5가 된다. 이런 방식으로 유닉스의 chmod 명령에서는 숫자를 이용, 파일의 퍼미션을 설정할 수 있다. 다음 예를 보면서 좀 더 자세히 살펴보자.


$ ls -l foobar.txt
-rw-rw----    1 mewmew   mewmew        500  6월  8 23:00 foobar.txt
$ chmod 640 foobar.txt
$ ls -l foobar.txt
-rw-r-----    1 mewmew   mewmew        500  6월  8 23:00 foobar.txt


좀 더 퍼미션의 숫자 표현에 친숙해 지기 위해 한번 8가지 퍼미션의 경우를 다 적어보면 다음과 같다. 조금 귀찮아 보여도 한번 손가락으로 직접 꼽아가며 확인해 보자.


---  0 --x  1
-w- 2 -wx 3
r--   4 r-x  5
rw-  6 rwx 7


파일 소유권이 설정되는 과정
이제 유닉스에서 파일 소유권이 어떻게 결정되는지 알아보자. 유닉스에서 파일 소유권은 원칙적으로 그 파일을 ‘생성한’ 유저가 갖게 된다. 즉, mewmew라는 유저가 foobar.txt라는 파일을 하나 만들었다고 가정하자. 유닉스 시스템은 디폴트로 새로이 생성되는 foobar.txt의 소유자를 파일을 생성한 mewmew로 설정하게 된다.

그 다음으로 유닉스 시스템은 foobar.txt의 그룹 소유권을 결정하게 되는데 이를 위해 mewmew라는 유저가 어떤 그룹에 속해 있는지를 /etc/passwd의 패스워드 엔트리를 보고 결정하게 된다. /etc/passwd에 mewmew 유저는 다음과 같이 등록되어 있다.


mewmew:x:1012:1012:,,,:/home/mewmew:/bin/bash


혹 /etc/passwd의 문법을 잘 모르는 사람은 박스 기사를 참고해서 passwd의 매뉴얼 페이지를 잠깐 읽어보자. 여기서 mewmew은 디폴트로 1012의 uid를 가지고 1012의 gid를 가짐을 알 수 있다. 그렇다면 이제 /etc/group를 잠깐 보자.


mewmew:x:1012:


따라서 mewmew이 생성한 foobar.txt의 그룹 소유권자의 디폴트는 1012의 gid를 갖는 mewmew 그룹이 된다. 그렇다면 이 foobar.txt의 기본 읽기/쓰기/실행 퍼미션은 어떻게 정해질까? 유닉스에서는 umask를 이용해서 생성되는 파일의 기본 퍼미션이 정해진다. umask는 masking, 즉 빼기 방식으로 파일 퍼미션을 정하는데 보통 022를 umask 값으로 많이 쓴다. /etc/profile의 쉘 설정을 보면 보통 다음과 같은 명령어를 볼 수 있다.


umask 022


이 명령이 실행되면 일반 파일은 666(rw-rw-rw)의 퍼미션에서 umask의 022를 뺀 644(rw-r--r--)의 디폴트 퍼미션을 갖게 되고 디렉토리의 경우는 777(rwxrwxrwx)의 퍼미션에서 umask의 022를 뺀 755(rwxr-xr-x)를 갖게 된다.

setuid, setgid, sticky bit
setuid는 유닉스에서 볼 수 있는 특이한 파일 보안 개념이다. 일반적으로 실행 가능한 유닉스 파일을 실행할 때 유닉스에서는 그 명령을 내린 유저의 권한에 따라 실행하게 된다. 예를 들어 mewmew 유저가 다음과 같은 퍼미션의 /bin/ls 명령을 실행시키면 mewmew 유저는 root 유저도 아니고, wheel 그룹에 속해 있지도 않지만 others 퍼미션에 명시된 r-x 권한 중에서 x 권한이 허가되어 있는 까닭에 유닉스 시스템은 mewmew 유저의 /bin/ls 명령 실행을 허가해 주게 된다.


-r-xr-xr-x  1 root  wheel  32464 28 May 01:43 /bin/ls


그런데 setuid 옵션이 켜진 파일은 실행될 때, 예를 들어 앞의 경우 /bin/ls 명령에 setuid가 켜져 있다면 mewmew 유저가 명령을 실행했더라도 유닉스 시스템은 /bin/ls 파일의 소유자인 root 유저가 명령을 실행한 것으로 간주해서 /bin/ls 명령을 실행한다. 즉, /bin/ls 명령이 mewmew가 아닌 root 소유로 실행되는 것이다. setuid를 켜는 방법은 다음과 같다. 두 명령 모두 동일한 명령이다. 아래 명령의 경우는 rwxr-xr-x 퍼미션이 755이고 앞의 4가 setuid 옵션이다.


$ chmod u+s foobar
$ chmod 4755 foobar


두 번째 명령의 경우 숫자 표현이 조금 독특한데 한번 섹션 2의 chmod의 매뉴얼 페이지의 일부분을 인용해 보겠다. 다음의 매뉴얼 페이지 인용을 보면 유닉스의 파일에는 user, group, others이외에 디폴트로 생략되어 있는 3 비트짜리 setuid, setgid, sticky bit 옵션이 있으며, 차례대로 4, 2, 1의 숫자로 나타낼 수 있음을 알 수 있다.


CHMOD(2)            Linux Programmer's Manual            CHMOD(2)

NAME
       chmod, fchmod - change permissions of a file

SYNOPSIS
       #include
       #include

       int chmod(const char *path, mode_t mode);
       int fchmod(int fildes, mode_t mode);

DESCRIPTION
       The mode of the file given by path or referenced by fildes
       is changed.

       Modes are specified by or'ing the following:

              S_ISUID   04000 set user ID on execution

              S_ISGID   02000 set group ID on execution

              S_ISVTX   01000 sticky bit

              S_IRUSR (S_IREAD)
                        00400 read by owner
(이하 생략)


이런 방식은 어디에서 응용하면 좋을까? 간단한 예가 게임에서 하이 스코어 리스트를 관리하는 경우를 들어볼 수 있다. 하이 스코어 리스트는 일반 유저들은 마음대로 고칠 수 없도록 해야 하지만 막상 서로 다른 유저가 하나의 하이 스코어 리스트 파일을 공유하는 것은 쉽지 않은 일이다. foobar, doobar 유저가 fungame을 하는 상황을 생각해보자. fungame의 파일 퍼미션은 다음과 같다. fungame 유저는 fungame 실행파일을 위해 새로 만들어진 유저이다. 그리고 fungame_high_score_list를 다음과 같이 구성했다.


-r-sr-xr-x  1 fungame  wheel    65535 28 May 08:13 /usr/local/bin/fungame
-rw-r--r--  1 fungame  fungame  38    28 May 09:00 /usr/local/etc/fungame_high_score_list


setuid가 설정되어 있지 않는 경우라면 foobar 유저가 fungame을 실행하면 fungame 실행파일은 foobar 유저의 소유로 실행되기 때문에 fungame에서 fungame_high_score_list 파일을 액세스하면 others에 해당하는 읽기 권한밖에 얻을 수 없다. doobar 유저 역시 마찬가지로 fungame이 실행될 때 doobar 유저가 others로 분류되는 바람에 fungame에서 하이 스코어 리스트 파일을 읽을 수밖에 없는 상황이 벌어진다.

하지만 fungame 실행파일에 setuid를 켜 주면 상황이 달라진다. setuid가 설정된 fungame 실행파일은 어떤 사용자가 이 명령을 실행시키든지 간에 fungame 사용자가 명령을 실행한 것으로 간주하기 때문이다. 따라서 fungame 실행파일은 사용자에 상관없이 fungame_high_score_list 파일을 읽고 쓸 수 있게 된다.

이런 setuid 보안 모델은 보통 일반 유저가 시스템 관련 명령을 실행했을 때 그 명령이 잠깐 동안 관리자인 root 권한을 얻을 필요가 있을 때 많이 사용된다. 이것을 보통 setuid root라고 부른다. setuid root는 보안상의 문제를 초래하기도 하지만 유저에 따라서 케이스를 모두 구분해 주어야 하는 복잡한 경우를 피할 수 있다. 앞의 게임 하이 스코어 리스트 예를 passwd 명령과 /etc/passwd로 바꾸어 생각해 보면 setuid root의 유용함을 쉽게 알 수 있다. 참고로 /etc/passwd와 /usr/bin/passwd의 파일 퍼미션을 살펴보면 다음과 같다. 예상대로 /usr/bin/passwd는 setuid가 root 유저로 설정되어 있고 /etc/passwd 파일은 root 유저가 읽기와 쓰기 권한을 갖고 있음을 볼 수 있다.


-r-s--x--x    1 root     root        13476  6월 18  2002 /usr/bin/passwd
-rw-r--r--    1 root     root        43188  6월 16 23:10 /etc/passwd


하지만 setuid root는 보안상의 문제가 생길 수 있다는 점을 항상 염두에 두고 있어야 한다. 간단한 예로, 누군가가 root 권한을 재주껏 얻은 다음 bash 하나를 복사하고 거기에 setuid를 설정해 놓은 경우를 생각해보자.


-rwsr-xr-x    1 root     root       519964  7월  9  2001 bash


이 셸을 실행시키는 사용자는 당연히 root 권한을 갖게 된다. 실제, 간혹 리눅스 서버가 해킹에 뚫리고 난 뒤에는 이렇게 setuid root된 셸이 만들어져 있는 경우가 많다.

setgid는 setuid 만큼 자주 발생하지는 않지만 그 원리는 setuid와 마찬가지로 setgid가 설정된 파일은 실행될 때 그룹 소유자가 그 파일에 지정된 그룹으로 설정된다. setgid는 write 명령과 같이 특정 그룹을 매개체로 액세스 권한을 공유할 때 사용된다. write 명령을 통해 다른 유저에게 메시지를 보내는 경우 setgid가 없는 경우는 다른 사람의 터미널 디바이스(/dev/tty??와 같은)에 디폴트로 쓰기를 할 수 없고 따라서 메시지를 보낼 수 없게 된다. 하지만, 유저들이 쓰는 모든 터미널 디바이스 파일을 tty와 같은 그룹으로 묶고, write 파일에 tty 그룹으로 setgid를 해 놓으면 그룹 퍼미션을 통해서 다른 유저의 터미널 화면에 쓰기를 하는 것이 가능해진다.

sticky bit은 /tmp와 /var/tmp와 같이 임시 파일이 생겨났다가 없어지는 디렉토리에 자주 사용된다. /tmp 디렉토리의 경우 모든 유저들이 마음대로 파일을 읽고 쓰는 것이 가능해야 하는데 이렇게 되면 /tmp 디렉토리는 rwxrwxrwx 퍼미션을 가져야 하고 이것은 유저들이 /tmp 디렉토리에서는 다른 유저의 파일을 지울 수도 있다는 문제를 초래할 수 있다. 하지만 sticky bit이 설정된 디렉토리에서 유저 A는 유저 B가 생성한 파일을 지울 수 없게 된다. 좀 더 정확히 말하면 /tmp와 같은 sticky bit이 설정된 디렉토리에서는 디음과 같은 경우가 아니고서는 파일을 지울 수 없다.


◆ 파일의 소유자
◆ 파일이 놓인 디렉토리의 소유자
◆ 수퍼유저


sticky bit은 다음과 같이 t가 추가된 형태로 표기된다. 숫자로 표현하면 1777이 될 것이다. 이것 역시 앞에 잠깐 언급한 chmod(2) 매뉴얼 페이지를 참고해 보자.


drwxrwxrwt  105 root     root       102400  6월 13 13:09 /tmp


su
일반적으로 윈도우를 많이 쓰던 사용자가 리눅스를 쓰기 시작하면서 접하기 쉬운 실수가 수퍼유저인 root 계정으로 리눅스 시스템을 쓰는 것이다. 하지만 윈도우와는 달리 리눅스는 일반 유저 계정으로도 별다른 불편 없이 모든 작업을 할 수 있고, 필요할 때만 root 계정을 쓰면 되며, 특히 일반 유저 계정을 쓸 경우 바이러스와 같은 악성 코드가 실행 권한을 얻게 되는 것을 구조적으로 방지할 수 있기 때문에 처음부터 일반 사용자로 시스템을 사용하는 습관을 들이는 것이 중요하다. 그런 이유로 리눅스에서는 필요할 때만 su 명령으로 root shell을 획득하고 그렇지 않은 경우는 항상 일반 유저의 권한으로 컴퓨터를 사용하는 것이 좋다. 최근에 윈도우 XP에서도 도입된 Switch User 기능이 윈도우판 su라고 볼 수 있겠다.


리눅스 시스템의 환경 변수와 디렉토리
필자의 개인적인 생각이지만 리눅스 데스크탑이 아직까지 널리 쓰이지 않고 있는 이유 중의 하나는 리눅스 배포판이 전통적으로 보안에 신경을 많이 쓴 형태로 설정되어 나오기 때문이라고 본다. 따라서 윈도우를 쓰던 사용자들이 리눅스를 쓰면 보안상 이유로 디폴트로 숨겨놓은 기능들을 잘 모르고 지나치게 되고, 이것이 나중에 불편함으로 느껴지는 경우가 많은 듯 하다. 사용자 홈 디렉토리와 환경 변수에서 이런 사소한 불편함을 간단히 몇 가지 다루어 보자.

PATH 환경 변수
우선, 리눅스에서는 명령을 실행할 때 기본적으로 수퍼유저의 권한이 필요한 실행파일들의 디렉토리 위치를 PATH 환경변수에서 빼 놓고 있다. 실제, su 명령을 실행한 다음 fdisk와 같은 수퍼유저용 명령을 실행해보면 대부분의 리눅스 배포판에서 command not found라는 에러 메시지를 만나게 된다. 도대체 PATH가 어떻게 설정되어 있을까?


$ echo $PATH
/usr/local/bin:/usr/bin:/bin:/usr/bin/X11:/usr/games


그렇다면 fdisk는 어디에 들어가 있을까? 다음의 결과를 보면 /sbin/fdisk에 들어가 있음을 알 수 있다. 그러나 PATH 환경 변수에는 /sbin 디렉토리가 들어가 있지 않다. 따라서 일반 유저는 su 명령으로 수퍼유저가 되더라도 PATH 환경 변수에 /sbin 디렉토리가 들어가 있지 않은 까닭에 fdisk 명령어가 실행이 되지 않는 것이다. 참고로 리눅스의 명령어 중 일반 사용자용 명령어들은 전통적으로 bin 디렉토리 아래 많이 들어가고 수퍼유저용 명령어들은 sbin 디렉토리에 많이 들어간다.


$ find / -name fdisk
/usr/share/webmin/mscstyle3/fdisk
/usr/share/webmin/fdisk
/sbin/fdisk
/etc/webmin/fdisk


그러면 PATH 환경 변수에 /sbin 디렉토리를 추가해 보자. sbin 디렉토리는 /sbin 이외에도 /usr/sbin, /usr/local/sbin이 있을 수 있고, 보통 /usr/sbin에는 배포판이 설치될 때 /sbin 만큼 필수적이지는 않지만 중요한 시스템 관리 명령어들이 많이 들어가 있으니 /sbin과 함께 /usr/sbin도 같이 넣어보자. 참고로 bash 외의 csh이나 tcsh은 여기서는 다루지 않도록 하겠다.


$ export PATH=${PATH}:/sbin:/usr/sbin


이것을 전통적인 본 셸 방식으로 풀어쓰면 다음과 같이 된다.


$ PATH=${PATH}:/sbin:/usr/sbin
$ export PATH


그러나 이 명령을 셀 하나를 실행할 때마다 타이핑하기에는 귀찮을 테니 이것을 셸이 실행될 때마다 같이 실행되도록 해 보자. bash 셸은 시작하면서 다음과 같은 파일의 내용을 실행시킨다. bash의 매뉴얼 페이지를 잠깐 살펴 보면 FILES 항목 아래쪽에 설정파일에 대한 설명이 나온다.


/etc/profile - system-wide initialization file, executed for login shells
~/.bash_profile - personal initialization file, executed for login shells
~/.bashrc - the individual per-interactive-shell startup file


우리의 경우는 홈 디렉토리의 ~/.bash_profile이나 ~/.bashrc를 쓰는 것이 적당해 보인다. PATH 정보는 일반적으로 로그인 셸 설정에 속하니 .bash_profile에 넣어두면 된다. 참고로 리눅스 시스템 설정을 할 때는 별다른 필요가 없으면 홈 디렉토리 아래의 .로 시작하는 설정파일에 설정해 주는 것이 좋다. 물론, 수퍼유저로 변신해서 직접 /etc 아래의 설정파일을 바꾸어 줄 수도 있지만 항상 내가 수퍼유저 권한을 갖고 있을 수도 없는 것이고, 실제 일반 유저의 권한으로도 내 입맛에 맞게 시스템 환경을 상당한 부분 맞추어 볼 수 있다.

PATH 환경 변수와 관련해서 참고로 하나 알아 둘 것은 현재 디렉토리에 들어 있는 명령을 실행시키는 방법이다. 대부분의 리눅스 배포판에서 hello.c같은 소스를 컴파일하고 다음과 같은 명령을 실행해보면 에러가 난다.


$ gcc -o hello hello.c
$ ls -F
hello* hello.c
$ hello
bash: hello: command not found
$ ./hello
hello, world


이것은 PATH 환경 변수에서 현재 디렉토리인 점(.)이 빠져 있기 때문이다. 왜 이런 복잡해 보이는 방식이 디폴트로 설정되어 있을까? 그것은 보안상의 문제 때문이다. 예를 들어, 수퍼유저가 /tmp나 /var/tmp 디렉토리에서 무심코 ls 명령을 실행시켰는데 이것이 /bin/ls가 아닌 누군가가 심어놓은 악성 /tmp/ls를 실행시키게 된다면 문제가 생길 수 있다. 이것을 아예 원천적으로 방지하기 위해 현재 디렉토리의 명령어를 실행하려면 보통 앞에 명시적으로 ./를 붙여주는 것이다.

디렉토리
리눅스의 파일 시스템 디렉토리 구조는 전통적인 방식을 지금도 따르고 있다. 루트 디렉토리 아래에 실행 파일은 /bin, 라이브러리는 /lib, 설정파일은 /etc, 수퍼유저용 명령어는 /sbin과 같은 디렉토리에 들어가게 되어 있다. 여기서 유심히 보아야 할 것은 이런 bin, lib, etc와 같은 디렉토리 명명법은 다른 디렉토리 안에서도 반복이 된다는 사실이다. 한번, 소프트웨어 패키지들이 위치하고 있는 /usr 디렉토리를 살펴보자.


$ cd /usr
$ ls -F
X11R6/  dict/  etc/      kerberos/  libexec/  lost+found/  root/  share/  tmp@
bin/    doc/   include/  lib/       local/    man/         sbin/  src/


여기서도 친숙한 bin, etc, lib, sbin과 같은 디렉토리가 보임을 알 수 있다. 실제, 필자가 쓰는 리눅스 시스템에서 /usr/etc 디렉토리에는 아무런 파일도 들어가 있지 않고 그냥 디렉토리 이름만 하나 만들어져 있지만 /usr 디렉토리에서도 / 디렉토리와 동일한 디렉토리 명명법이 반복되고 있음을 볼 수 있다. 이것은 /usr/local 과 /home/mewmew 홈디렉토리에서도 그대로 반복된다.


$ cd /usr/local
$ ls -F
Zend/      games/                 lib/      mysql-old/     src/
apache/    gd/                    libexec/  netpbm/        var1/
bin/       giflib/                libpng/   python-2.2.1/  zend/
doc/       include/               man/      qmailscan/     zend-old/
etc/       jakarta-tomcat-4.0.3/  mrtg/     sbin/          zend-old-2/
freetype/  jpeg/                  mysql/    share/         zlib/

$ cd
$ ls -F
Mail/    doc/             public_html@  swarm_for_fedora_core_1/
backup/  fish.txt         share/        tel.txt
bin/     hangul_fonts/    src/          temp/
dkdk     man/             storage/      www/


직접 손으로 소프트웨어 패키지 소스코드를 컴파일 해 보면 이러한 명명법이 상당히 쓸모가 있음을 알 수 있다. 일반적으로 ./configure; make; make install로 설치가 되는 소스 패키지의 경우 ./configure 스크립트에서 디폴트로 소스 패키지가 설치되는 디렉토리 (PREFIX)가 /usr/local이다. 이것을 필요에 따라 /usr이라든지 혹은 사용자 홈 디렉토리를 가리키는 $HOME 환경 변수로 설정해 줄 수도 있는 것이다.


$ ./configure --prefix=/usr
$ ./configure --prefix=${HOME}


다음의 경우 나중에 컴파일이 끝나고 make install을 수행하면 보통 ${HOME}/bin, ${HOME}/etc, 필요에 따라서 ${HOME}/lib나 매뉴얼 페이지가 들어가는 ${HOME}/man 까지 설치되는 경우도 있다. 특히, 관리자 권한을 얻기 힘든 일반 웹 호스팅 서비스를 이용할 때 이렇게 내 홈 디렉토리 안에 손수 필요한 패키지를 컴파일해 쓰는 경우가 흔한데 이럴 경우에는 앞의 PATH 환경 변수에 ${HOME}/bin 엔트리도 하나 넣어 주면 더욱 편리할 것이다.

그리고 리눅스 배포판에서 rpm이나 deb 같은 패키지 관리 시스템이 발전함에 따라 /usr 아래의 /usr/bin, /usr/lib와 같은 디렉토리는 직접 시스템 관리자가 필요한 소프트웨어를 컴파일해서 집어넣는 공간이라기보다는 패키지 관리자로 관리하는 공간이 되어버렸다. 실제, 손으로 컴파일하는 소프트웨어들은 /usr/local 아래쪽에 넣는 것이 나중에 시스템 유지/보수 측면에서도 편리하다. 반드시 /usr 안쪽으로 넣어야 하는 패키지들은 소스코드를 우선 컴파일해서 rpm이나 deb 패키지를 만들고 그것을 다시 설치하는 편이 의존성 유지의 측면에서 유리하다. 이제 리눅스의 전체적인 디렉토리 구조를 쉽게 넘기기 좋은 부분만 간단히 짚어보고 넘어가자.

/etc
설정파일이 들어가는 곳이다. 사용자의 입장에서는 여기서 /etc/profile.d 디렉토리를 잠깐 지켜볼 필요가 있다. /etc 아래에서 .d로 끝나는 디렉토리들은 마치 그 디렉토리 안에 들어가 있는 파일들이 하나의 설정파일을 이루고 있는듯한 역할을 하는 경우가 많다. bash는 실행되면서 먼저 /etc/profile을 참조하는데 /etc/profile.d가 있는 경우 /etc/profile.d 안의 모든 스크립트들을 실행한다. 참고로 필자가 쓰는 시스템의 /etc/profile.d에는 다음과 같은 스크립트들이 들어있다.


$ ls -F /etc/profile.d/
colorls.csh*  lang.csh*  less.csh*  mc.csh*  which.sh*
colorls.sh*   lang.sh*   less.sh*   mc.sh*


여기서 sh로 끝나는 파일들은 bash용, csh로 끝나는 파일들은 csh용 스크립트들인데 /etc/profile에 하나로 넣을 수도 있으나 편의상 나누어 놓은 것들이다. 비슷한 예로 /etc/xinet.d를 들 수 있다. inetd는 원래 대몬(daemon)의 대몬으로 불리는 역할을 하는데 inetd에 대몬 서비스를 등록해 놓으면 외부에서 서비스 요청이 들어올 때마다 필요한 대몬 프로세스를 실행시키고 서비스가 끝나면 종료시키는 역할을 하면서 좀 더 시스템 자원을 효율적으로 쓸 수 있도록 해 준다. 이것의 확장판이 xinetd인데 xinetd는 서비스 하나하나마다 설정파일을 따로 두고 이것들을 모아서 /etc/xinetd.d에 넣어놓도록 하고 있다.


$ ls -F /etc/xinetd.d
chargen      daytime      echo      finger  rexec   rsh    talk    time
chargen-udp  daytime-udp  echo-udp  ntalk   rlogin  rsync  telnet  time-udp


참고로 구형 inetd의 설정파일인 /etc/inetd.conf를 잠깐 살펴보자. 어느 형태를 선호하는지는 사용자의 선택이겠지만 .d 로 끝나는 디렉토리 안에 설정 파일을 각각 분리해 놓는 아이디어가 있다는 점은 기억해 놓도록 하자.


$ more /etc/inetd.conf
# /etc/inetd.conf:  see inetd(8) for further informations.
#
# Internet server configuration database
#
#      
#
#:INTERNAL: Internal services
#echo           stream  tcp     nowait  root    internal
#echo           dgram   udp     wait    root    internal
#chargen        stream  tcp     nowait  root    internal
#chargen        dgram   udp     wait    root    internal
#discard                stream  tcp     nowait  root    internal
#discard                dgram   udp     wait    root    internal
#daytime                stream  tcp     nowait  root    internal
#daytime        dgram   udp     wait    root    internal
#time           stream  tcp     nowait  root    internal
#time           dgram   udp     wait    root    internal


/etc/skel 디렉토리에는 얼핏 보면 아무 파일도 없는 것처럼 보인다. 하지만 이 디렉토리에는 새로 유저 계정을 만들었을 때 그 유저의 홈 디렉토리에 디폴트로 설치될 숨겨진 설정파일들이 들어가 있다.


$ ls -al /etc/skel
합계 28
drwxr-xr-x    2 root     root         4096 2004-05-13 13:53 .
drwxr-xr-x  121 root     root         8192 2004-06-10 11:53 ..
-rw-r--r--    1 root     root          266 2002-07-09 23:20 .alias
-rw-r--r--    1 root     root          704 2004-05-09 01:43 .bash_profile
-rw-r--r--    1 root     root         1290 2004-05-09 01:43 .bashrc
-rw-r--r--    1 root     root          375 2002-07-09 23:20 .cshrc


리눅스에서 새로 유저 계정을 추가하는 방법은 여러 가지가 있다. 전통적으로 많이 쓰는 방법이 adduser, 혹은 useradd 스크립트 명령을 이용하는 방법이고 요즘은 KDE나 GNOME에서는 그래픽 유저 인터페이스 환경에서 새로운 사용자를 추가할 수도 있다. 하지만 원래 리눅스 시스템에서는 직접 손으로 사용자를 추가하는 것도 가능하다. 간단하게 새로 사용자 계정을 만들어보자. 우선, 리눅스의 사용자는 숫자로 된 사용자 ID와 그룹 ID가 필요하다. 이것이 uid와 gid인데 su 명령 등을 이용해서 수퍼유저가 된 다음 한번 /etc/passwd 파일을 보자.


root:x:0:0:sysadmin,,,:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/bin/sh
bin:x:2:2:bin:/bin:/bin/sh
sys:x:3:3:sys:/dev:/bin/sh
sync:x:4:65534:sync:/bin:/bin/sync
games:x:5:60:games:/usr/games:/bin/sh
man:x:6:12:man:/var/cache/man:/bin/sh
lp:x:7:7:lp:/var/spool/lpd:/bin/sh
mail:x:8:8:mail:/var/mail:/bin/sh
.
...생략...
.
foobar:x:1000:1000:Foobar the great one,,,:/home/foobar:/bin/bash
doobar:x:1001:1001:Doobar the beautiful one,,,:/home/doobar:/bin/bash
langel:x:1002:1002:,,,:/home/langel:/bin/bash

여기에 새로 hoover라는 사용자를 추가하려면 /etc/passwd 파일을 적당히 비슷하게 고쳐주면 된다. 새로 uid 1003과 gid 1003를 langel에 추가해주자. 패스워드 필드에는 *를 넣어 아직 로그인을 못하는 계정임을 명시해 주자.

hoover:*:1003:1003:,,,:/home/hoover:/bin/bash

마찬가지로 /etc/group 파일도 hoover 그룹을 추가해 주자. 어떤 시스템은 사용자의 그룹을 새로 만들지 않고 그냥 user 그룹에 추가하는 경우도 있다.

root:x:0:
daemon:x:1:
bin:x:2:
sys:x:3:
adm:x:4:
tty:x:5:
.

... 생략 ...
.

foobar:x:1000
doobar:x:1001
langel:x:1002
hoover:x:1003


이렇게 한 다음 mkdir 명령으로 /home/hoover 디렉토리를 만들고 /etc/skel 디렉토리에서 기본 설정파일을 cp 명령으로 복사해준다. 그리고 chown -R hoover /home/hoover 명령과 chgrp -R hoover /home/hoover 명령으로 홈 디렉토리와 그 아래 파일들의 소유권을 명확하게 해 준 다음 마지막으로 passwd hoover 명령으로 새로운 hoover 사용자의 비밀번호를 설정해주면 직접 손으로 새 사용자 등록이 끝나게 된다. useradd와 같은 명령은 이 과정을 간단하게 자동화 시켜놓은 셈인 것이다.

여기서도 리눅스의 심플함의 매력이 묻어나는데 오래전에 필자는 솔라리스 워크스테이션을 처음 쓰면서 솔라리스에는 아예 리눅스에서 볼 수 있었던 대화식 useradd 명령어조차 없던 것을 보고 놀랬던 기억이 있다. 그래도 솔라리스에도 이런 useradd와 같은 사용자 생성 스크립트가 하나 기본으로 제공되어 있으면 좋지 않을까 했지만 오히려 이런 스크립트가 없는 것이 더 나을 수도 있다는 생각을 곧 하게 되었다. 사용자가 아닌 시스템 관리자의 입장에서는 사용자 계정 발급과 말소는 주요한 업무 중의 하나일 텐데 사용자 계정 생성과 삭제를 특정 명령어에 묶어 놓으면 좀 더 유연한 시스템을 만들기가 힘들다.

한 예로, 갑자기 전산 실습을 위해 1000명의 ID를 새로 발급해야 한다든가, 혹은 하루에도 수백 명씩 ID가 생성되고 없어지는 시스템을 유지하려면 오히려 이런 심플함이 시스템 관리자의 입장에서 좀 더 편리하고 효율적인 사용자 계정 관리 시스템을 만드는데 도움이 된다. 반복 작업을 스크립트로 처리할 수도 있고 웹 인터페이스와 연동을 할 수도 있기 때문이다. 어쨌든, 우리가 관심이 있는 시스템은 이런 대규모 시스템은 아니니 심플함이 시스템이나 레이어 간에 인터페이스를 구성하는데 도움이 될 수 있다는 점만 체크하고 넘어가도록 하자.

/dev, /proc
유닉스의 특징 중의 하나는 모든 것을 파일로 처리한다는 점이다. 이것이 유닉스 상의 프린팅과 같이 어떨 때는 자유도가 지나친 까닭에 오히려 복잡한 상황을 초래하기도 하지만 /dev 처럼 디바이스도 파일로, /proc 처럼 프로세스와 시스템 정보도 파일로 처리하는 아이디어는 한번쯤 짚고 넘어갈 필요가 있다. 여기서는 /proc 파일시스템을 위주로 살펴보자.

/proc 파일시스템에 들어가 보면 맨 처음 보이는 것이 수백MB 정도의 크기를 갖는 kcore 파일이다. 유심히 보면, kcore 파일의 사이즈가 자신의 시스템 메모리 크기와 일치한다는 사실을 알 수 있다. 이렇게, /proc 파일시스템은 가상의 파일시스템으로서 현재의 시스템 정보를 파일의 형식을 빌어 저장해 놓고 있는데 여기서 몇 가지 중요한 파일들은 다음과 같다. 대부분은 more 명령이나 vi와 같은 에디터로 직접 열어볼 수 있는 텍스트 형식의 파일들이다.


하드웨어 정보
/proc/cpuinfo - 시스템 CPU에 관한 정보가 들어가 있다.
/proc/interrupts - 시스템 하드웨어 인터럽트 사용 정보
/proc/dma - dma 정보
/proc/devices - 커널에 설정되어 있는 디바이스 정보

현재 시스템 구동 정보
/proc/net - 네트워크 정보
/proc/uptime - 시스템이 부팅 후 지금까지 계속해서 켜져 있는 시간
/proc/stat - 여러 가지 시스템 정보
/proc/loadavg - 시스템에 부하가 걸린 정도
/proc/meminfo - 시스템 메모리 사용 정보
/proc/modules - 현재 로딩되어 있는 모듈 정보


/proc 디렉토리 아래에 숫자로 만들어진 디렉토리는 모두 실행중인 프로세스이다. 이 디렉토리 안에 들어가면 그 프로세스에 관한 여러가지 정보를 알 수 있다. 참고로 윈도우의 디바이스 드라이버와 리눅스의 모듈을 혼동하는 경우가 간혹 있는 것 같아 설명을 몇 자 덧붙이면, 리눅스의 모듈은 윈도우의 디바이스 드라이버와 같다고 생각해도 되지만 리눅스 모듈은 단어 그대로 modular하다는 장점이 있다. 리눅스 모듈을 이용하면 시스템을 재부팅하지 않고도 디바이스 드라이버를 로딩(loading)/언로딩(unloading) 할 수 있는 것이다. 이런 이유로 리눅스에서는 자주 쓰이지 않는 하드웨어는 필요할 때만 모듈을 로딩하고 그 하드웨어를 사용한 다음 다시 모듈을 언로드해서 시스템 자원을 아껴 쓸 수 있다.


$ cd /proc/862
$ ls
cmdline  cwd  environ  exe  fd  maps  mem  mounts  root  stat  statm  status
$ ls -al
합계 0
dr-xr-xr-x 3 mp3 mp3 0 2004-06-17 16:55 . 
dr-xr-xr-x 112 root root 0 2004-06-10 20:52 ..
-r--r--r-- 1 mp3 mp3 0 2004-06-17 16:56 cmdline
lrwxrwxrwx 1 mp3 mp3 0 2004-06-17 16:56 cwd -> /proc/862
-r-------- 1 mp3 mp3 0 2004-06-17 16:56 environ
lrwxrwxrwx 1 mp3 mp3 0 2004-06-17 16:56 exe -> /bin/bash
dr-x------ 2 mp3 mp3 0 2004-06-17 16:56 fd
-r--r--r-- 1 mp3 mp3 0 2004-06-17 16:56 maps
-rw------- 1 mp3 mp3 0 2004-06-17 16:56 mem
-r--r--r-- 1 mp3 mp3 0 2004-06-17 16:56 mounts
lrwxrwxrwx 1 mp3 mp3 0 2004-06-17 16:56 root -> /
-r--r--r-- 1 mp3 mp3 0 2004-06-17 16:56 stat
-r--r--r-- 1 mp3 mp3 0 2004-06-17 16:56 statm
-r--r--r-- 1 mp3 mp3 0 2004-06-17 16:56 status

$ more cmdline
-bash
$ more environ
USER=foobar=foobarHOME=/home/foobar/resources/CD-RipPATH=/usr/local/bin:/bin:/u
sr/bin:/usr/X11R6/binMAIL=/var/mail/mp3SHELL=/bin/bashSSH_CLIENT=
220.73.160.119
39288 22SSH_CONNECTION=220.73.160.119 39288 147.66.166.66 22SSH_TTY=/dev/pts/4TE
RM=xtermLANG=ko_KR.EUC-KR
$ more stat
862 (bash) S 861 862 862 34820 937 0 1253 1913 387 8166 7 1 15 37 15 0 0 0 62030
688 4096000 440 4294967295 134512640 135116540 3221224976 3221224296 1074886392
0 65536 3686404 1266761467 3222391889 0 0 17 0


이러한 /proc 파일 시스템의 정보를 좀 더 사용자가 편하게 볼 수 있도록 해 주는 것들이 ps, top, uname, uptime과 같은 명령어들이다.

/usr
/usr 디렉토리 아랫쪽에는 다양한 서브디렉토리가 존재한다. X윈도우가 /usr/X11R6 디렉토리 아래쪽으로 위치하고 있으며 /usr/share, /usr/doc과 같은 디렉토리들도 존재한다. 여기서 눈여겨 볼 것은 /usr/lib 디렉토리 아랫쪽의 라이브러리들과 /usr/include 아래쪽의 헤더 파일들이다. 이곳에 들어가 있는 라이브러리와 헤더파일들은 컴파일러가 명시적으로 디렉토리를 지정하지 않아도 알아서 찾아 준다.

다음 호에서는 개발자에게 필요한 도구를
이번 호에서는 유닉스의 보안 모델의 기초가 되는 파일 퍼미션과 소유권에 관련한 내용, 그리고 리눅스 시스템을 접했을 때 쉽게 친숙해지지 않는 개념과 명시적으로 잘 기술되어 있지 않는 전통적인 관습을 명령어와 함께 알아보았다. 다음 호에서는 이번 호 기사에서 조금 더 보충할 필요가 있는 유닉스 명령어를 잠깐 추가하고 개발자들에게 필수적인 에디터와 다른 보조 도구들의 사용법을 다루어 보도록 하겠다. @

man 명령어 사용하기  
유닉스에 적응하면서 겪게 되는 낯설음 중의 하나가 man 명령어의 생소함이다. 윈도우의 도움말과는 달리 man 명령어는 상당히 내용이 어렵고 형식이 지나치게 딱딱한 면이 없지 않고, 웹과 같은 하이퍼텍스트가 이미 일반화된 지금에는 그다지 매력적으로 보이지 않을 수도 있지만 유닉스 매뉴얼 페이지의 강점은 그 간결함과 통일된 형식이다. 익숙해질수록 편리해지는 유닉스와 마찬가지로 매뉴얼 페이지도 익숙해지는 노력을 들일만한 가치가 있다.

man 명령의 형식은 man 명령어 다음에 궁금한 사항을 적어주면 되는 간단한 구조를 갖고 있다. 하지만 매뉴얼 페이지는 원래 서가에 있는 여러 권의 매뉴얼의 구조를 갖고 있다. 매뉴얼 페이지는 다음의 8가지 파트로 구성되어 있다.

Commands available to users
Unix and C system calls
C library routines for C programs
Special file names
File formats and conventions for files used in Unix
Games
Word processing packages
System administration commands and procedures

여기서 눈여겨 볼 부분은 섹션 3이다. 예를 들어, 웬만한 C 함수들은 이곳에 모두 정리가 되어 있다. man 명령어는 단순히 유닉스 명령어에 관한 설명만 다루는 것이 아니고 매뉴얼화 할 필요가 있는 항목들을 모두 담고 있는 것이다. 그런데 여기서 하나 조심해야 할 것이, man 다음의 항목이 여러 섹션에 걸쳐 있는 경우이다. 예를 들어 printf 함수에 대한 매뉴얼 페이지를 보고 싶어서 man printf를 하면 다음과 같이 섹션 1의 유닉스 명령어에 관한 매뉴얼 페이지가 디폴트로 출력된다.

PRINTF(1)                      FSF                      PRINTF(1)

NAME
       printf - format and print data

SYNOPSIS
       printf FORMAT [ARGUMENT]...
       printf OPTION

DESCRIPTION
       Print ARGUMENT(s) according to FORMAT.

       --help display this help and exit

       --version
              output version information and exit

(이하 생략)

그러나 우리가 찾고 싶은 printf 함수의 매뉴얼 페이지는 프로그래밍에 관련된 섹션 3에 저장되어 있다. 섹션 3을 명시적으로 지정해 주려면 다음과 같이 섹션 넘버를 적어주면 된다.

$ man 3 printf

그러면 우리가 원하는 printf 함수에 대한 매뉴얼 페이지가 출력된다.

PRINTF(3)           Linux Programmer's Manual           PRINTF(3)

NAME
       printf,  fprintf,  sprintf,  snprintf,  vprintf, vfprintf,
       vsprintf, vsnprintf - formatted output conversion

SYNOPSIS
       #include

       int printf(const char *format, ...);
       int fprintf(FILE *stream, const char *format, ...);
       int sprintf(char *str, const char *format, ...);
       int snprintf(char *str, size_t size, const  char  *format,
       ...);

이렇게 여러 섹션에 이름이 같은 항목이 존재하는 경우를 쉽게 찾기 위해서는 man -k 명령을 사용하면 좋다. (man -k 대신에 apropos명령어를 써도 된다) printf의 경우 man -k 명령을 쓰면 다음과 같은 결과를 얻을 수 있다.

$ man -k printf

printf               (1)  - format and print data
printf               (3)  - formatted output conversion

시스템 설정 파일의 문법도 매뉴얼 페이지에서 다루는 항목 중의 하나이다. 예를 들어 /etc/passwd의 문법을 참조하고 싶을 때는 다음과 같이 원하는 매뉴얼 페이지를 찾을 수 있다. 참고로 섹션 5는 시스템 설정파일을 다룬다.

$ man -k passwd

passwd               (1)  - update a user's authentication tokens(s)
passwd               (5)  - password file

$ man 5 passwd
PASSWD(5)                  File formats                 PASSWD(5)

NAME
       passwd - password file

DESCRIPTION
       Passwd  is  a  text file, that contains a list of the sys-
       tem's accounts, giving for each account some useful infor-
       mation like user ID, group ID, home directory, shell, etc.
       Often it also contains the encrypted  passwords  for  each
       account.   It  should  have  general read permission (many
       utilities, like ls(1) use it  to  map  user  IDs  to  user
       names), but write access only for the superuser.

매뉴얼 페이지를 계속 살펴보면 알겠지만, 리눅스 시스템의 매뉴얼 페이지는 단순히 리눅스 운영체제와 프로그래밍에 관련된 부분 이외에 Perl 레퍼런스 가이드(perl reference guide)와 같은 얼핏 생각하기에는 따로 다운받아야 할 것 같은 정보도 방대한 양이 이미 들어가 있음을 알 수 있다.

참고로 GNU 시스템에서는 man보다는 info의 사용을 권장하고 있다. info는 man과 비슷한 구조를 갖고 있지만 하이퍼텍스트 기능이 들어가 있어 브라우징이 좀 더 편리하다. 여기서는 info는 다루지 않기로 한다.

지금은 많이 잊혀져가는 테크닉이지만 고전적인 유닉스 방식으로 출력용 깔끔한 메뉴얼 페이지를 만들어 보자. man 명령어에는 -t 옵션을 주면 매뉴얼 페이지를 포스트스크립트 형식으로 출력을 해 준다. 한번 고스트스크립트를 이용해서 어떤 모습으로 출력이 될 것인지 프리뷰를 해 보자. gv의 - 옵션은 포스트스크립트 파일 입력을 표준 출력에서 받겠다는 옵션이다.

$ man -t 5 passwd | gv -

이것을 pdf 파일로 만들려면 다음과 같은 간단한 명령어로 해결이 될 것이다. 심플한 명령어 한두 가지로 복잡한 기능까지 처리해 내는 유닉스의 미학이 드러나는 부분이다.

$ man -t 5 passwd | ps2pdf > passwd_manual.pdf


* 이 기사는 ZDNet Korea의 자매지인 마이크로소프트웨어에 게재된 내용입니다

 

손정우

2004/11/08