---
id: linux-ssh
title: "SSH"
shortTitle: "SSH"
track: linux
trackTitle: "Linux & Shell Scripting"
order: 6
level: core
status: published
estimatedMinutes: 35
prerequisites:
  - linux-shell-scripting
next: linux-packages
reviewedAgainst: 2026-10-15
sources:
  - title: "KodeKloud Engineer — 100 Days of DevOps"
    url: https://engineer.kodekloud.com/practice
  - title: "ssh(1)"
    url: https://man.openbsd.org/ssh
  - title: "sshd(8)"
    url: https://man.openbsd.org/sshd
  - title: "sshd_config(5)"
    url: https://man.openbsd.org/sshd_config
---

# SSH

SSH là cách bạn đăng nhập và chuyển file vào server. Lab này tạo key riêng trong thư mục bài, nên key sẵn có trong `~/.ssh` không bị đụng tới.

## Để làm gì

Sau bài này, bạn tạo được cặp key Ed25519, chỉ định đúng file private key khi kết nối, đưa đúng một public key lên server bằng `ssh-copy-id -i`, và chặn root bằng `PermitRootLogin no`. Một dòng sai trong `sshd_config` cũng đủ khóa bạn ở ngoài server.

## Gồm những gì

- `ssh-keygen -t ed25519 -f -N -C`, `ssh -i`, `ssh-copy-id -i`.
- `authorized_keys` trên server, quyền thư mục `.ssh`.
- `PermitRootLogin`: `yes`, `prohibit-password`, `forced-commands-only`, `no`.
- `PasswordAuthentication` trên `sshd`.

Bài này không nói về VPN, bastion nhiều tầng hay SSH certificate của tổ chức. Jump host chỉ được nhắc qua.

## Dùng khi nào

Bạn dùng SSH mỗi khi cần shell trên một máy Linux. CI cũng dùng SSH với kiểu deploy cũ, nhưng job mới nên dùng identity của cloud thay vì cất một private key trong secret.

Đừng bật `PasswordAuthentication yes` ra cả Internet chỉ vì chưa kịp đưa key lên. Hãy đưa key lên cho xong rồi tắt đăng nhập bằng password.

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

`ssh-keygen` tạo một cặp private key và public key. Public key là dòng bạn đưa vào `~/.ssh/authorized_keys` của user trên server. Private key thì ở lại máy bạn; ai có được nó là đăng nhập được với danh nghĩa của bạn.

Hiện tại Ed25519 là lựa chọn mặc định hợp lý. Chỉ dùng RSA khi gặp thiết bị cũ chưa hỗ trợ Ed25519.

`sshd` là process nghe trên port 22. Nếu bạn sửa `sshd_config` mà chưa reload, rule cũ vẫn đang áp dụng. Nếu reload với file sai cú pháp, lần sau `sshd` không lên được, và bạn mất đường vào nếu không còn session nào đang mở. Vì vậy hãy giữ một session đang mở, sửa ở session khác, và chạy `sshd -t` trước khi reload.

`PermitRootLogin` nhận đúng một argument: `yes`, `prohibit-password`, `forced-commands-only`, hoặc `no`. OpenSSH hiện lấy mặc định `prohibit-password`. Giá trị đó tắt password và keyboard-interactive cho root, nhưng root vẫn vào bằng public key. `no` chặn mọi cách. `forced-commands-only` chỉ cho root bằng key khi key có option `command`, dùng cho một lệnh cố định như backup. `without-password` là tên cũ của `prohibit-password`. Muốn root không vào được thì ghi `PermitRootLogin no`. `PasswordAuthentication no` không thay được dòng đó: root vẫn có thể vào bằng key khi `PermitRootLogin` còn là `prohibit-password`.

Về quyền: `~/.ssh` phải là `700`, `authorized_keys` là `600`, và cả hai thuộc đúng user. Nếu quyền lỏng hơn, `sshd` bỏ qua file và từ chối key, mà phía client không nhận được thông báo nào dễ hiểu.

## Lệnh và code mẫu

```bash
mkdir -p ~/devops-lab/ssh
ssh-keygen -t ed25519 -f ~/devops-lab/ssh/lab_ed25519 -N "" -C "devops-lab"
```

Khi đã có server và public key đã nằm trong `authorized_keys`, kết nối bằng:

```bash
ssh -i ~/devops-lab/ssh/lab_ed25519 user@host
```

`-i` chỉ định private key. Đưa key lên server lần đầu, khi password vẫn còn bật:

```bash
ssh-copy-id -i ~/devops-lab/ssh/lab_ed25519.pub user@host
```

`-i` ở đây nhận file identity. Tên không kết thúc bằng `.pub` thì `ssh-copy-id` tự thêm `.pub`. Không có `-i`, script có thể lấy mọi key đang nằm trong ssh-agent. Một lần chạy này đăng nhập bằng password để ghi `authorized_keys`, nên password phải còn bật đúng lần đó. Xong thì tắt password.

Trên server, trước khi reload:

```bash
sudo sshd -t && sudo systemctl reload ssh
```

Tùy distro, unit tên là `ssh` hoặc `sshd`. Chạy `systemctl status ssh sshd` để biết unit nào đang chạy.

## Thực hành

1. Tạo key trong `~/devops-lab/ssh`. Đừng dùng `-f ~/.ssh/id_ed25519` nếu file đó đã tồn tại.
2. Mở `lab_ed25519.pub`. Đây là dòng được phép đưa lên server. File không có đuôi `.pub` thì không gửi cho ai cả.
3. Nếu có VM lab, thêm dòng trong file `.pub` vào `authorized_keys`, rồi `ssh -i` vào. Không có VM thì dừng ở bước 2.
4. Xóa thư mục lab khi không cần key đó nữa.

## Kết quả mong đợi

Thư mục lab có hai file: `lab_ed25519` và `lab_ed25519.pub`, và `ssh-keygen` in ra loại key `ED25519`. Nếu bạn làm bước login, SSH vào thẳng shell mà không hỏi password của user.

## Checklist

- [ ] Giữ private key trên máy bạn, chỉ đưa file `.pub` lên server.
- [ ] Chạy `sshd -t` trước khi reload, và giữ một session cũ.
- [ ] Tắt đăng nhập password khi key đã login được.
- [ ] Chặn root bằng `PermitRootLogin no`. `prohibit-password` vẫn cho root vào bằng key.

## Thẻ ôn

### linux-ssh-key
kind: distinguish
cue: Chỉ đưa file `.pub` lên `authorized_keys` của server. Private key ở lại máy bạn. Ai đọc được private key là đăng nhập được với danh nghĩa của bạn.

Q: Bạn tạo một cặp SSH key để vào server. File nào được phép đưa lên server, file nào phải ở lại máy bạn, và vì sao Ed25519 là lựa chọn mặc định hợp lý khi không có ràng buộc cũ?

A: Public key là file kết thúc bằng `.pub`. Nội dung đó được thêm vào `~/.ssh/authorized_keys` trên server. Private key không rời máy bạn, không gửi vào chat, không để trong repo. Ai có bản private key là mở được phiên với đúng user đó, vì server chỉ kiểm tra chữ ký khớp public key đã cài. Ed25519 là kiểu key mặc định hợp lý cho cặp mới: ngắn và được OpenSSH hỗ trợ sẵn, trừ khi hệ thống cũ bắt buộc một kiểu khác.

### linux-ssh-test
kind: command
cue: Sửa `sshd_config` thì `sshd -t` trước, giữ session đang mở, thử một kết nối mới, rồi mới đóng session cũ.

Q: Bạn sửa `sshd_config` và định reload ngay để áp dụng. Làm thế nào để một dòng sai không khóa bạn ở ngoài máy?

A: Chạy `sshd -t` trước. Lệnh này kiểm tra file cấu hình mà chưa đổi tiến trình đang phục vụ. Giữ nguyên cửa sổ SSH đang đăng nhập, đừng đóng. Mở một cửa sổ khác và thử đăng nhập mới. Kết nối mới vào được thì cấu hình mới ổn, lúc ấy mới đóng session cũ. Một dòng sai có thể làm lần start sau `sshd` không lên. Nếu bạn đã đóng session duy nhất, không còn cửa để sửa.

### linux-ssh-perm
kind: trap
cue: `chmod 777 ~/.ssh` làm `sshd` bỏ qua key. Thư mục để `700`, file key để `600`.

Q: Đăng nhập bằng key bị từ chối sau khi ai đó `chmod 777` thư mục `~/.ssh` cho "đỡ lỗi quyền". `sshd` đang làm gì, và hai quyền cần đặt lại là gì?

A: `sshd` cố ý bỏ qua `authorized_keys` khi thư mục hoặc file key để quá mở, vì user khác trên máy có thể đã sửa danh sách key. Mở `777` không chữa được từ chối, nó là lý do bị từ chối. Đặt `700` cho `~/.ssh` để chỉ chủ thư mục vào được, và `600` cho file key để chỉ chủ file đọc được. Rồi thử đăng nhập lại.

### linux-ssh-password
kind: decide
cue: Đừng bật đăng nhập bằng password ra Internet vì chưa kịp đưa key. Đưa public key lên, login bằng key được, rồi mới tắt password.

Q: Server SSH đang mở ra Internet. Public key chưa được đưa lên, và có người bảo bật `PasswordAuthentication yes` tạm vài hôm cho tiện. Vì sao không, và nếu đổi `Port` thì còn phải khớp chỗ nào?

A: Password trên cổng mở ra Internet là thứ bị dò liên tục. "Tạm vài hôm" thường thành cấu hình quên tắt. Đưa public key lên trước, xác nhận một phiên mới vào được bằng key, rồi mới tắt đăng nhập password. Nếu đổi `Port` cho khỏi cổng 22 mặc định, security group hoặc firewall và `sshd_config` phải mở đúng cùng một port. Lệch một phía thì hoặc vẫn mở 22, hoặc cổng mới không vào được.

### linux-ssh-root
kind: decide
cue: `PermitRootLogin no` chặn mọi cách root vào SSH. Mặc định `prohibit-password` vẫn cho root vào bằng public key.

Q: Cần khóa root khỏi SSH. `PasswordAuthentication no` đã ghi xong. Root với một public key vẫn vào được. Argument nào của `PermitRootLogin` mới chặn hết, và mặc định hiện tại để lộ lỗ nào?

A: Ghi `PermitRootLogin no`. OpenSSH mặc định `prohibit-password`: tắt password của root, không tắt key. `forced-commands-only` vẫn cho root chạy một lệnh gắn sẵn trên key. `PasswordAuthentication no` áp cho user thường và không thay dòng `PermitRootLogin`. Sửa xong thì `sshd -t`, giữ session cũ, thử một kết nối root mới phải bị từ chối, rồi mới đóng session.

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

**`chmod 777 ~/.ssh`.** Khi đó `sshd` bỏ qua `authorized_keys` và từ chối key. Đặt `700` cho thư mục và `600` cho file key.

**Đổi `Port 22` sang port khác rồi bị mất session.** Security group và `sshd` phải mở cùng một port. Nếu chỉ đổi một phía, kết nối tới port mới sẽ timeout. Sửa xong, thử kết nối port mới từ một session khác trước khi đóng cửa sổ hiện tại.

**Tưởng `PasswordAuthentication no` đã khóa root.** Mặc định `PermitRootLogin prohibit-password` vẫn nhận public key của root. Muốn chặn hết thì `PermitRootLogin no`.

## Nguồn

1. [KodeKloud Engineer — 100 Days of DevOps](https://engineer.kodekloud.com/practice) — đề cương ngày khóa root SSH và ngày xác thực bằng key. Trang practice cần đăng nhập. Bài này viết lại lệnh, không chép lab.
2. [ssh(1)](https://man.openbsd.org/ssh) — `-i`, và cách client chọn key.
3. [sshd(8)](https://man.openbsd.org/sshd) — `sshd -t`.
4. [sshd_config(5)](https://man.openbsd.org/sshd_config) — `PasswordAuthentication`, `PermitRootLogin`, `PubkeyAuthentication`.
5. [ssh-copy-id(1)](https://manpages.debian.org/unstable/openssh-client/ssh-copy-id.1.en.html) — `-i` chỉ copy đúng một identity.

## Tiếp theo

[Package manager](./05-packages.md)
