エラーの内容
Redisに対してコマンドを実行すると、次のようなエラーが返ってきます:
127.0.0.1:6379> GET mykey
(error) NOPERM this user has no permissions to run the 'get' command
またはアプリケーションログから:
ReplyError: NOPERM this user has no permissions to run the 'get' command
これはRedis ACL(アクセス制御リスト)が正常に機能している証拠です。Redis 6.0(2020年4月リリース)で導入されたこの機能により、クライアントが認証したユーザーには当該コマンドの実行権限がないか、そのキーパターンへのアクセスが許可されていません。
原因
Redisへのすべての接続は、何らかのユーザーとして認証されます。明示的に設定した名前付きユーザーか、認証情報が送信されない場合は暗黙のdefaultユーザーです。各ユーザーには、実行できるコマンド、アクセスできるキーパターン、使用できるpub/subチャンネルを制御するACLルールがあります。制限に引っかかると、RedisはNOPERMを返します。
よくある原因:
aclfileまたはACL SETUSERでdefaultユーザーが制限されている- 専用のアプリユーザーが作成されたが、書き込み権限のみ(または設定者によっては読み取り権限のみ)が付与されている
- ユーザーにコマンドのアクセス権はあるが、キーが許可されたパターンに一致しない(例:
cache:*キーのみ許可) - 最近のセキュリティ強化作業で、既存ユーザーから広範なコマンドアクセスが削除された
ステップ1:接続中のユーザーを確認する
まずここから始めます。セッションでアクティブなユーザーを確認しましょう:
127.0.0.1:6379> ACL WHOAMI
"appuser"
defaultと表示された場合、クライアントが認証情報を送信していないため、未認証で接続しています。名前付きユーザーが表示された場合、そのユーザーのACLルールがブロックの原因です。
ステップ2:ユーザーのACLルールを確認する
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) "*"
2つのフィールドに注目します。commandsは許可されている内容を示します。上記の例では、+set -getはSETが許可されているがGETが明示的に拒否されていることを意味します。次にkeysを確認します。コマンドが許可されていても、ユーザーがcache:*のような特定のキーパターンに制限されている場合があります。
全ユーザーを一覧表示するには:
127.0.0.1:6379> ACL LIST
1) "user default on nopass ~* &* +@all"
2) "user appuser on #abc123... ~cache:* +set -get"
ステップ3:不足している権限を付与する
管理者ユーザー(+@allまたは少なくとも+aclを持つユーザー)で接続し、ACL SETUSERを使ってルールを修正します。
特定のコマンドを許可する
ACL SETUSER appuser +get
コマンドカテゴリを許可する(例:すべての読み取りコマンド)
ACL SETUSER appuser +@read
すべてのコマンドを許可する(信頼できる内部サービスのみ)
ACL SETUSER appuser +@all
特定のキーパターンを許可する
ACL SETUSER appuser ~cache:* ~session:*
完全な例 — 読み取り専用ユーザーをゼロから作り直す
ACL SETUSER readonly_app on >SecurePass123 ~* +@read
これにより、以下の設定でユーザーが作成(または上書き)されます:
- 有効化済み(
on) - パスワードは
SecurePass123 - 全キーパターンにアクセス可能(
~*) - すべての読み取りコマンドを実行可能(
+@read)—GET、MGET、LRANGE、SMEMBERS、HGETなど
ステップ4:ACLの変更を永続化する
ACL SETUSERはメモリ上の状態のみを更新します。Redisを再起動すると、すべての変更が元に戻ります。変更を永続化するには2つの方法があります:
オプションA:ACLファイルを使用する(推奨)
redis.confに以下を追加:
aclfile /etc/redis/users.acl
次に、現在のルールをディスクに書き出します:
127.0.0.1:6379> ACL SAVE
Redisはすべてのユーザールールをそのファイルに書き込み、再起動時にそこから読み込みます。
オプションB:redis.confに直接記述する
設定ファイルにユーザールールを直接追加します:
user appuser on >SecurePass123 ~cache:* +@read +set
ファイルを保存した後、Redisを再起動してください。
ステップ5:修正を確認する
ユーザーのルールを再度取得して、コマンドが正しく設定されているか確認します:
127.0.0.1:6379> ACL GETUSER appuser
...
5) "commands"
6) "+@read +set"
次に、失敗していたコマンドを実行します:
127.0.0.1:6379> AUTH appuser SecurePass123
OK
127.0.0.1:6379> GET mykey
"hello"
エラーが解消されました。ACLログで最近の拒否履歴を確認することもできます。他にブロックされているものがあるか不明な場合に役立ちます:
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"
...
トラブルシューティングが完了したらログをクリアします:
ACL LOG RESET
補足Tips
コマンドカテゴリを使えば入力が大幅に減る
個別のコマンドを列挙する代わりに、Redisではカテゴリ単位で権限を一括付与できます:
+@read— GET、MGET、LRANGE、SMEMBERS、HGETなど+@write— SET、DEL、LPUSH、SADD、HSETなど+@string— 文字列に関するすべてのコマンド+@hash— ハッシュに関するすべてのコマンド+@dangerous— FLUSHDB、CONFIG、DEBUGなど(厳重に制限すること)
利用可能なカテゴリをすべて表示するには:
ACL CAT
特定のカテゴリに含まれるコマンドを確認するには:
ACL CAT read
最小権限の原則 — 本当に守ること
エラーを解消するためだけに+@allを付与するのは避けましょう。楽な方法ではありますが、ACLの意味がなくなってしまいます。キューからジョブを取り出すバックグラウンドワーカーであれば、+@read +blpop +brpopだけで十分です。アプリが実際に何を呼び出しているか調べましょう。ACL LOGが役立ちます。そして必要な権限だけを付与してください。
本番環境ではdefaultユーザーを無効化する
未認証の接続にはいかなるアクセスも許可すべきではありません。本番環境での標準的な設定:
user default off nopass ~* -@all
これにより、アプリが認証情報を送り忘れた場合、フル権限でこっそり接続されるのではなく、明確なエラーが返ります。実際のアクセスはすべて名前付きユーザーで行います。
本番適用前にカテゴリをテストする
あるコマンドが+@readに含まれていると思っていたら、実は+@stringや+@sortedsetだったということはよくあります。本番ユーザーにルールを適用する前に、ACL CAT <category>を使ってターミナルで確認するようにしています。10秒もあれば確認でき、深夜のデバッグを何度も救ってくれます。

