Fedora ServerへのRedisインストールと設定:SELinuxとfirewalldを乗り越えてハイパフォーマンスキャッシュサーバーを構築する

Fedora tutorial - IT technology blog
Fedora tutorial - IT technology blog

午前2時。本番環境のアプリが接続タイムアウトエラーを連発していた。ログを確認すると、Redisクライアントが接続できていない。午後にセットアップしたFedora Serverに Redis をインストールし終えたばかりで、ローカルでredis-cli pingを実行するとPONGが返ってきていた——しかし別サーバーのアプリからは完全にタイムアウトしていた。

これはFedoraを使い始めて最初に学んだ教訓だ:FedoraはUbuntuではない。SELinux enforcingとfirewalldははるかに厳格で、bind address、firewall、SELinuxという三つの層をすべて対処しなければ、朝まで debug しても解決できない。

実際に直面した問題

Redisをインストールしてサービスを起動し、サーバー上で直接テストすると問題なかった。しかし同じ内部ネットワーク上の別サーバーで動いているNode.jsアプリが接続しようとすると、ちょうど30秒後にタイムアウトしてしまう。

/var/log/redis/redis.logを確認——異常なし。systemctl status redisを確認——起動中。ファイアウォールを疑いfirewall-cmd --list-portsを確認——port 6379がない。ポートを開けてリロードして再テスト——それでもタイムアウト。

そこでようやく思い出した:FedoraはデフォルトでSELinuxをenforcingモードで動かしている。Ubuntuは通常AppArmorをもっと緩いプロファイルで使う。FedoraはSELinuxが最初から厳格で、誰も事前に教えてくれなかった。

原因の分析

調べてみると、同時に対処しなければならないことが三つあった——一つずつデバッグしていたのが1時間以上かかった原因だった:

  • Redis bind address:デフォルトでRedisは127.0.0.1のみリッスンしている——ローカルアクセスのみで、別サーバーのアプリからは絶対に接続できない
  • firewalld:port 6379はデフォルトで完全にブロックされており、例外なし
  • SELinux:ファイアウォールを開放しても、SELinuxがRedisのポートバインドや他プロセスからのネットワーク接続をブロックする可能性がある

この三つはすべて対処する必要がある。一つでも見落とすとまたデバッグの繰り返しになる。

Fedora ServerへのRedisのインストール

ステップ1:デフォルトリポジトリからRedisをインストール

メインの開発マシンとしてFedoraを2年間使っているが、FedoraリポジトリのRedisは常に比較的新しく、現在は7.xで外部リポジトリは不要だ。policycoreutils-python-utilsパッケージは後のSELinuxのステップで使うaudit2allowaudit2whyのために一緒にインストールする。

sudo dnf install redis policycoreutils-python-utils -y

# バージョン確認
redis-server --version

ステップ2:Redisの設定

主な設定ファイルは/etc/redis/redis.confにある。編集前にバックアップを取る:

sudo cp /etc/redis/redis.conf /etc/redis/redis.conf.bak
sudo nano /etc/redis/redis.conf

変更が必要な行:

# localhostとサーバーの内部IPの両方にバインド
# 192.168.1.100を内部ネットワークインターフェースの実際のIPに置き換える
bind 127.0.0.1 192.168.1.100

# パスワードを設定——ネットワークに公開する場合は必須
# Redis 6+はより強力なACLをサポートするが、シンプルな設定にはrequirepassで十分
requirepass YourStrongPassword123!

# メモリ制限——目安:サーバーの全RAMの約25〜30%
# 512mbはRAM 2〜4GBのサーバーに適している
maxmemory 512mb
maxmemory-policy allkeys-lru

# デフォルトポート
port 6379

bind addressについて:アプリとRedisが同じサーバーにある場合は127.0.0.1のままにする。別サーバーからの接続が必要な場合は内部インターフェースのIPを追加する。ファイアウォールが十分に設定されていない限り、本番環境で0.0.0.0は使わないようにする。

ステップ3:サービスの有効化と起動

sudo systemctl enable redis --now
sudo systemctl status redis

SELinux問題の解決

このセクションが最もスキップされやすい部分だ——ほとんどのRedisチュートリアルはUbuntu向けに書かれており、SELinuxについてはほとんど触れていない。Fedoraでは違う:最初からenforcingモードで動いており、setenforce 0で無効化すべきではない。正しい方法はpolicyを作成することだ。

SELinuxがブロックしているかの確認

# Redisに関連する最近のdenyを確認
sudo ausearch -m avc -ts recent | grep redis

# setroubleshootのメッセージを確認
sudo journalctl -t setroubleshoot | tail -50

Redisに関連するavc: denied \{ name_connect \}のような行が見えたら——間違いなく、SELinuxがブロックしている

方法1:既存のSELinux booleanを有効化する

# Redisに関連するすべてのbooleanを確認
sudo getsebool -a | grep redis

# 必要なネットワーク接続を許可
sudo setsebool -P redis_can_network_connect on

booleanの名前はFedoraのバージョンによって異なる——redis_can_network_connectがリストにない場合は、方法2に進む。

方法2:auditログからSELinux policyを作成する(最も正確な方法)

こちらの方法をよく使う——実際にブロックされているものからpolicyを作成するので、推測がいらない:

# ブロックされた理由を確認
sudo ausearch -m avc -ts recent | grep redis | audit2why

# policyモジュールを作成
sudo ausearch -m avc -ts recent | grep redis | audit2allow -M redis_custom

# policyを適用
sudo semodule -i redis_custom.pp

# モジュールがロードされているか確認
sudo semodule -l | grep redis

Redisのデータディレクトリを変更した場合

デフォルトの/var/lib/redis以外のディレクトリにデータを変更した場合、SELinuxコンテキストを再設定する必要がある:

# /data/redisへの変更例
sudo semanage fcontext -a -t redis_db_t "/data/redis(/.*)?" 
sudo restorecon -Rv /data/redis

firewalldの設定

ポートを開放する——正しい方法で

すべてのIPに対してport 6379を完全に開放するのは速いが安全ではない。rich ruleを使って内部サブネットのみを許可する:

# 192.168.1.0/24サブネットのみRedis接続を許可
sudo firewall-cmd --permanent --add-rich-rule='rule family="ipv4" source address="192.168.1.0/24" port port="6379" protocol="tcp" accept'
sudo firewall-cmd --reload

# ルールが適用されているか確認
sudo firewall-cmd --list-rich-rules

特定のアプリサーバーだけが接続する必要がある場合:

sudo firewall-cmd --permanent --add-rich-rule='rule family="ipv4" source address="192.168.1.50" port port="6379" protocol="tcp" accept'
sudo firewall-cmd --reload

rich rulesの代わりにinternalゾーンを使用する

専用の内部ネットワークインターフェース(例:eth1)がある場合、internalゾーンの方がすっきりしていて後から管理しやすい:

# 内部インターフェースをinternalゾーンに追加
sudo firewall-cmd --permanent --zone=internal --add-interface=eth1

# internalゾーンでRedisポートを開放
sudo firewall-cmd --permanent --zone=internal --add-port=6379/tcp
sudo firewall-cmd --reload

FedoraでのセキュアなRedisデプロイチェックリスト

午前2時のトラブルを何度か経験した後、本番環境で実行している手順がこれだ——この順番で:

# 1. インストール
sudo dnf install redis policycoreutils-python-utils -y

# 2. パスワードとbind addressでRedisを設定
sudo cp /etc/redis/redis.conf /etc/redis/redis.conf.bak
# /etc/redis/redis.confのbind、requirepass、maxmemoryを編集

# 3. サービスを起動
sudo systemctl enable redis --now

# 4. SELinuxがauditイベントを生成するよう約5分動かしてからpolicyを作成
sudo ausearch -m avc -ts recent | grep redis | audit2allow -M redis_custom
sudo semodule -i redis_custom.pp

# 5. 内部サブネットのファイアウォールを開放
sudo firewall-cmd --permanent --add-rich-rule='rule family="ipv4" source address="192.168.1.0/24" port port="6379" protocol="tcp" accept'
sudo firewall-cmd --reload

# 6. ネットワーク内の別のマシンからテスト
redis-cli -h 192.168.1.100 -p 6379 -a YourStrongPassword123! ping

セットアップ後の確認

# Redisが正しいインターフェースをリッスンしているか確認
sudo ss -tlnp | grep redis

# 認証テスト
redis-cli -h 127.0.0.1 -a YourStrongPassword123! ping

# サーバー情報を確認
redis-cli -a YourStrongPassword123! info server | grep -E "redis_version|uptime|connected_clients"

オプション:systemdでリソースを制限する

追加する価値のある制御レイヤーがもう一つある——Redisがメモリリークに遭遇したりリクエストのfloodを受けた場合に備えてsystemdレベルで制限する:

sudo systemctl edit redis

以下の内容を追加する:

[Service]
LimitNOFILE=65535
MemoryMax=512M
sudo systemctl daemon-reload
sudo systemctl restart redis

三つの保護レイヤー——SELinux、firewalld、パスワード認証——は互いを補完する。どれか一つを省いても不必要な脆弱点を生み出すことになる。最初に15分かけて正しくセットアップすれば、午前2時の2時間のデバッグを防げる。

Share: