리눅스 시스템 기초 · 02. 파일 · 권한 · 사용자 관리 — 이 영역 글의 "N편"은 제목 앞 번호(영역 내 번호)다. 전체 250편 구성은 통합 로드맵에서 확인할 수 있다.
파일 · 권한 · 사용자 관리 01 / 50 · Part 1. Linux 파일 관리 기초
실습 환경: Rocky Linux 9.8 · Ubuntu 24.04.5 (Docker 격리 컨테이너, 테스트 계정analyst)
기초편 연계: 「리눅스 시스템 기초」 11. Linux 파일과 디렉터리 관리 · 14. inode와 파일 시스템
「파일 · 권한 · 사용자 관리」 시리즈의 첫 글이다. 이 시리즈는 Linux 파일 시스템, 파일 권한, 소유권, 사용자·그룹, sudo, ACL, 특수 권한을 단계적으로 다루고, 마지막에는 보안관제(SOC) 관점에서 권한 변경과 권한 상승 흔적을 분석 하는 실습으로 마무리한다.
「리눅스 시스템 기초」 시리즈가 Linux 전체를 넓게 훑었다면, 이 시리즈는 그중 파일 → inode → 권한 → 소유권 → 사용자 → 그룹 → sudo → ACL → 권한 상승 → 보안관제 한 축만 깊게 파고든다.
첫 글의 질문은 단순하다.
파일과 디렉터리는 무엇이 다르고, Linux는 파일을 실제로 어떻게 저장하는가?
이 질문에 정확히 답할 수 있어야 이후의 권한·소유권·타임스탬프·무결성 분석이 "외운 명령어"가 아니라 "구조에서 나오는 결론"이 된다.
Linux에서 일반 문서, 디렉터리, 장치(/dev/sda), 프로세스 정보(/proc/1/status)는 모두 같은 인터페이스(open · read · write · close) 로 다룬다. 이것이 "모든 것은 파일"이라는 말의 뜻이다. 모든 것이 디스크에 저장된 문서라는 뜻이 아니다.
| 구성 요소 | 저장 위치 | 담고 있는 것 | 확인 명령 |
|---|---|---|---|
| 이름 | 상위 디렉터리의 엔트리 | 사람이 부르는 이름 → inode 번호 | ls |
| inode | inode 테이블 | 종류, 권한, 소유자, 크기, 링크 수, 타임스탬프, 블록 위치 | ls -li, stat |
| 데이터 블록 | 데이터 영역 | 실제 내용 | cat, sha256sum |
핵심은 inode에 파일 이름이 없다 는 점이다. 이름은 디렉터리 안에만 있다.
디렉터리도 inode와 데이터 블록을 가진 파일이다. 다만 데이터 블록에 문서 내용 대신 "이름 → inode 번호" 목록 이 들어 있다.
| 구분 | 일반 파일 | 디렉터리 |
|---|---|---|
ls -l 첫 글자 | - | d |
| 데이터 블록 내용 | 사용자 데이터 | 이름 → inode 번호 목록 |
cat으로 읽기 | 가능 | Is a directory 오류 |
| 기본 링크 수 | 1 | 2 (자기 이름 + 안의 .) |
r 권한의 의미 | 내용 읽기 | 이름 목록 보기 |

cat /data/lab01/report.txt를 실행하면 커널 내부에서 다음 순서로 처리된다.
/ → data → lab01 → report.txt 순서로 쪼갠다.report.txt → inode 12)이 흐름에서 보안상 중요한 결론이 세 가지 나온다.
w 권한에 달려 있다 (22편)..과 ..도 실제 엔트리다. 그래서 새 디렉터리의 링크 수는 2이고, 하위 디렉터리가 생길 때마다 상위 디렉터리의 링크 수가 1씩 증가한다.모든 실습은 격리된 Docker 컨테이너(Rocky Linux 9.8)에서 테스트 계정
analyst로 실행했다./data는 실습용으로 따로 만든 ext4 볼륨 이다. 운영 서버에서는 반드시 테스트 디렉터리에서만 진행한다.
cd /data && mkdir lab01 && cd lab01
# 1) 일반 파일과 디렉터리 만들기
echo "hello soc" > report.txt
mkdir logs
# 2) inode 번호와 함께 목록 보기
ls -li
# 3) 종류·inode·크기·링크 수 비교
stat -c '%n | %F | inode=%i | size=%s | links=%h' report.txt logs
# 4) 디렉터리를 파일처럼 읽어 보기
cat logs
# 5) 디렉터리 안의 . 과 .. 확인
ls -lia logs
ls -id . logs/..
| 옵션 | 의미 |
|---|---|
ls -i | inode 번호 표시 |
ls -a | .으로 시작하는 항목까지 표시 |
ls -d | 디렉터리 내용이 아니라 디렉터리 자체 정보 표시 |
stat -c | 원하는 필드만 형식 지정 출력 (%i inode, %F 종류, %h 링크 수) |

텍스트 원본(실제 출력):
[analyst@rocky9-lab ~]$ cd /data && mkdir lab01 && cd lab01
[analyst@rocky9-lab lab01]$ echo "hello soc" > report.txt
[analyst@rocky9-lab lab01]$ mkdir logs
[analyst@rocky9-lab lab01]$ ls -li
total 8
13 drwxr-xr-x 2 analyst analyst 4096 Sep 24 10:15 logs
12 -rw-r--r-- 1 analyst analyst 10 Sep 24 10:15 report.txt
[analyst@rocky9-lab lab01]$ stat -c '%n | %F | inode=%i | size=%s | links=%h' report.txt logs
report.txt | regular file | inode=12 | size=10 | links=1
logs | directory | inode=13 | size=4096 | links=2
[analyst@rocky9-lab lab01]$ cat logs
cat: logs: Is a directory
[analyst@rocky9-lab lab01]$ ls -lia logs
total 8
13 drwxr-xr-x 2 analyst analyst 4096 Sep 24 10:15 .
11 drwxr-xr-x 3 analyst analyst 4096 Sep 24 10:15 ..
[analyst@rocky9-lab lab01]$ ls -id . logs/..
11 .
11 logs/..
| 출력 | 해석 |
|---|---|
13 drwxr-xr-x 2 ... logs | inode 13, 종류 d(디렉터리), 링크 수 2 |
12 -rw-r--r-- 1 ... report.txt | inode 12, 종류 -(일반 파일), 링크 수 1 |
report.txt \| regular file \| size=10 | hello soc 9바이트 + 줄바꿈 1바이트 = 10바이트 |
logs \| directory \| size=4096 | 디렉터리 크기는 내용물 합계가 아니라 엔트리 목록이 차지하는 블록 크기 (ext4 기본 4KiB) |
cat: logs: Is a directory | 디렉터리는 일반 read로 내용을 읽을 수 없다. 목록은 ls(getdents 시스템 콜)로 읽는다 |
ls -lia logs의 . = 13 | logs/.는 logs 자신(inode 13)을 가리키는 엔트리 |
ls -lia logs의 .. = 11 | logs/..는 상위 lab01(inode 11)을 가리키는 엔트리 |
ls -id . logs/.. 모두 11 | 서로 다른 경로가 같은 inode 를 가리킨다 → 이름과 실체가 분리되어 있다는 증거 |
logs의 링크 수가 2인 이유도 여기서 설명된다. lab01 안의 logs라는 이름 1개 + logs 안의 . 1개 = 2다.
파일 구조를 이해하면 다음 공격 기법이 "왜 가능한지" 설명할 수 있다.
| 공격 기법 | 이용하는 구조 | 관련 편 |
|---|---|---|
파일명 위장 (report.pdf.sh, 공백·유니코드 이름) | 이름은 디렉터리 엔트리의 문자열일 뿐 | 17·19편 |
숨김 파일 (.hidden, ...) | .으로 시작하는 이름은 ls 기본 출력에서 제외 | 19편 |
| 타임스탬프 조작 (timestomping) | inode의 atime·mtime은 사용자가 바꿀 수 있음 | 8·15편 |
| 삭제 후에도 남는 파일 | 이름이 지워져도 열린 파일의 inode는 유지 | 2·10편 |
| 파일 교체 (바꿔치기) | 디렉터리 w 권한만 있으면 이름을 다른 inode로 연결 가능 | 22편 |
반대로 과장하지 말아야 할 점도 있다. 파일이 ls에 안 보인다고 해서 모두 악성은 아니다. .bashrc, .ssh처럼 정상 설정 파일도 대부분 숨김 파일 이다. 판단은 항상 위치·소유자·생성 시점·내용을 함께 본다.

SOC 분석가가 파일 하나를 볼 때 던지는 질문은 구조와 1:1로 대응한다.
| 관점 | 질문 | 근거 데이터 |
|---|---|---|
| Evidence | 이 파일은 언제 생겼고 누가 소유하는가? | inode의 소유자·타임스탬프 (stat) |
| IOC | 알려진 악성 파일과 같은 파일인가? | 데이터 블록의 해시 (sha256sum) |
| Detection | 어떤 로그·이벤트로 생성을 탐지할 수 있는가? | auditd 파일 감시, FIM(파일 무결성 모니터링), EDR |
| Investigation | 어떤 계정이 어떤 경로로 만들었는가? | 계정 로그(/var/log/secure, auth.log) + 프로세스 정보 |
| Response | 삭제 전에 무엇을 보존해야 하는가? | stat 결과, 해시, cp -a로 원본 속성 보존 복사 (9편) |
주의할 점: 파일 생성 자체는 기본 설정에서 로그로 남지 않는다. 일반 syslog는 파일 생성·수정을 기록하지 않는다. auditd 규칙이나 FIM 도구를 미리 설정해야 파일 이벤트가 SIEM으로 들어온다 (49편).
| 실수 | 결과 | 예방 |
|---|---|---|
| 디렉터리 크기 4096을 "안의 파일 합계"로 오해 | 용량 분석 오류 | 실제 사용량은 du -sh |
| 이름을 바꾸면 흔적이 사라진다고 생각 | inode·타임스탬프는 그대로 남아 있음 | 분석 시 ls -li로 inode 기준 추적 |
실습을 /etc, /var에서 진행 | 운영 설정 손상 | 전용 테스트 디렉터리(~/lab, /data/lab) 사용 |
cat으로 디렉터리 내용을 보려 함 | Is a directory | 디렉터리는 ls -la |
| 파일 = 이름이라고 생각 | 하드 링크·삭제된 열린 파일을 설명하지 못함 | "이름 → inode → 데이터" 3단 구조로 이해 |
[ ] 파일이 이름 · inode · 데이터 블록으로 나뉜다는 것을 설명할 수 있다
[ ] ls -li 로 inode 번호를 확인했다
[ ] stat -c 로 종류 · 크기 · 링크 수를 비교했다
[ ] 디렉터리의 . 과 .. 이 실제 엔트리임을 inode 번호로 확인했다
[ ] 디렉터리 링크 수가 2인 이유를 설명할 수 있다
[ ] 파일 생성이 기본 로그에 남지 않는다는 점을 확인했다
자기 이름 + 내부의 . 때문이다.다음 글 「02. Linux 파일 시스템과 파일의 관계」 에서는 이 구조를 한 단계 아래에서 본다. 파일 시스템이 inode와 데이터 블록을 어떻게 관리하는지, 그리고 프로세스가 파일을 열 때 생기는 파일 디스크립터 가 무엇인지 다룬다. 특히 삭제했는데 디스크 용량이 줄지 않는 이유 를 실제로 재현한다.