Skip to content

File System & Permissions — Hiểu Hệ Thống Tập Tin Linux Foundation

Bạn vừa join vào đội SRE của một công ty fintech. Ngày đầu tiên, senior giao cho bạn debug một incident: app production không ghi được log — health check pass, nhưng hệ thống logging hoàn toàn im lặng. Bạn SSH vào server, chạy ls -la /var/log/myapp/ và thấy owner là root:root, trong khi app chạy bằng user deploy. Vấn đề rõ ràng — nhưng bạn cần hiểu tại sao nó xảy ra và làm sao ngăn nó tái diễn.

Nguyên tắc xuyên suốt: Trên Linux, mọi thứ đều là file — config, process, device, socket. Hiểu filesystem và permission là nền tảng để vận hành bất kỳ hệ thống nào.


🎯 Mục tiêu

  • Nắm vững cấu trúc FHS (Filesystem Hierarchy Standard) — biết file nào nằm ở đâu trên server
  • Hiểu mô hình permission triad (user/group/other) và ký hiệu octal
  • Sử dụng thành thạo chmod, chown, ls -la, find -perm trong thao tác hàng ngày
  • Hiểu sticky bit, SetUID, SetGID — và khi nào chúng nguy hiểm
  • Phân tích và xử lý production incident liên quan đến permission

1. Linux File System Hierarchy — Bản Đồ Máy Chủ

Khi SSH vào một server Linux lần đầu, bạn cần bản đồ. FHS (Filesystem Hierarchy Standard) chính là bản đồ đó — nó quy định mọi thứ nằm ở đâu trên hệ thống.

🗺️ Mental Model — Cấu trúc thư mục gốc

/
├── etc/        ← Config files (nginx.conf, systemd units, ssh config)
├── var/
│   ├── log/    ← Application logs
│   └── lib/    ← Application state data (databases, caches)
├── home/       ← User directories
├── tmp/        ← Temporary files (cleared on reboot)
├── proc/       ← Virtual filesystem — live process info
├── usr/
│   ├── bin/    ← User binaries (python, node, go)
│   └── lib/    ← Shared libraries
├── opt/        ← Third-party software
└── srv/        ← Web server data

📌 Ghi nhớ nhanh — "File nào nằm ở đâu?"

Thư mụcChứa gìVí dụ thực tế
/etc/Config hệ thống & ứng dụng/etc/nginx/nginx.conf, /etc/ssh/sshd_config
/var/log/Log files/var/log/nginx/access.log, /var/log/syslog
/var/lib/State data của ứng dụng/var/lib/postgresql/, /var/lib/docker/
/home/Home directory của user/home/deploy/.ssh/authorized_keys
/tmp/File tạm (bị xóa khi reboot)Upload trung gian, build artifacts tạm
/proc/Virtual filesystem — process info live/proc/cpuinfo, /proc/12345/status
/opt/Phần mềm bên thứ 3/opt/datadog-agent/, /opt/elasticsearch/
/usr/bin/Binary của user-space/usr/bin/python3, /usr/bin/node

💡 "Everything is a file"

Đây là triết lý cốt lõi của Linux. Config → file. Process info → file trong /proc/. Device → file trong /dev/. Socket → file. Khi bạn nắm được nguyên tắc này, bạn có thể debug hầu hết mọi thứ chỉ bằng cat, ls, và grep.

bash
# Xem CPU info — không cần cài tool
cat /proc/cpuinfo | head -20

# Xem memory usage — cũng là file
cat /proc/meminfo | grep MemAvailable

# Xem process đang mở những file nào
ls -la /proc/12345/fd/

⚠️ Cạm bẫy

  1. Tìm config bằng find / -name myconfig — quét toàn bộ filesystem thay vì biết check /etc/ trước
  2. Lưu application data vào /tmp/ — mất hết khi server reboot (đã có team mất database cache vì lỗi này)
  3. Không biết /proc/ tồn tại — cài thêm monitoring tool chỉ để xem PID đang dùng bao nhiêu memory, trong khi cat /proc/<pid>/status cho ngay kết quả
  4. Nhầm /usr/bin/ với /usr/local/bin/ — binary cài qua package manager nằm ở /usr/bin/, binary bạn compile từ source nằm ở /usr/local/bin/
  5. Để log ở home directory thay vì /var/log/ — khi disk đầy, /var/ có thể mount riêng partition, nhưng /home/deploy/logs/ thì chia sẻ partition với cả hệ thống

2. Permission Model — Ai Được Làm Gì?

Mỗi file và directory trên Linux đều có 3 lớp quyền — owner, group, other. Đây là hàng rào bảo mật cơ bản nhất trên mọi server.

🔐 Đọc hiểu output của ls -la

-rwxr-xr--  1  deploy  web-team  4096  Mar 8  config.yaml
│├─┤├─┤├─┤     │       │
│ │   │  │     owner   group
│ │   │  └── other: r-- (read only)
│ │   └───── group: r-x (read + execute)
│ └───────── owner: rwx (read + write + execute)
└─────────── type: - (regular file), d (directory)

Phân tích:

  • User deploy (owner) → có thể đọc, ghi, chạy file này
  • Group web-team → có thể đọc và chạy, nhưng không ghi được
  • Tất cả user khác → chỉ có thể đọc

🔢 Octal Notation — Ngôn ngữ số của permission

Mỗi quyền tương ứng một giá trị:

r = 4    w = 2    x = 1

rwx = 4+2+1 = 7    r-x = 4+0+1 = 5    r-- = 4+0+0 = 4    --- = 0

Các permission phổ biến trên production:

OctalSymbolicDùng choVí dụ
755rwxr-xr-xScripts, binaries, thư mục web/var/www/myapp/
644rw-r--r--Config files, HTML, static assetsnginx.conf, index.html
600rw-------SSH keys, secrets, credentials~/.ssh/id_ed25519, .env
700rwx------Private directories~/.ssh/

🛠️ Terminal Workflow — Thao tác permission hàng ngày

bash
# ========================================
# 📋 Kiểm tra permissions hiện tại
# ========================================

# Xem chi tiết permissions của thư mục
ls -la /var/log/myapp/
# drwxr-xr-x 2 root root 4096 Mar 8 14:00 .
# -rw-r--r-- 1 root root  512 Mar 8 14:00 app.log

# Xem permissions dạng octal (với stat)
stat -c '%a %U:%G %n' /var/log/myapp/*
# 644 root:root /var/log/myapp/app.log

# ========================================
# 👤 Thay đổi ownership
# ========================================

# Chuyển ownership cho deploy user (recursive)
sudo chown -R deploy:web-team /var/www/myapp/

# Chỉ đổi group (giữ nguyên owner)
sudo chown :web-team /var/www/myapp/config.yaml

# ========================================
# 🔒 Thiết lập permissions đúng chuẩn
# ========================================

# Thư mục web app — owner full quyền, group & other đọc + execute
chmod 755 /var/www/myapp/

# Config file — owner đọc/ghi, group & other chỉ đọc
chmod 644 /var/www/myapp/config.yaml

# SSH private key — CHỈ owner đọc/ghi, không ai khác
chmod 600 /home/deploy/.ssh/id_ed25519

# Thư mục .ssh — CHỈ owner truy cập
chmod 700 /home/deploy/.ssh/

# ========================================
# 🔍 Audit — Tìm files có permission sai
# ========================================

# Tìm files mà ANYONE có thể ghi (nguy hiểm!)
find /var/www/ -type f -perm /o=w -ls

# Tìm files 777 — red flag trên production
find /var/www/ -type f -perm 777 -ls

# Tìm files không thuộc deploy user
find /var/www/myapp/ ! -user deploy -ls

💡 Mẹo nhớ Octal nhanh

Nghĩ theo binary: rwx = 111 = 7, r-x = 101 = 5, r-- = 100 = 4.

Hoặc đơn giản hơn — 3 con số phổ biến nhất:

  • 7 = full quyền (owner thường cần)
  • 5 = đọc + execute (group/other cho thư mục & script)
  • 4 = chỉ đọc (group/other cho file)
  • 0 = không có quyền gì

Vậy 754 = owner full, group đọc+execute, other chỉ đọc.


3. Special Permissions — Sticky Bit, SetUID, SetGID

Ngoài rwx, Linux còn có 3 permission đặc biệt mà bạn sẽ gặp trên production server.

📌 Sticky Bit (+t)

Tác dụng: Trong thư mục có sticky bit, chỉ owner của file mới có quyền xóa file đó — dù thư mục cho phép write.

bash
# Ví dụ kinh điển: /tmp/
ls -la / | grep tmp
# drwxrwxrwt  12 root root 4096 Mar 8 14:00 tmp
#          ^— chữ "t" ở cuối = sticky bit

# Tại sao cần?
# /tmp/ cho phép mọi user ghi (777), nhưng nhờ sticky bit,
# user A không thể xóa file của user B

# Thiết lập sticky bit
chmod +t /var/shared-uploads/
# Hoặc dạng octal: chmod 1755 /var/shared-uploads/
#                         ^— số 1 đầu tiên = sticky bit

📌 SetUID (u+s)

Tác dụng: Khi execute file có SetUID, process chạy với quyền của file owner, không phải quyền của người gọi.

bash
# Ví dụ kinh điển: lệnh passwd
ls -la /usr/bin/passwd
# -rwsr-xr-x 1 root root 68208 Mar 8 /usr/bin/passwd
#    ^— chữ "s" thay cho "x" ở owner = SetUID

# Tại sao cần?
# User thường chạy "passwd" để đổi password
# File /etc/shadow chỉ root đọc/ghi được
# SetUID cho phép passwd chạy với quyền root để ghi vào /etc/shadow

📌 SetGID (g+s)

Tác dụng: File mới tạo trong thư mục sẽ kế thừa group của thư mục (thay vì group của người tạo).

bash
# Ví dụ: thư mục dự án chung của team
sudo mkdir /var/projects/api-service
sudo chown :web-team /var/projects/api-service
sudo chmod g+s /var/projects/api-service

# Giờ mọi file tạo trong đây sẽ tự động thuộc group web-team
touch /var/projects/api-service/new-config.yaml
ls -la /var/projects/api-service/new-config.yaml
# -rw-r--r-- 1 deploy web-team 0 Mar 8 new-config.yaml
#                     ^^^^^^^^^— kế thừa group từ thư mục

⚠️ SetUID — Con dao hai lưỡi

Tuyệt đối không bao giờ đặt SetUID lên custom scripts hoặc binary do bạn viết. Đây là một trong những attack vector phổ biến nhất trên Linux.

bash
# ❌ NGUY HIỂM — Đừng bao giờ làm điều này
chmod u+s /opt/myapp/deploy-script.sh
# Nếu script có lỗ hổng, attacker có thể execute với quyền root!

# ✅ Thay thế an toàn — Dùng sudo với config cụ thể
# Trong /etc/sudoers.d/deploy:
# deploy ALL=(root) NOPASSWD: /opt/myapp/deploy-script.sh

Audit SetUID trên server:

bash
# Tìm TẤT CẢ files có SetUID — review danh sách này định kỳ
find / -perm -4000 -type f -ls 2>/dev/null

4. 🔥 Production Incident — "App Không Ghi Được Log"

🔴 Incident Report — Silent Logging Failure

Tình huống

Deploy team push phiên bản API mới lên production lúc 14:00. App khởi động thành công, health check endpoint trả về 200 OK. Tuy nhiên, 30 phút sau, team monitoring báo cáo: không có log mới nào xuất hiện trong hệ thống logging. Error tracking dashboard cũng trống — các lỗi đang xảy ra nhưng hoàn toàn im lặng.

Triệu chứng

  • ✅ App chạy, health check pass
  • ✅ API trả response bình thường
  • ❌ Không có log nào trong /var/log/myapp/
  • ❌ Error tracking dashboard trống
  • ❌ Không thể debug bất kỳ issue nào từ user report

Điều tra

bash
# Bước 1: Kiểm tra app có đang chạy không
$ ps aux | grep myapp
deploy  12345  0.5  2.1  myapp --config /etc/myapp/config.yaml
# ✅ App đang chạy, dưới user "deploy"

# Bước 2: Kiểm tra thư mục log
$ ls -la /var/log/myapp/
drwxr-xr-x 2 root root 4096 Mar 8 14:00 .
-rw-r--r-- 1 root root    0 Mar 8 14:00 app.log
# ❌ Owner là root:root — app chạy bằng user deploy!

# Bước 3: Xác nhận — deploy user có ghi được không?
$ sudo -u deploy touch /var/log/myapp/test.log
touch: cannot touch '/var/log/myapp/test.log': Permission denied
# ❌ Xác nhận: deploy không có quyền ghi

# Bước 4: Kiểm tra deploy script gần nhất
$ git log --oneline -5 -- deploy/
a1b2c3d  feat: add new log directory creation in deploy script
# 💡 Deploy script tạo thư mục mới, nhưng chạy bằng root...

Nguyên nhân gốc (Root Cause)

Deploy script chạy dưới quyền root (qua sudo) và tạo thư mục /var/log/myapp/ mới. Thư mục được tạo với owner mặc định là root:root. App chạy bằng user deploykhông có quyền ghi vào thư mục log → log silently fails (app không crash vì log failure được catch và ignore).

Khắc phục (Fix)

bash
# Fix ngay lập tức
sudo chown -R deploy:deploy /var/log/myapp/
sudo chmod 755 /var/log/myapp/

# Verify
ls -la /var/log/myapp/
# drwxr-xr-x 2 deploy deploy 4096 Mar 8 15:30 .
# ✅ Owner đúng rồi

# Restart app để đảm bảo log file handle mới
sudo systemctl restart myapp

# Kiểm tra log xuất hiện
tail -f /var/log/myapp/app.log
# [2024-03-08 15:31:02] INFO: Application started successfully
# ✅ Log hoạt động trở lại

Phòng ngừa (Prevention)

bash
# ✅ Deploy script phải LUÔN chown sau khi tạo directory
#!/bin/bash
# deploy.sh

APP_USER="deploy"
APP_GROUP="deploy"
LOG_DIR="/var/log/myapp"

# Tạo thư mục nếu chưa có
mkdir -p "$LOG_DIR"

# QUAN TRỌNG: Đặt ownership đúng ngay sau khi tạo
chown -R "$APP_USER:$APP_GROUP" "$LOG_DIR"
chmod 755 "$LOG_DIR"

echo "✅ Log directory ready: $LOG_DIR (owner: $APP_USER:$APP_GROUP)"

Bài học

#Bài họcHành động
1Deploy script tạo file/dir → luôn chownThêm bước chown vào mọi deploy script
2Log failure nên crash loud, không silentConfig app để fail-fast nếu không ghi được log
3Monitoring cần check log freshnessAlert nếu log file không có entry mới trong 5 phút

5. Bài Tập Nhanh — Đọc Permission & Fix Lỗi

📝 Bài 1: Đọc ls -la output

Cho output sau:

-rw-r-----  1  nginx    web-ops   2048  Mar 8 10:00  ssl-cert.pem
drwxrwx---  3  deploy   web-team  4096  Mar 8 09:00  app/
-rwxr-xr-x  1  root     root      8192  Mar 8 08:00  health-check.sh
-rw-rw-r--  1  deploy   web-team  1024  Mar 8 07:00  config.yaml

Trả lời các câu hỏi:

  1. User nginx có thể ghi vào ssl-cert.pem không?
  2. User intern (thuộc group web-team) có thể xóa file trong thư mục app/ không?
  3. User deploy có thể chạy health-check.sh không?
  4. User monitor (không thuộc group nào đặc biệt) có thể đọc config.yaml không?
✅ Đáp án
  1. ✅ — nginx là owner, permission owner = rw- → có quyền write
  2. ✅ — intern thuộc group web-team, thư mục app/ cho group quyền rwx → có quyền write (tạo/xóa file trong directory)
  3. ✅ — deploy không phải owner (root) và không thuộc group root, nhưng other có quyền r-x → có quyền execute
  4. ✅ — config.yaml cho other quyền r-- → user bất kỳ đều đọc được

🐛 Bài 2: Fix Permission — SSH Key bị sai

🐛 Spot-the-Bug

Bạn vừa copy SSH key lên server mới. Khi chạy ssh git@github.com, nhận lỗi:

bash
$ ssh -i ~/.ssh/id_ed25519 git@github.com
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@         WARNING: UNPROTECTED PRIVATE KEY FILE!          @
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
Permissions 0644 for '/home/deploy/.ssh/id_ed25519' are too open.
It is required that your private key files are NOT accessible by others.
This private key will be ignored.

Kiểm tra:

bash
$ ls -la ~/.ssh/
drwxr-xr-x 2 deploy deploy 4096 Mar 8  .ssh/
-rw-r--r-- 1 deploy deploy  464 Mar 8  id_ed25519
-rw-r--r-- 1 deploy deploy  104 Mar 8  id_ed25519.pub

Tìm lỗi và fix.

💡 Gợi ý

SSH yêu cầu private key phải có permission rất chặt. Nhìn vào permission hiện tại của id_ed25519 (644) — ai khác ngoài owner có thể đọc file này?

Thư mục .ssh/ cũng cần kiểm tra — nó cho phép other đọc nội dung directory không?

✅ Lời giải

Lỗi 1: id_ed25519 có permission 644 (rw-r--r--) — group và other đọc được private key. SSH từ chối sử dụng key không được bảo vệ.

Lỗi 2: Thư mục .ssh/ có permission 755 (rwxr-xr-x) — other có thể list file trong directory.

Fix:

bash
# Fix thư mục .ssh — chỉ owner truy cập
chmod 700 ~/.ssh/

# Fix private key — chỉ owner đọc/ghi
chmod 600 ~/.ssh/id_ed25519

# Public key có thể ít chặt hơn, nhưng 644 là đủ
chmod 644 ~/.ssh/id_ed25519.pub

# Verify
ls -la ~/.ssh/
# drwx------ 2 deploy deploy 4096 Mar 8  .ssh/
# -rw------- 1 deploy deploy  464 Mar 8  id_ed25519
# -rw-r--r-- 1 deploy deploy  104 Mar 8  id_ed25519.pub

# Test lại
ssh -i ~/.ssh/id_ed25519 git@github.com
# Hi deploy! You've successfully authenticated...

Ghi nhớ: Private key luôn 600, thư mục .ssh luôn 700.


6. Khi Nào chmod 777 Là Sai?

⚠️ Cạm bẫy

Kịch bản quá quen thuộc

Bạn deploy app, gặp lỗi "Permission denied". Thay vì tìm hiểu nguyên nhân, bạn chạy:

bash
# ❌ "Giải pháp" của người vội vàng
sudo chmod -R 777 /var/www/myapp/
# "Nó chạy rồi! Deploy tiếp thôi..."

Tại sao 777 là thảm họa?

chmod 777 = rwxrwxrwx

Nghĩa là: BẤT KỲ USER NÀO trên hệ thống đều có thể:
✅ Đọc toàn bộ source code, config, secrets
✅ Ghi đè bất kỳ file nào (inject malicious code)
✅ Execute bất kỳ file nào (chạy backdoor)

Hậu quả thực tế:

  • 🔴 Security audit sẽ flag ngay lập tức — fail compliance
  • 🔴 Attacker exploit một vulnerability nhỏ → có full access vào toàn bộ app
  • 🔴 Đồng nghiệp vô tình ghi đè config → production down
  • 🔴 CI/CD tạo file với owner khác → conflict permission giữa các deploy

Cách đúng — Hiểu rồi mới chmod

bash
# Bước 1: Xác định app chạy bằng user nào
ps aux | grep myapp
# deploy  12345  myapp --port 3000

# Bước 2: Xác định app cần truy cập thư mục nào
strace -e openat -p 12345 2>&1 | head -20
# openat(AT_FDCWD, "/var/www/myapp/config.yaml", O_RDONLY) = 3
# openat(AT_FDCWD, "/var/log/myapp/app.log", O_WRONLY|O_CREAT) = -1 EACCES

# Bước 3: Đặt ownership đúng
sudo chown -R deploy:web-team /var/www/myapp/

# Bước 4: Đặt permission chính xác
# Thư mục: 755 (owner full, group+other đọc+traverse)
find /var/www/myapp/ -type d -exec chmod 755 {} \;

# File thường: 644 (owner đọc/ghi, group+other chỉ đọc)
find /var/www/myapp/ -type f -exec chmod 644 {} \;

# Script: 755 (thêm execute)
chmod 755 /var/www/myapp/start.sh

# Secrets: 600 (chỉ owner)
chmod 600 /var/www/myapp/.env

💡 Audit permission trên production

Trên production server, dùng find với -perm để audit permission thay vì đoán. Chạy định kỳ (hoặc đưa vào CI/CD):

bash
# Tìm files mà "other" có quyền write — potential security issue
find /var/www/ -type f -perm /o=w -ls

# Tìm files 777 — red flag
find /var/www/ -type f -perm 777 -ls

# Tìm files SetUID — review danh sách này thường xuyên
find / -perm -4000 -type f -ls 2>/dev/null

# Tìm files không thuộc expected user
find /var/www/myapp/ ! -user deploy -ls

Pro tip: Lưu output audit vào file và diff giữa các lần chạy — bất kỳ thay đổi nào bất thường đều đáng investigate.


7. Quiz — Kiểm Tra Kiến Thức

🧠 Quiz

Câu 1: File /etc/nginx/nginx.conf có permission 644. User www-data (thuộc group root) muốn ghi vào file này. Kết quả?

  • [ ] A. Ghi được — www-data thuộc group root và group có quyền r--
  • [x] B. Không ghi được — group permission chỉ có r-- (read), không có w (write)
  • [ ] C. Ghi được — file config luôn cho phép write
  • [ ] D. Không ghi được — chỉ user nginx mới truy cập được file .conf

💡 Giải thích: Permission 644 = rw-r--r--. Group (root) chỉ có quyền read (r--). Muốn www-data ghi được, cần đổi group ownership hoặc thêm write permission cho group: chmod 664 hoặc chown :www-data nginx.conf.


Câu 2: Bạn chạy chmod 700 ~/.ssh/chmod 600 ~/.ssh/id_ed25519. Tại sao SSH yêu cầu permission chặt như vậy?

  • [ ] A. Vì SSH protocol chỉ hỗ trợ đọc file với permission 600
  • [ ] B. Vì performance — permission chặt hơn giúp đọc file nhanh hơn
  • [x] C. Vì security — nếu user khác đọc được private key, họ có thể impersonate bạn
  • [ ] D. Vì Linux kernel không cho phép SSH đọc file với permission khác 600

💡 Giải thích: SSH private key là credential — tương đương password. Nếu bất kỳ ai trên server đọc được, họ có thể SSH vào mọi server mà bạn có quyền truy cập. SSH client chủ động từ chối sử dụng key có permission quá rộng để bảo vệ bạn.


Câu 3: Thư mục /tmp/ có permission 1777. Số 1 ở đầu nghĩa là gì?

  • [ ] A. SetUID — process chạy với quyền root
  • [ ] B. SetGID — file mới kế thừa group
  • [x] C. Sticky bit — chỉ owner mới xóa được file của mình
  • [ ] D. Extended ACL — permission nâng cao

💡 Giải thích: Sticky bit (octal 1) trên directory nghĩa là: dù mọi user đều có quyền write vào /tmp/ (vì 777), nhưng user A không thể xóa file do user B tạo. Chỉ file owner và root mới xóa được. Đây là cơ chế bảo vệ thiết yếu cho shared directories.


8. Checklist — Ghi Nhớ Nhanh

✅ Checklist triển khai

  • [ ] SSH keys: private key 600, thư mục .ssh 700
  • [ ] Web app: thư mục 755, file 644, secrets 600
  • [ ] Log directory: ownership = app user, permission 755
  • [ ] Deploy script: luôn chown sau khi tạo directory
  • [ ] Không bao giờ chmod 777 trên production
  • [ ] Audit định kỳ: find -perm /o=w để tìm file có permission quá rộng
  • [ ] SetUID audit: find / -perm -4000 — review danh sách thường xuyên
  • [ ] Config files: owned by root, readable by app user (group permission)