Tóm tắt: Tạo hai CA key (user CA và host CA). Ký public key của user bằng ssh-keygen -s user_ca -I <id> -n <principals> -V +8h; server tin user CA qua TrustedUserCAKeys và giới hạn principal bằng AuthorizedPrincipalsFile. Ký host key bằng -h; client tin host CA bằng dòng @cert-authority trong known_hosts. Thu hồi bằng KRL (RevokedKeys). Cấu hình sshd nền tảng xem SSH hardening.
Luồng hoạt động
flowchart LR U["User: id_ed25519.pub"] -->|"gửi public key"| CA["User CA (ssh-keygen -s)"] CA -->|"id_ed25519-cert.pub (8h, principals)"| U U -->|"ssh: key + cert"| S["sshd"] S --> T{"Ký bởi TrustedUserCAKeys?"} T -->|"có, còn hạn, không bị revoke"| P{"Principal có trong file của user đích?"} P -->|có| OK[Đăng nhập] P -->|không| NO[Từ chối] T -->|không| NO
So với authorized_keys:
| authorized_keys | SSH certificate | |
|---|---|---|
| Thêm người mới | Sửa file trên mọi server | Ký cert, server không đổi |
| Hết quyền | Xoá key khỏi mọi server | Cert tự hết hạn (TTL ngắn) |
| Host key | TOFU, cảnh báo khi đổi host key | Client tin host CA, không hỏi fingerprint |
| Ràng buộc | from=, command= trong từng dòng |
Trong cert: principal, thời hạn, source-address, force-command |
Cấu hình
Tạo CA (trên máy CA, tách khỏi server thường)
ssh-keygen -t ed25519 -f user_ca -C "user-ca@example.com" # đặt passphrase
ssh-keygen -t ed25519 -f host_ca -C "host-ca@example.com"Ai có user_ca (private) thì đăng nhập được mọi server tin CA đó. Giữ offline/HSM hoặc dùng CA có quản lý (Vault SSH secrets engine, step-ca). Chỉ user_ca.pub và host_ca.pub được phát đi.
Ký cert cho user
ssh-keygen -s user_ca -I "alice@example.com-2026-11-06" -n alice,netops -V +8h id_ed25519.pub
# -> tạo id_ed25519-cert.pub cạnh key, ssh client tự dùng
ssh-keygen -L -f id_ed25519-cert.pub # xem Key ID, Serial, Valid, Principals, Extensions| Tuỳ chọn | Ý nghĩa |
|---|---|
-I <key_id> |
Danh tính ghi vào log sshd, đặt dễ truy vết (email + ngày) |
-n alice,netops |
Principals: tên user hoặc "vai trò" mà cert được dùng |
-V +8h, -V -5m:+1d |
Thời hạn (lùi 5 phút để tránh lệch đồng hồ) |
-z 1042 |
Serial, dùng khi thu hồi theo serial |
-O source-address=198.51.100.0/24 |
Chỉ dùng được từ dải này |
-O force-command="/usr/local/bin/backup" |
Cert chỉ chạy được một lệnh |
-O no-port-forwarding, -O no-agent-forwarding |
Bỏ extension tương ứng |
Server tin user CA
TrustedUserCAKeys /etc/ssh/user_ca.pub
AuthorizedPrincipalsFile /etc/ssh/auth_principals/%u
RevokedKeys /etc/ssh/revoked.krl
HostCertificate /etc/ssh/ssh_host_ed25519_key-cert.pubsudo install -d -m 755 /etc/ssh/auth_principals
echo netops | sudo tee /etc/ssh/auth_principals/deploy # ai có principal "netops" được vào user deploy
echo alice | sudo tee /etc/ssh/auth_principals/alice
sudo ssh-keygen -k -f /etc/ssh/revoked.krl # KRL rỗng, tránh sshd từ chối mọi key vì thiếu file
sudo sshd -t && sudo systemctl reload sshKhông có AuthorizedPrincipalsFile thì sshd yêu cầu tên user đích nằm trong principals của cert. Có file thì chỉ principal liệt kê trong file mới hợp lệ, nhờ đó map vai trò → user.
Host certificate
# Ký host key của server (chạy trên máy CA, sau khi lấy ssh_host_ed25519_key.pub về)
ssh-keygen -s host_ca -I bastion01 -h -n bastion01.example.com,203.0.113.30 -V +52w ssh_host_ed25519_key.pub@cert-authority *.example.com,203.0.113.* ssh-ed25519 <nội-dung-host_ca.pub>Principals của host cert phải khớp đúng tên/IP mà client gõ. Kết hợp ProxyJump xem SSH config và jump host.
Thu hồi
ssh-keygen -k -u -f /etc/ssh/revoked.krl alice-cert.pub # thu hồi một cert/key (-u: cập nhật KRL có sẵn)
printf 'serial: 1042\n' > revoke.txt
ssh-keygen -k -u -s user_ca.pub -f /etc/ssh/revoked.krl revoke.txt # thu hồi theo serial của CA
ssh-keygen -Q -f /etc/ssh/revoked.krl alice-cert.pub # kiểm tra: in REVOKED, exit 1Phân phối KRL tới mọi server (Ansible), sshd đọc lại file theo từng kết nối, không cần reload.
Lỗi thường gặp
| Triệu chứng | Nguyên nhân | Cách xử lý |
|---|---|---|
Permission denied (publickey), log Certificate invalid: name is not a listed principal |
Principal không khớp user đích / file principals | Ký lại với -n đúng hoặc sửa auth_principals/<user> |
Log Certificate invalid: expired hoặc not yet valid |
Hết hạn hoặc lệch đồng hồ | Ký lại; kiểm tra NTP; dùng -V -5m:+8h |
| Client vẫn hỏi xác nhận fingerprint | Thiếu HostCertificate trên server hoặc pattern @cert-authority không khớp tên gõ |
ssh -v xem có Server host certificate; sửa principals/pattern |
Mọi user đều bị từ chối sau khi thêm RevokedKeys |
File KRL không tồn tại/không đọc được | Tạo KRL rỗng bằng ssh-keygen -k -f |
| Cert hợp lệ nhưng không port-forward được | Ký kèm -O no-port-forwarding hoặc AllowTcpForwarding no |
Ký lại hoặc nới Match trong sshd |
| Agent không dùng cert | Chỉ add private key, cert không cùng tên <key>-cert.pub |
ssh-add ~/.ssh/id_ed25519 khi file cert nằm cạnh; hoặc CertificateFile trong ~/.ssh/config |
Tham khảo
man ssh-keygen(mục CERTIFICATES, KEY REVOCATION LISTS)man sshd_config(TrustedUserCAKeys,AuthorizedPrincipalsFile,RevokedKeys,HostCertificate)man sshd(mục SSH_KNOWN_HOSTS FILE FORMAT,@cert-authority)