---
id: linux-filesystem-permissions
title: "File system và permissions"
track: linux
trackTitle: "Linux Basics & Shell Scripting"
order: 2
level: core
status: published
estimatedMinutes: 60
prerequisites:
  - linux-shell-files
next: linux-selinux
reviewedAgainst: 2026-10-15
sources:
  - title: "KodeKloud Engineer — 100 Days of DevOps"
    url: https://engineer.kodekloud.com/practice
  - title: "Filesystem Hierarchy Standard 3.0"
    url: https://refspecs.linuxfoundation.org/FHS_3.0/fhs/index.html
  - title: "chmod(1) — Linux man-pages"
    url: https://man7.org/linux/man-pages/man1/chmod.1.html
  - title: "chown(1) — Linux man-pages"
    url: https://man7.org/linux/man-pages/man1/chown.1.html
  - title: "ln(1) — Linux man-pages"
    url: https://man7.org/linux/man-pages/man1/ln.1.html
---

# File system và permissions

Các lệnh trong bài chạy được trên Linux hoặc WSL2.

## Để làm gì

Bạn cần đọc được cây directory trên máy Linux và đặt đúng permission cho file. Khi gặp `Permission denied` lúc chạy script, SSH từ chối key, hay app không đọc được file config, chỗ đầu tiên nên kiểm tra thường là permission. Ba bit đặc biệt — setuid, setgid, sticky — quyết định file chạy với quyền ai, file mới trong thư mục nhóm thuộc group nào, và ai được xóa file trong thư mục dùng chung. Hard link và symlink là hai cách đặt thêm tên cho cùng dữ liệu hoặc cho một đường dẫn: xóa một tên thì hậu quả khác nhau. Account service thì trỏ shell tới `nologin`, và account tạm thì có ngày tắt riêng với hạn của password.

## Gồm những gì

- Đường dẫn chuẩn trong Filesystem Hierarchy Standard (FHS).
- Ba lớp permission: owner, group, other.
- `chmod` theo số và theo ký hiệu: `u+x`, `755`, `644`, `600`, `u+s`, `g+s`, `+t`.
- Ba bit đặc biệt: setuid (`4000`), setgid (`2000`), sticky / restricted deletion (`1000`). Chữ `s` / `S` / `t` / `T` trên `ls -l`.
- `chown` để đổi owner và group.
- `umask`: permission mặc định khi tạo file mới.
- Hard link (`ln TARGET LINK`) và symlink (`ln -s TARGET LINK`): inode, số link, xóa tên, directory, partition.
- `useradd -s` / `-e` / `-m` / `-M`, `usermod -s` / `-e`, `chage -l` / `-E` / `-M`.

Bài này không cấu hình SELinux hay AppArmor. Cần nhớ là hai cơ chế đó vẫn có thể chặn process dù `ls -l` cho thấy permission đã đúng. SELinux nằm ở bài sau.

## Dùng khi nào

Bạn quay lại bài này khi process báo `Permission denied`, khi script không chạy, khi thư mục nhóm tạo file sai group, khi ai đó xóa được file của người khác trong `/tmp`, khi `rm` một tên mà chương trình khác vẫn đọc được (hoặc symlink báo No such file), hoặc khi phát hiện file chứa secret đang để mọi user trên máy đọc được.

Đừng sửa bằng `chmod 777`. Quyền đó cho mọi user local được ghi và execute file. Đừng gắn setuid lên script Bash: kernel Linux bỏ qua bit đó trên file shebang.

## Kiến thức cốt lõi

FHS quy ước mỗi directory dùng để làm gì. Distro hiện nay hay gộp `/bin` vào `/usr/bin`, nhưng các directory dưới đây vẫn là chỗ đầu tiên để tìm file:

| Đường dẫn | Vai trò |
| --- | --- |
| `/etc` | Cấu hình của host |
| `/var` | Dữ liệu thay đổi theo thời gian: log, cache, state của service (`/var/lib`) |
| `/var/log` | Log |
| `/home` | Thư mục người dùng |
| `/tmp` | File tạm, thường có sticky bit |
| `/proc`, `/sys` | Giao diện kernel dạng file, không phải đĩa chứa dữ liệu ứng dụng |
| `/opt` | Phần mềm cài thêm theo từng sản phẩm |

Mỗi file có owner, group, và một bộ permission. Mỗi lớp có ba bit:

| Bit | Giá trị | Với file | Với thư mục |
| --- | --- | --- | --- |
| `r` | 4 | Đọc nội dung | Liệt kê tên bên trong |
| `w` | 2 | Sửa nội dung | Tạo, đổi tên, xóa mục bên trong |
| `x` | 1 | Chạy file | Đi xuyên qua thư mục |

`755` là `rwxr-xr-x`: owner có read, write, execute; group và other có read và execute. `644` là `rw-r--r--`: owner ghi được, không ai có bit x.

`chmod +x script.sh` chỉ bật thêm bit x, trừ những bit bị `umask` che. Với `umask 022`, file đang `600` sẽ thành `711`, không thành `755`.

Ba bit `r` `w` `x` chưa đủ. `chmod` còn một chữ số đứng trước bộ ba số quen thuộc. `chmod(1)` gọi chúng là set-user-ID, set-group-ID, và restricted deletion flag (sticky bit). Cộng octal: `4` setuid, `2` setgid, `1` sticky. `4755` = setuid + `755`. `2775` = setgid trên directory + `775`. `1777` = sticky + `777`. Ký hiệu: `u+s`, `g+s`, `+t` (hoặc `o+t`).

`ls -l` không in số. Nó thay chữ `x` ở đúng lớp:

| Bit | Có luôn `x` | Không có `x` | Ví dụ |
| --- | --- | --- | --- |
| setuid | `s` chỗ owner | `S` | `-rwsr-xr-x` là `4755` |
| setgid | `s` chỗ group | `S` | `drwxrwsr-x` là `2775` |
| sticky | `t` chỗ other | `T` | `drwxrwxrwt` là `1777` (`/tmp`) |

`stat -c '%a %A'` in cả octal lẫn chuỗi đó. Đọc `S` / `T` là bit đặc biệt bật nhưng lớp đó thiếu execute — file setuid mà owner không có `x` thì chẳng ai chạy được như bạn tưởng.

**setuid (SUID, `4000`)**

Gắn trên file thực thi. Khi user bất kỳ exec file đó, process chạy với effective user ID của owner file, không phải của người đang gõ lệnh. Mục đích: cho user thường làm đúng một việc cần quyền owner (thường là root), mà không phát password root.

Ví dụ trên mọi distro: `/usr/bin/passwd` thuộc root, mode thường `4755` (`-rwsr-xr-x`). User thường đổi mật khẩu của mình được vì process `passwd` chạy với euid root, nên ghi được `/etc/shadow`. `passwd` tự giới hạn: chỉ sửa được dòng của user đang gọi, không mở cửa sửa user khác.

```html
<div class="net-flow" role="img" aria-label="User thường gọi passwd, kernel đặt euid root, process ghi shadow">
<div class="net-step" style="--layer:3"><strong>User thường</strong><span>gõ passwd</span></div>
<div class="net-arrow" aria-hidden="true"></div>
<div class="net-step" style="--layer:4"><strong>/usr/bin/passwd</strong><span>setuid, owner root</span></div>
<div class="net-arrow" aria-hidden="true"></div>
<div class="net-step" style="--layer:1"><strong>Kernel</strong><span>euid = owner = root</span></div>
<div class="net-arrow" aria-hidden="true"></div>
<div class="net-step" style="--layer:5"><strong>/etc/shadow</strong><span>chỉ sửa đúng dòng của user</span></div>
</div>
```

Hai chỗ hay nhầm. Một: setuid trên script (`#!/bin/bash`) Linux bỏ qua. Interpreter sẽ chạy với quyền người gọi, không phải owner file. Cần binary (C đã link, hoặc file hệ thống như `passwd`). Hai: partition gắn `nosuid` thì kernel cũng bỏ setuid/setgid — USB hoặc home mount kiểu đó thì `chmod 4755` vô dụng.

**setgid (SGID, `2000`)**

Trên file thực thi: process chạy với effective group ID của group sở hữu file. Ít gặp hơn setuid, cùng ý: nâng đúng group, không nâng user.

Trên directory: file và directory con tạo mới lấy group của directory cha, không lấy group chính của người tạo. `chmod(1)` giữ setgid trên directory trừ khi bạn gỡ tường minh (`g-s`, hoặc numeric kiểu `00755` / `=755`). Việc nhóm: thư mục project `2775`, group `ops`, mọi file mới cùng group `ops` — thành viên group sửa được nếu mode cho phép, không phải mỗi người một group riêng.

**sticky bit (restricted deletion, `1000`)**

`chmod(1)` nói rõ nghĩa trên directory: user không đặc quyền không xóa và không đổi tên một mục trừ khi họ là owner của mục đó hoặc owner của directory. Root vẫn xóa được. Bit `w` trên directory vẫn cho phép tạo file; sticky chỉ khóa thao tác xóa/đổi tên file người khác.

`/tmp` và `/var/tmp` gần như luôn `1777` (`drwxrwxrwt`): ai cũng tạo file tạm, không ai xóa file của người khác. Không có sticky, `777` trên thư mục chung nghĩa là user A `rm` được file của user B.

Trên file thường, man còn nhắc nghĩa cũ (giữ text trên swap). Linux hiện nay không dùng nghĩa đó. Gặp sticky trên file thường thì đừng coi là “chạy nhanh hơn”.

**Hard link và symlink**

Một file trên đĩa là một inode: metadata + khối dữ liệu. Tên trong directory chỉ là một mục trỏ tới số inode. `ls -l` cột thứ hai (và `stat -c '%h'`) là số tên đang trỏ tới inode đó.

Hard link (`ln(1)` mặc định, không `-s`): thêm một tên mới cùng inode. `ln TARGET LINK_NAME` — TARGET phải đã tồn tại. Hai tên bình đẳng. Đọc/ghi tên nào cũng đụng cùng dữ liệu. Không nhân bản nội dung, `du` trên từng tên không cộng hai lần. Xóa một tên (`rm`) chỉ giảm số link. Dữ liệu còn chừng nào số link > 0 (và không còn process giữ file descriptor). Khi số link về 0 và không ai mở, kernel mới trả khối đĩa.

```html
<div class="net-flow" role="img" aria-label="Hai tên hard link cùng một inode">
<div class="net-step" style="--layer:3"><strong>data.txt</strong><span>tên trong directory</span></div>
<div class="net-arrow" aria-hidden="true"></div>
<div class="net-step" style="--layer:1"><strong>inode</strong><span>cùng số, nlink ≥ 2</span></div>
<div class="net-arrow" aria-hidden="true"></div>
<div class="net-step" style="--layer:3"><strong>data-hard</strong><span>tên thứ hai, không kém hơn</span></div>
</div>
```

Hai hạn chế. Không hard link directory — `ln` không `-s` lên thư mục sẽ lỗi; `-d`/`-F` chỉ cho superuser thử, hệ thống gần như không cho. Không hard link sang partition khác: `link(2)` trả lỗi kiểu `Invalid cross-device link`. `stat -c '%d'` hai bên phải cùng device.

Symlink / soft link (`ln -s TARGET LINK_NAME`): file mới, inode riêng. Nội dung là một chuỗi đường dẫn. `ls -l` chữ đầu `l` và `->`. `readlink` in đúng chuỗi đó. TARGET lúc tạo không bắt buộc tồn tại — `ln(1)` nói symbolic link giữ text tùy ý. Đường dẫn tương đối được giải theo thư mục chứa symlink, không theo chỗ bạn đang `pwd`. Xóa hoặc đổi tên đích thì symlink đứt: mở ra `No such file or directory`. Có thể trỏ directory. Có thể cắt sang partition khác vì nó chỉ là text.

```html
<div class="net-flow" role="img" aria-label="Symlink là inode riêng chứa đường dẫn">
<div class="net-step" style="--layer:4"><strong>data-soft</strong><span>inode riêng, type symlink</span></div>
<div class="net-arrow" aria-hidden="true"></div>
<div class="net-step is-memo" style="--layer:5"><strong>chuỗi path</strong><span>không phải dữ liệu file</span></div>
<div class="net-arrow" aria-hidden="true"></div>
<div class="net-step" style="--layer:3"><strong>data.txt</strong><span>inode dữ liệu; mất thì link đứt</span></div>
</div>
```

`ln -s` + `-r` tạo path tương đối tính từ chỗ đặt link. `chmod` trên symlink Linux đi theo đích; `ls -l` không `-L` thì hiện chính symlink. Đừng hard link khi bạn cần shortcut sang ổ khác hoặc sang thư mục.

`umask` là mặt nạ được trừ khỏi quyền tối đa. File mới xuất phát từ `666`, directory mới từ `777`. Với `umask 022`, file mới là `644` và directory mới là `755`.

Account local ghi trong `/etc/passwd`. Trường shell là một đường dẫn tuyệt đối. Account của service không được mở shell thì trỏ trường đó tới `nologin`: chương trình in lời từ chối rồi thoát với mã 1. Đường dẫn không giống nhau giữa các distro. Lấy bằng `command -v nologin`, rồi đưa đúng chuỗi đó cho `-s`. Để trống trường shell không phải là cấm login. `usermod -s ""` xóa trường, và user vào shell mặc định của hệ thống.

`useradd -e YYYY-MM-DD` và `usermod -e` đặt ngày tài khoản bị tắt. Ngày tính theo UTC. `chage -E` đặt cùng ngày đó trên account đã có. `chage -l` in các hạn để đọc. `chage -M` là số ngày tối đa password còn hiệu lực, một trường khác trong `/etc/shadow`. File `/etc/nologin` hoặc `/run/nologin` là chuyện khác: `pam_nologin` dùng một trong hai file đó để chặn user thường trên cả máy, không phải shell của một account.

## Lệnh và code mẫu

Xem permission, kể cả file ẩn:

```bash
ls -la /var/log
```

Cột đầu có dạng `-rwxr-xr-x`. Ký tự đầu là loại: `-` là file thường, `d` là directory, `l` là symlink.

Đặt một bộ permission:

```bash
chmod 755 deploy.sh
chown deploy:deploy deploy.sh
```

`chown user:group` đổi cả owner lẫn group. `chown user` chỉ đổi owner, còn `chown :group` chỉ đổi group. Trên nhiều máy, lệnh này cần root hoặc `sudo`.

Ký hiệu `chmod` có dạng `[ugoa][+-=]quyền`. `u` là owner, `g` là group, `o` là other, `a` là cả ba. `chmod u+x deploy.sh` chỉ bật bit execute cho owner. Không ghi `ugoa` thì `chmod +x` giống `a`, nhưng vẫn trừ bit mà `umask` đang che. Chữ `s` và `t` cùng bộ đó: `chmod u+s file`, `chmod g+s dir`, `chmod +t dir`. Gỡ: `u-s`, `g-s`, `-t`.

Xem ba bit đặc biệt trên máy (không sửa file hệ thống):

```bash
ls -l /usr/bin/passwd
stat -c '%a %A %U %G' /usr/bin/passwd
ls -ld /tmp
stat -c '%a %A' /tmp
```

`passwd` phải thấy `s` chỗ owner và owner `root`. `/tmp` phải thấy `t` chỗ other, octal `1777`.

Thư mục nhóm và thư mục scratch trong `$HOME` — không cần root:

```bash
mkdir -p "$HOME/devops-lab/linux/shared" "$HOME/devops-lab/linux/scratch"
chmod 2775 "$HOME/devops-lab/linux/shared"
chmod 1777 "$HOME/devops-lab/linux/scratch"
stat -c '%a %A %G' "$HOME/devops-lab/linux/shared" "$HOME/devops-lab/linux/scratch"
touch "$HOME/devops-lab/linux/shared/note.txt"
stat -c '%a %A %G' "$HOME/devops-lab/linux/shared/note.txt"
```

`shared` in `2775` và `rws` chỗ group. `note.txt` cùng group với `shared`. `scratch` in `1777` và `rwt`.

Hard link và symlink trong `$HOME` — không cần root:

```bash
cd "$HOME/devops-lab/linux"
printf '%s\n' 'link-lab' > data.txt
ln data.txt data-hard
ln -s data.txt data-soft
stat -c '%n inode=%i nlink=%h type=%F' data.txt data-hard data-soft
readlink data-soft
rm data.txt
cat data-hard
cat data-soft
```

`data.txt` và `data-hard` cùng `%i`, `nlink` = 2 trước khi `rm`. `data-soft` inode khác, type symbolic link. Sau `rm data.txt`, `cat data-hard` vẫn in `link-lab`; `cat data-soft` lỗi.

Tạo account chỉ trên VM lab. `useradd` sửa `/etc/passwd`. `-m` tạo home và copy `/etc/skel`. `-M` không tạo home, kể cả khi `CREATE_HOME` đang bật. Account không login thì dùng `-M`. `-p` nhận password đã mã hóa và lộ trên danh sách process, nên đừng dùng.

```bash
nologin_path="$(command -v nologin)"
sudo useradd -s "$nologin_path" -M labapp
sudo useradd -e 2026-12-31 -s "$nologin_path" -M labtemp
getent passwd labapp labtemp
sudo chage -l labtemp
sudo usermod -e 2026-11-01 labtemp
```

`chage -l "$USER"` đọc hạn của chính bạn, không cần root. Đọc hạn của user khác thì cần root.

Lab dưới đây chỉ làm trong `$HOME` nên không cần root:

```bash
mkdir -p "$HOME/devops-lab/linux"
cd "$HOME/devops-lab/linux"

printf '%s\n' '#!/usr/bin/env bash' 'echo hello' > script.sh
chmod 644 script.sh
ls -l script.sh
```

## Thực hành

1. Tạo lab ở trên.
2. Chạy `./script.sh` khi file còn `644`.
3. Chạy `chmod 755 script.sh`, rồi chạy lại `./script.sh`.
4. Chạy `stat -c '%a %A %U %G' script.sh` để xem cả octal và ký hiệu.
5. Chạy `umask`, rồi `touch sample.txt` và so permission của file mới với `umask`.
6. Chạy `command -v nologin`. Đó là argument của `-s`, không phải chữ `nologin` đứng một mình.
7. `chage -l "$USER"` và tìm dòng account expires. Dòng đó khác dòng password expires.
8. `stat -c '%a %A %U' /usr/bin/passwd`. Khớp `4755` (hoặc ít nhất có chữ `s` chỗ owner) và owner `root`.
9. Tạo `shared` / `scratch` như mẫu trên. `stat` `shared` ra `2775`; file mới trong đó cùng group với thư mục. `scratch` ra `1777`.
10. `chmod 4644` một file copy trong lab rồi `ls -l`: chữ `S` (hoa) vì setuid bật mà owner không có `x`. `chmod 4755` đổi thành `s` thường.
11. Tạo `data.txt` / `data-hard` / `data-soft` như mẫu. Cùng inode trên hard; `rm data.txt` rồi `cat data-hard` còn chữ, `cat data-soft` đứt. `ln` không `-s` lên một thư mục phải lỗi.

## Kết quả mong đợi

- Lần chạy đầu fail vì thiếu bit x.
- Sau `chmod 755`, lệnh in `hello`.
- `stat` in `755 -rwxr-xr-x` cùng user hiện tại.
- `sample.txt` có permission bằng `666` trừ `umask`. Với `umask 022`, kết quả là `644`.
- `command -v nologin` in một đường dẫn tuyệt đối, thường là `/usr/sbin/nologin` hoặc `/sbin/nologin`.
- `chage -l` in riêng ngày tắt account và hạn của password.
- `/usr/bin/passwd` có setuid, owner root. `shared` là `2775`. `scratch` và `/tmp` là `1777`.
- Hard link cùng inode, còn dữ liệu sau khi xóa một tên. Symlink inode riêng, đứt khi đích mất.

## Checklist

- [ ] Chỉ ra directory nào giữ config, log, và dữ liệu thay đổi.
- [ ] Đọc được `rwxr-xr-x` là `755`.
- [ ] Phân biệt `chmod +x` với `chmod 755`.
- [ ] Nhớ bit `x` trên directory nghĩa là được đi vào directory đó.
- [ ] Cấm shell của một account bằng `useradd -s` trỏ tới đường dẫn `nologin`, không để trống trường shell.
- [ ] Đặt ngày tắt account bằng `-e` hoặc `chage -E`. `chage -M` là tuổi của password.
- [ ] Đọc `s` / `S` / `t` / `T` trên `ls -l` và đổi ra `4xxx` / `2xxx` / `1xxx`.
- [ ] Nói được setuid trên file, setgid trên file khác trên directory, sticky trên directory (`/tmp`).
- [ ] Không gắn setuid lên script; kiểm `passwd` là binary.
- [ ] Phân biệt hard link và symlink: inode, `nlink`, xóa tên, directory, partition.

## Thẻ ôn

### linux-filesystem-permissions-special
kind: distinguish
cue: setuid `4000`/`u+s` file chạy bằng euid owner (`passwd`). setgid `2000`/`g+s` trên dir: file mới kế thừa group. sticky `1000`/`+t` trên dir: không xóa file người khác (`/tmp` = `1777`).

Q: Một máy có `/usr/bin/passwd` là `-rwsr-xr-x` root, thư mục nhóm `drwxrwsr-x`, và `/tmp` là `drwxrwxrwt`. Ba chữ `s`/`t` mỗi chỗ làm gì? Octal tương ứng? Gắn `u+s` lên script `.sh` có nâng quyền không?

A: Chữ `s` chỗ owner là setuid: process lấy euid của owner file, nên user thường chạy `passwd` vẫn ghi được `/etc/shadow` đúng dòng của mình. `4755`. Chữ `s` chỗ group trên directory là setgid: file mới lấy group của thư mục, không lấy group chính của người tạo. `2775`. Chữ `t` chỗ other là sticky: trong thư mục `777`, user không xóa hay đổi tên file mình không sở hữu. `/tmp` là `1777`. Script có shebang thì kernel Linux bỏ setuid; `u+s` trên `.sh` không nâng quyền. Cần binary. `S`/`T` hoa nghĩa là bit đặc biệt bật nhưng lớp đó không có `x`.

### linux-filesystem-permissions-755
kind: command
cue: `rwxr-xr-x` là `755`. Trên thư mục, bit `x` nghĩa là được đi vào thư mục đó, không chỉ là "được chạy".

Q: `ls -l` in `rwxr-xr-x` trên một thư mục. Đó là số nào? Owner, group và other mỗi bên được gì? Bit `x` trên thư mục khác bit `x` trên file thế nào? `644` thì sao?

A: `755`. Owner có đọc, ghi và đi vào. Group và other có đọc và đi vào, không có ghi. Trên file, `x` là được thực thi. Trên thư mục, `x` là được đi vào, tức truy cập file bên trong khi đã biết tên. Thiếu `x` trên thư mục thì dù có `r` cũng không dùng được các mục bên trong theo đường dẫn. `644` là `rw-r--r--`: owner ghi được, không ai có bit `x`.

### linux-filesystem-permissions-links
kind: distinguish
cue: `ln a b` hard: cùng inode, xóa một tên dữ liệu còn. `ln -s a b` soft: inode riêng, đích mất thì đứt. Hard không sang thư mục / partition khác.

Q: Bạn `ln data.txt data-hard` rồi `ln -s data.txt data-soft`. `stat` hai cặp khác nhau chỗ nào? `rm data.txt` thì `cat` từng link ra sao? Khi nào bắt buộc `-s`?

A: Hard link cùng số inode, `nlink` tăng (thường 2). Không tốn thêm bản nội dung. `rm data.txt` chỉ bỏ một tên; `cat data-hard` vẫn đọc được. Symlink là file mới, type `symbolic link`, `%i` khác; nội dung là path. `rm data.txt` thì `cat data-soft` lỗi (broken). Hard không tạo được cho directory và không cắt sang filesystem khác (`%d` khác nhau). Shortcut sang thư mục hoặc sang partition khác thì `ln -s`. Path tương đối trong symlink tính từ thư mục chứa link, không từ `pwd`.

### linux-filesystem-permissions-777
kind: trap
cue: Script báo Permission denied thì sửa đúng quyền, đừng `chmod 777`. Script để `755`, file dữ liệu `644`, private key `600`.

Q: Một script in `Permission denied`. Cách sửa nhanh nhất trên mạng là `chmod 777`. Vì sao không làm vậy, và script, file dữ liệu, private key mỗi loại để quyền nào?

A: `777` cho mọi account trên máy được ghi và chạy file đó. Ai đăng nhập được là sửa được script, lần chạy sau sẽ chạy bản đã bị sửa. Script cần chạy thì `755`: owner ghi, người khác chỉ đọc và chạy. File dữ liệu không cần thực thi thì `644`. Private key `chmod 600`, vì OpenSSH từ chối dùng key nếu user khác đọc được. Permission denied thường là thiếu `x` trên script hoặc thiếu quyền đi vào thư mục, không phải lý do để mở toàn bộ.

### linux-filesystem-permissions-nologin
kind: distinguish
cue: Cấm shell của một account thì `useradd -s` trỏ tới đường dẫn `command -v nologin` in ra. Trường shell để trống là vào shell mặc định.

Q: Một account service không được mở shell. Có người xóa trường shell trong `/etc/passwd` cho khỏi login. Việc đó khác `useradd -s` tới `nologin` thế nào?

A: `nologin` là chương trình: in lời từ chối rồi thoát mã 1. `-s` phải nhận đường dẫn tuyệt đối mà `command -v nologin` in ra. `usermod -s ""` xóa trường shell, và account vào shell mặc định của hệ thống, không bị cấm. `/etc/nologin` hoặc `/run/nologin` lại là `pam_nologin`, chặn user thường trên cả máy. Một account thì sửa trường shell của account đó.

### linux-filesystem-permissions-expire
kind: distinguish
cue: Ngày tắt account là `useradd -e` hoặc `chage -E`, dạng `YYYY-MM-DD`, tính theo UTC. `chage -M` là số ngày password còn hiệu lực.

Q: Account tạm phải ngừng dùng vào một ngày đã định. `chage -M 30` có phải lệnh đó không, và lệnh nào đặt đúng ngày tắt account?

A: Không. `-M` ghi số ngày tối đa password còn hiệu lực, một trường khác trong `/etc/shadow`. Ngày tắt account là `useradd -e YYYY-MM-DD` lúc tạo, hoặc `usermod -e` và `chage -E` khi account đã có. Ngày đó theo UTC. `chage -l` in cả hai hạn để khỏi nhìn nhầm. `-1` hoặc chuỗi rỗng ở `-e` / `-E` xóa ngày tắt account.

## Lỗi thường gặp

**Sửa `Permission denied` bằng `chmod 777`.** Làm vậy thì mọi account local đều sửa và chạy được file. Script nên để `755`, file dữ liệu để `644`. Riêng private key, OpenSSH từ chối dùng nếu user khác đọc được, nên hãy `chmod 600 key`.

**`chmod` symlink rồi không chắc file đích đã đổi chưa.** Trên Linux, `chmod` đi theo symlink tới file đích, nhưng `ls -l` có thể đang hiện thông tin của chính symlink. Xem `ls -lL` hoặc `stat` trên đường dẫn đích trước khi kết luận.

**Permission của file đã đúng mà process vẫn bị từ chối.** Kiểm tra bit `x` của mọi directory cha trên đường dẫn. Khi chắc chỗ đó ổn rồi mới xét tới SELinux hoặc AppArmor.

**Xóa trường shell rồi tưởng account không login được.** `usermod -s ""` đưa user vào shell mặc định. Cấm shell thì `-s` trỏ tới đường dẫn `nologin`.

**`chage -M` khi việc cần làm là tắt account vào một ngày.** `-M` là tuổi của password. Ngày tắt account là `-e` hoặc `chage -E`.

**Gắn setuid lên script rồi tưởng user thường chạy như root.** Linux không tôn trọng setuid/setgid trên file shebang. `ls` vẫn hiện `s`. Process vẫn là user đang gõ. `passwd` là binary.

**Thư mục nhóm `775` mà không `g+s`.** File mới mang group của người tạo. Thành viên khác trong group `ops` có thể không ghi được. Cần `2775` trên directory.

**Thư mục chung `777` không sticky.** Ai có `w` trên directory cũng `rm` được file người khác. `/tmp` phải `1777`.

**`chmod 755` lên directory đang `2775`.** `chmod(1)`: numeric thiếu chữ số dẫn (kiểu `755`) không gỡ setgid trên directory theo cách bạn tưởng mỗi lần. Muốn gỡ tường minh thì `g-s`, `00755`, `-2000`, hoặc `=755`.

**`rm` file gốc rồi tưởng hard link cũng chết.** Hard link còn thì inode còn. `cat` tên còn lại vẫn ra nội dung. Symlink mới đứt.

**`ln` không `-s` sang ổ khác hoặc lên thư mục.** Hard link cùng filesystem, không phải directory. Shortcut thư mục hoặc partition khác: `ln -s`.

**Symlink path tương đối tính từ `pwd` lúc gõ.** `ln(1)`: path tương đối giải theo thư mục chứa link. Tạo link từ chỗ khác rồi `cd` đi, đích lệch.

## Nguồn

1. [KodeKloud Engineer — 100 Days of DevOps](https://engineer.kodekloud.com/practice) — đề cương ngày Linux user, shell không tương tác, và ngày hết hạn account. Trang practice cần đăng nhập. Bài này viết lại lệnh, không chép lab.
2. [Filesystem Hierarchy Standard 3.0](https://refspecs.linuxfoundation.org/FHS_3.0/fhs/index.html) — Linux Foundation, quy ước mỗi directory dùng để làm gì.
3. [chmod(1)](https://man7.org/linux/man-pages/man1/chmod.1.html) và [chmod(2)](https://man7.org/linux/man-pages/man2/chmod.2.html) — `u+s` / `g+s` / `+t`, octal `4`/`2`/`1`, setuid/setgid trên directory, restricted deletion trên `/tmp`.
4. [chown(1)](https://man7.org/linux/man-pages/man1/chown.1.html) — đổi owner và group.
5. [Coreutils — File permissions](https://www.gnu.org/software/coreutils/manual/html_node/File-permissions.html) — umask, setuid, setgid, sticky bit.
6. [useradd(8)](https://man7.org/linux/man-pages/man8/useradd.8.html), [usermod(8)](https://man7.org/linux/man-pages/man8/usermod.8.html), [chage(1)](https://man7.org/linux/man-pages/man1/chage.1.html), [nologin(8)](https://man7.org/linux/man-pages/man8/nologin.8.html) — `-s`, `-e`, `-E`, `-M`, và shell từ chối login.
7. [ln(1)](https://man7.org/linux/man-pages/man1/ln.1.html) — hard link mặc định, `-s` / `--symbolic`, TARGET phải tồn tại khi hard; symlink giữ text, path tương đối theo thư mục chứa link.

## Tiếp theo

[SELinux: cài và đặt mode](./08-selinux.md)
