SSL修正: error:0200100D:system library:fopen:Permission denied — Nginxが証明書ファイルを読み取れない問題

intermediate🔒 SSL/TLS2026-05-13| Ubuntu 20.04/22.04、Debian 11/12、CentOS 7/8、RHEL 8/9 — Nginx 1.18以降

Error Message

SSL: error:0200100D:system library:fopen:Permission denied (SSL: error:20074002:BIO routines:file_ctrl:system lib)
#nginx#ssl#パーミッション#証明書#linux

エラーの内容

Nginx を起動(またはリロード)すると、ログに以下のエラーが表示されます:

SSL: error:0200100D:system library:fopen:Permission denied (SSL: error:20074002:BIO routines:file_ctrl:system lib)

意味:Nginx が証明書または秘密鍵ファイルを開こうとしたところ、OS に拒否されました。プロセスに読み取り権限がないということです。それだけです。

このエラーは通常、nginx -t または systemctl reload nginx の直後に発生します。/var/log/nginx/error.log を確認すると、失敗した具体的なファイルが特定できます。

根本原因

Nginx はマスタープロセスとワーカープロセスに作業を分割します。マスターは root として動作するため、この部分は問題なく起動します。しかし、ワーカーは低権限ユーザーで動作します:Debian/Ubuntu では www-data、CentOS/RHEL では nginx です。これらのワーカーが実際に SSL 接続を処理しますが、特別なファイルアクセス権を継承しません。

以下のようなケースでこのエラーが発生します:

  • 証明書または鍵ファイルの所有者が root:root で、パーミッションが 600 の場合。root のみが読み取れるため、Nginx ワーカーユーザーはアクセスできません。
  • 親ディレクトリのパーミッションが 700 の場合、ワーカーはディレクトリを辿ることすらできません。ファイルが存在しないも同然です。
  • certbot で更新した場合、/etc/letsencrypt/live//etc/letsencrypt/archive/ はデフォルトで 700 に設定されています。これは常にそうなっています。
  • SELinux または AppArmor がアクセスをブロックしている場合、ls -la で見た目は問題なくても実際にはブロックされています。

ステップ 1 — Nginx が開けないファイルを特定する

まず設定テストを実行します:

sudo nginx -t 2>&1

サービスがすでに起動している場合は、エラーログを grep で検索します:

sudo tail -50 /var/log/nginx/error.log | grep -i 'permission\|fopen\|SSL'

出力にファイル名が表示されます。メモしておいてください。すぐに必要になります。

ステップ 2 — 現在のパーミッションを確認する

証明書ディレクトリとその内容を確認します:

ls -la /etc/nginx/ssl/
# Let's Encrypt の場合:
ls -la /etc/letsencrypt/live/yourdomain.com/
ls -la /etc/letsencrypt/archive/yourdomain.com/

次に、Nginx が実際にどのユーザーで動作しているかを確認します:

grep -E '^user' /etc/nginx/nginx.conf
# 通常: user www-data;  (Debian/Ubuntu)
# または:user nginx;     (CentOS/RHEL)

修正 A — Let's Encrypt 以外の証明書ファイル(最も一般的)

証明書が /etc/nginx/ssl/ に保存されている場合は、グループ所有権を Nginx ユーザーに設定し、パーミッションを適切に制限します:

# CentOS/RHEL の場合は www-data を nginx に変更してください
sudo chown root:www-data /etc/nginx/ssl/
sudo chmod 750 /etc/nginx/ssl/

sudo chown root:www-data /etc/nginx/ssl/yourdomain.crt
sudo chown root:www-data /etc/nginx/ssl/yourdomain.key

# 640 = root は読み書き可、グループ(Nginx ワーカー)は読み取り可、その他はアクセス不可
sudo chmod 640 /etc/nginx/ssl/yourdomain.crt
sudo chmod 640 /etc/nginx/ssl/yourdomain.key

Nginx ワーカーはグループメンバーシップを通じて読み取りアクセスを取得します。秘密鍵はそれ以外の全員からアクセス不可のままです。

8 進数の値をなかなか覚えられない場合は、ToolCraft の Unix Permissions Calculator を使うと、チェックボックスをクリックするだけで視覚的に適切な数値を確認できます。

修正 B — Let's Encrypt 証明書(Certbot)

Certbot は意図的に /etc/letsencrypt/700 に設定しています。回避策は 2 つあります。

オプション 1:Nginx ユーザーを ssl-cert グループに追加する(Debian/Ubuntu):

sudo usermod -aG ssl-cert www-data
sudo chgrp ssl-cert /etc/letsencrypt/live/
sudo chgrp ssl-cert /etc/letsencrypt/archive/
sudo chmod g+rx /etc/letsencrypt/live/
sudo chmod g+rx /etc/letsencrypt/archive/
sudo chgrp -R ssl-cert /etc/letsencrypt/archive/yourdomain.com/
sudo chmod -R g+r /etc/letsencrypt/archive/yourdomain.com/

オプション 2:証明書を Nginx が管理するディレクトリにコピーする(シンプルで環境を問わず動作):

sudo mkdir -p /etc/nginx/ssl
sudo cp /etc/letsencrypt/live/yourdomain.com/fullchain.pem /etc/nginx/ssl/
sudo cp /etc/letsencrypt/live/yourdomain.com/privkey.pem /etc/nginx/ssl/
sudo chown root:www-data /etc/nginx/ssl/*.pem
sudo chmod 640 /etc/nginx/ssl/*.pem

証明書をコピーすると同期の問題が生じます。更新のたびに実行されるデプロイフックで解決しましょう:

# /etc/letsencrypt/renewal-hooks/deploy/copy-to-nginx.sh
#!/bin/bash
cp /etc/letsencrypt/live/yourdomain.com/fullchain.pem /etc/nginx/ssl/
cp /etc/letsencrypt/live/yourdomain.com/privkey.pem /etc/nginx/ssl/
chown root:www-data /etc/nginx/ssl/*.pem
chmod 640 /etc/nginx/ssl/*.pem
systemctl reload nginx
sudo chmod +x /etc/letsencrypt/renewal-hooks/deploy/copy-to-nginx.sh

修正 C — SELinux によるアクセスブロック(CentOS/RHEL)

パーミッションは正しく見えるのに CentOS/RHEL でエラーが続く場合、ほぼ確実に SELinux が原因です。監査ログを確認します:

sudo ausearch -m avc -ts recent | grep nginx

AVC 拒否が記録されている場合は、証明書ファイルに正しい SELinux コンテキストを設定します:

sudo chcon -t cert_t /etc/nginx/ssl/yourdomain.key
sudo chcon -t cert_t /etc/nginx/ssl/yourdomain.crt
# 標準的な場所にある場合は restorecon の方が適切です:
sudo restorecon -Rv /etc/nginx/ssl/

修正 D — AppArmor プロファイル(Ubuntu)

Ubuntu の AppArmor は、パーミッションが正しくてもファイルアクセスをサイレントにブロックすることがあります。Nginx にアクティブなプロファイルがあるか確認します:

sudo aa-status | grep nginx
sudo dmesg | grep -i apparmor | grep nginx

プロファイルが読み込まれている場合は、デバッグ中に complain モードに設定します。これにより、Nginx は動作しながらブロックしたはずの内容をログに記録します:

sudo aa-complain /etc/apparmor.d/usr.sbin.nginx
# 正常に動作したら、証明書パスをプロファイルに正しく追加してから enforce モードに戻します

修正の確認

上記の修正を行った後、このチェックリストを実行します:

# 設定テスト — 「syntax is ok + test is successful」と表示されれば OK
sudo nginx -t

# リロード
sudo systemctl reload nginx

# 新しい SSL エラーはありますか?
sudo tail -20 /var/log/nginx/error.log

# 外部から TLS ハンドシェイクを確認
curl -vI https://yourdomain.com 2>&1 | grep -E 'SSL|TLS|certificate|subject'

正常な結果では、TLS ハンドシェイクが完了し、証明書のサブジェクトが出力に表示されます。ログにエラーがなければ完了です。

予防策

  • プロビジョニングスクリプトにパーミッション設定を組み込む。 Ansible、Terraform、または通常の bash セットアップスクリプトを使用している場合でも、デプロイ時に証明書の所有権と chmod を設定してください。後からパーミッションが変わってしまうことが、この問題の主な原因です。
  • 必ず certbot のデプロイフックを設定する。 更新のたびにパーミッションを再適用する必要があります。修正 B のフックはシェル 5 行でこれを処理します。
  • 秘密鍵に chmod 777 を設定しない。 秘密鍵のパーミッションは 600(root のみ)または 640(root + Web サーバーグループ)にしてください。それより広い設定はセキュリティ上の問題であり、修正ではありません。
  • 公開前にパス全体を確認する。 namei -om /path/to/your.key を実行してください。パスの各コンポーネントを辿り、どこでアクセスが失敗しているかを正確に表示します。ファイル単体に対する ls -la では見逃してしまうディレクトリのパーミッション問題を検出できます。

Related Error Notes