Lỗi gặp phải
Bạn chạy một lệnh với Redis và nhận ngay thông báo lỗi này:
127.0.0.1:6379> GET mykey
(error) NOPERM this user has no permissions to run the 'get' command
Hoặc từ log ứng dụng:
ReplyError: NOPERM this user has no permissions to run the 'get' command
Đây là Redis ACL (Access Control List) đang hoạt động đúng chức năng — một tính năng được giới thiệu từ Redis 6.0 (phát hành tháng 4 năm 2020). User mà client của bạn xác thực không có quyền chạy lệnh đó, hoặc không được phép truy cập vào key pattern tương ứng.
Nguyên nhân
Mỗi kết nối trong Redis đều xác thực dưới danh nghĩa một user nào đó — có thể là user được đặt tên cụ thể, hoặc user default ẩn nếu không có thông tin xác thực nào được gửi. Mỗi user có các quy tắc ACL kiểm soát lệnh nào được phép chạy, key pattern nào được phép truy cập, và pub/sub channel nào được sử dụng. Vi phạm bất kỳ giới hạn nào, Redis sẽ trả về lỗi NOPERM.
Các nguyên nhân thường gặp:
- User
defaultđã bị hạn chế quyền thông quaaclfilehoặcACL SETUSER - Một user riêng cho ứng dụng đã được tạo nhưng chỉ có quyền ghi — hoặc chỉ đọc, tùy thuộc vào cấu hình ban đầu
- User có quyền thực thi lệnh nhưng key không khớp với pattern được cho phép (ví dụ: chỉ được phép truy cập các key thuộc dạng
cache:*) - Một đợt tăng cường bảo mật gần đây đã thu hẹp quyền truy cập lệnh rộng của các user hiện có
Bước 1: Kiểm tra user đang kết nối
Bắt đầu từ đây. Xác nhận user nào đang hoạt động trong phiên hiện tại:
127.0.0.1:6379> ACL WHOAMI
"appuser"
Thấy default? Client của bạn đang không gửi thông tin xác thực — kết nối đang ở chế độ không xác thực. Thấy tên user cụ thể? Đó chính là user mà các quy tắc ACL đang chặn bạn.
Bước 2: Kiểm tra quy tắc ACL của user
127.0.0.1:6379> ACL GETUSER appuser
1) "flags"
2) 1) "on"
3) "passwords"
4) 1) "...(hashed)"
5) "commands"
6) "+set -get"
7) "keys"
8) "cache:*"
9) "channels"
10) "*"
Chú ý vào hai trường quan trọng. commands hiển thị những gì được phép — trong ví dụ trên, +set -get nghĩa là SET được phép nhưng GET bị từ chối rõ ràng. Sau đó kiểm tra keys: dù lệnh được phép, user vẫn có thể bị giới hạn chỉ trong các key pattern cụ thể như cache:*.
Để xem tất cả user cùng lúc:
127.0.0.1:6379> ACL LIST
1) "user default on nopass ~* &* +@all"
2) "user appuser on #abc123... ~cache:* +set -get"
Bước 3: Cấp quyền còn thiếu
Kết nối bằng user admin (có +@all hoặc ít nhất là +acl), rồi dùng ACL SETUSER để sửa lại quy tắc.
Cho phép một lệnh cụ thể
ACL SETUSER appuser +get
Cho phép một nhóm lệnh (ví dụ: tất cả lệnh đọc)
ACL SETUSER appuser +@read
Cho phép tất cả lệnh (chỉ dùng cho các dịch vụ nội bộ đáng tin cậy)
ACL SETUSER appuser +@all
Cho phép các key pattern cụ thể
ACL SETUSER appuser ~cache:* ~session:*
Ví dụ đầy đủ — tạo lại user chỉ đọc từ đầu
ACL SETUSER readonly_app on >SecurePass123 ~* +@read
Lệnh này tạo mới (hoặc ghi đè) một user với các thuộc tính sau:
- Đang hoạt động (
on) - Mật khẩu là
SecurePass123 - Có thể truy cập tất cả key pattern (
~*) - Có thể chạy tất cả lệnh đọc (
+@read) —GET,MGET,LRANGE,SMEMBERS,HGET, và nhiều lệnh khác
Bước 4: Lưu thay đổi ACL vĩnh viễn
ACL SETUSER chỉ cập nhật trạng thái trên bộ nhớ RAM. Khởi động lại Redis và mọi thứ sẽ bị reset. Có hai cách để giữ lại thay đổi:
Tùy chọn A: Dùng ACL file (khuyến nghị)
Trong file redis.conf của bạn:
aclfile /etc/redis/users.acl
Sau đó lưu các quy tắc hiện tại xuống đĩa:
127.0.0.1:6379> ACL SAVE
Redis sẽ ghi toàn bộ quy tắc user vào file đó và tải lại từ file khi khởi động.
Tùy chọn B: Khai báo trực tiếp trong redis.conf
Thêm quy tắc user trực tiếp vào file cấu hình:
user appuser on >SecurePass123 ~cache:* +@read +set
Khởi động lại Redis sau khi lưu file.
Bước 5: Xác nhận đã sửa thành công
Kiểm tra lại quy tắc của user và xác nhận các lệnh đã đúng:
127.0.0.1:6379> ACL GETUSER appuser
...
5) "commands"
6) "+@read +set"
Sau đó chạy lại lệnh bị lỗi trước đó:
127.0.0.1:6379> AUTH appuser SecurePass123
OK
127.0.0.1:6379> GET mykey
"hello"
Đã xong. Bạn cũng có thể kiểm tra ACL log để xem lại các lần từ chối gần đây — hữu ích khi không chắc còn lệnh nào khác bị chặn:
127.0.0.1:6379> ACL LOG
1) 1) "count"
2) (integer) 3
3) "reason"
4) "command"
5) "context"
6) "toplevel"
7) "object"
8) "get"
9) "username"
10) "appuser"
...
Xóa log sau khi xử lý xong sự cố:
ACL LOG RESET
Mẹo hay
Nhóm lệnh giúp tiết kiệm thời gian gõ
Thay vì liệt kê từng lệnh riêng lẻ, Redis cho phép bạn cấp quyền theo nhóm lệnh:
+@read— GET, MGET, LRANGE, SMEMBERS, HGET, v.v.+@write— SET, DEL, LPUSH, SADD, HSET, v.v.+@string— tất cả lệnh string+@hash— tất cả lệnh hash+@dangerous— FLUSHDB, CONFIG, DEBUG, v.v. (nên hạn chế nhóm này)
Liệt kê tất cả nhóm lệnh có sẵn:
ACL CAT
Xem chính xác các lệnh trong một nhóm:
ACL CAT read
Nguyên tắc tối thiểu quyền — thực sự nên áp dụng
Đừng cấp +@all chỉ để xử lý nhanh lỗi. Rất hấp dẫn, nhưng làm vậy sẽ phá vỡ toàn bộ mục đích của ACL. Một worker nền chỉ lấy job từ queue chỉ cần +@read +blpop +brpop là đủ. Hãy xác định chính xác những lệnh ứng dụng thực sự gọi — ACL LOG có thể giúp bạn — rồi chỉ cấp đúng những quyền đó.
Khóa user default trong môi trường production
Các kết nối không xác thực không nên có bất kỳ quyền truy cập nào. Một cấu hình production chuẩn:
user default off nopass ~* -@all
Từ đây, nếu ứng dụng quên gửi thông tin xác thực, nó sẽ nhận được lỗi rõ ràng thay vì âm thầm kết nối với toàn quyền. Các user được đặt tên sẽ xử lý toàn bộ quyền truy cập thực tế.
Kiểm tra nhóm lệnh trước khi lên production
Rất dễ nhầm lẫn khi cho rằng một lệnh thuộc nhóm +@read trong khi thực tế nó lại nằm trong +@string hoặc +@sortedset. Trước khi áp dụng quy tắc cho user trên production, tôi thường mở sẵn một terminal với lệnh ACL CAT <category> để kiểm tra lại. Chỉ mất 10 giây nhưng tránh được nhiều đêm debug mệt mỏi.

