Xác thực bằng mật khẩu qua SSH là bề mặt phơi ra lớn nhất của một máy chủ thông thường. Chống lại nó thì cả Fail2ban lẫn mọi ngưỡng đều vô ích: một nghìn địa chỉ với một lần thử mỗi giờ sẽ không bao giờ chạm giới hạn, và chúng sẽ dò đúng chừng nào việc dò còn khả thi. Khóa loại bỏ chính khả năng đó, và trong toàn bộ danh sách hardening thì đây là cấu hình duy nhất thay đổi tình hình về mặt chất.

Chỉ có một điều đáng sợ ở nó: tắt mật khẩu rồi bị bỏ lại bên ngoài. Dưới đây là trình tự khiến điều đó không thể xảy ra.

Khóa

Được tạo trên máy của bạn, không phải trên máy chủ:

ssh-keygen -t ed25519 -C "work laptop"

Dùng ed25519 thay vì RSA: ngắn hơn, nhanh hơn, và không có câu hỏi về độ dài. RSA chỉ cần khi phải với tới thứ gì đó rất cũ; khi đó dùng -t rsa -b 4096.

Đặt passphrase cho khóa là đáng làm: tệp khóa có thể bị đánh cắp khỏi laptop, và không có passphrase thì nó hoạt động ngay. Bạn sẽ không phải gõ nó mỗi lần — ssh-agent lo việc đó, và trên macOS cùng đa số Linux desktop nó đã chạy sẵn.

Việc sao chép lên máy chủ mất một lệnh, trong khi mật khẩu còn dùng được:

ssh-copy-id -i ~/.ssh/id_ed25519.pub user@203.0.113.25

Nếu không có ssh-copy-id, nội dung tệp .pub được thêm thành một dòng vào ~/.ssh/authorized_keys trên máy chủ. Quyền là quan trọng: thư mục .ssh là 700, tệp là 600, thuộc sở hữu của người dùng đó. Với bất kỳ giá trị nào khác, sshd lặng lẽ từ chối đọc tệp, và đó là nguyên nhân phổ biến nhất của «khóa không hoạt động».

Phép kiểm tra quyết định tất cả

Trước khi tắt bất cứ thứ gì, hãy mở một kết nối thứ hai mà không đóng cái đầu tiên:

ssh -o PasswordAuthentication=no user@203.0.113.25

Tùy chọn này cấm client quay lại dùng mật khẩu — nên nếu đăng nhập được thì tức là đã đăng nhập bằng khóa. Đừng đi tiếp cho tới khi lệnh này chạy được. Và hãy giữ kết nối đầu tiên mở tới tận cùng: chính nó sẽ sửa mọi thứ bạn kịp làm hỏng.

Tắt mật khẩu và một cái bẫy không hiển nhiên

Cấu hình đi vào một tệp riêng để bản cập nhật gói không ghi đè lên. Nhưng tên tệp thì quan trọng:

sudo tee /etc/ssh/sshd_config.d/10-hardening.conf <<'EOF'
PasswordAuthentication no
PermitRootLogin no
KbdInteractiveAuthentication no
PubkeyAuthentication yes
MaxAuthTries 3
LogLevel VERBOSE
EOF

Vấn đề là OpenSSH dùng giá trị đầu tiên nó tìm được cho một tham số, chứ không phải giá trị cuối như gần như mọi nơi khác. Các tệp trong sshd_config.d được đọc theo thứ tự bảng chữ cái, và các image đám mây của Ubuntu đặt ở đó 50-cloud-init.conf, tệp này thường chứa PasswordAuthentication yes. Trong tình huống đó tệp đánh số 90 của bạn sẽ chẳng làm gì cả: cấu hình trông như đã đặt trong khi mật khẩu vẫn tiếp tục hoạt động. Từ đó mà có con số 10 — nó được đọc sớm hơn.

Có thể thấy kết quả thực sự mà không cần đoán:

sudo sshd -T | grep -Ei 'passwordauth|permitrootlogin|pubkeyauth'

Lệnh này in ra cấu hình có hiệu lực sau tất cả các include. Hãy tin nó, đừng tin nội dung các tệp.

Sau đó là kiểm tra cú pháp và nạp lại dịch vụ nhẹ nhàng:

sudo sshd -t && sudo systemctl reload ssh

sshd -t là bắt buộc: một lỗi gõ trong tệp cấu hình cùng với restart sẽ để dịch vụ chết còn bạn ở bên ngoài. reload cũng không cắt các phiên đang mở.

Ubuntu 24.04: cổng không nằm ở nơi bạn nghĩ

Một cái bẫy riêng trên các bản Ubuntu gần đây: ở đó sshd được khởi động qua socket của systemd. Ở chế độ đó dòng Port trong sshd_config chẳng làm gì cả — cái lắng nghe là socket chứ không phải daemon. Nếu bạn đổi cổng thì thứ phải sửa là:

sudo systemctl edit ssh.socket

và ListenStream= (giá trị rỗng đặt lại giá trị trước) phải nêu cổng mới. Triệu chứng để nhận ra: cấu hình đã đổi, dịch vụ đã khởi động lại, mà máy chủ vẫn trả lời trên cổng 22.

Nhân nói về việc đổi cổng: như một biện pháp bảo vệ thì nó không có tác dụng — các trình quét tìm thấy dịch vụ trên bất kỳ cổng nào trong vài phút. Hiệu quả duy nhất của nó là nhật ký yên tĩnh hơn, vì việc dò hàng loạt chỉ nhắm vào cổng 22. Điều đó tiện lợi, nhưng đừng nhầm nó với an toàn.

Ai được phép đăng nhập

Một bổ sung hữu ích là danh sách rõ ràng:

AllowUsers deploy admin

Mọi thứ không có trong danh sách đều bị cắt trước cả khi khóa được kiểm tra. Điều đó cũng bao phủ trường hợp một gói nào đó tạo ra người dùng hệ thống có shell và thư mục home.

Lối vào dự phòng

Một khóa trên một laptop là điểm hỏng duy nhất. Ổ đĩa chết, laptop mất, và máy chủ trở nên không thể với tới mãi mãi. Mức tối thiểu hợp lý:

  • một khóa thứ hai từ thiết bị khác trong authorized_keys;
  • một bản sao khóa riêng trong trình quản lý mật khẩu hoặc trên USB đã mã hóa;
  • quyền truy cập console của nhà cung cấp đã được thử — VNC hoặc serial. Đã được thử nghĩa là bạn đã đăng nhập qua nó ít nhất một lần, chứ không phải «có cái nút ở đâu đó trong bảng điều khiển».

Nhân tiện hãy xem trong authorized_keys hiện đã có gì — cả của người dùng bạn lẫn của root. Một khóa lạ nằm ở đó sống sót qua mọi lần đổi mật khẩu và vẫn là quyền truy cập hoạt động được.

Sau đó

Việc dò mật khẩu sẽ không biến mất, nó chỉ trở nên vô dụng: trong nhật ký vẫn còn hàng nghìn dòng Failed password, không dòng nào có thể kết thúc bằng thành công. Thứ đáng theo dõi bây giờ là các lần đăng nhập thành công: từ địa chỉ nào, dưới tên người dùng nào, vào lúc nào. Một lần đăng nhập bằng khóa thành công từ địa chỉ lạ là sự kiện quan trọng hơn nhiều so với một triệu lần thất bại, và khó nhận ra hơn nhiều trong dòng chảy chung. Đó chính là bức tranh mà trang demo bên dưới cho thấy.