nginxでWebアプリの管理画面を
IPアドレスで制限する方法
DjangoやRailsなどのWebアプリケーションを公開するとき、管理画面(/admin/など)は誰でもアクセスできる状態にしておくべきではありません。
この記事では、nginxのリバースプロキシでIPアドレスによるアクセス制限を設定する方法を解説します。
こんな人向けの記事です
- VPSでWebアプリケーションを公開している(またはこれから公開する)
- 管理画面へのアクセスを自分だけに制限したい
- nginxをリバースプロキシとして使っている
なぜ管理画面のアクセス制限が必要なのか
Webアプリケーションの管理画面は、データの作成・編集・削除ができる強力な機能を持っています。攻撃者にとっては最も価値の高い標的です。
管理画面がインターネット上で誰でもアクセスできる状態だと、以下のリスクがあります。
- ブルートフォース攻撃: ログインフォームにパスワードを総当たりで試行
- 脆弱性の悪用: 管理画面フレームワーク自体の脆弱性を狙った攻撃
- 情報漏洩: ログインページの存在自体が、使用しているフレームワークの情報を与える
そもそも管理画面にアクセスできなければ、これらの攻撃は成立しません。IPアドレスで制限することで、ネットワークレベルで管理画面を保護できます。
構成イメージ
nginxが全てのリクエストを受け取り、URLパスに応じてアクセス制御を行います。
/admin/ /manage/ → 許可されたIPのみ通過、それ以外は 403 Forbidden/ (一般ページ)→ 全てのIPからアクセス可能
アプリケーション(gunicornなど)は外部に直接ポートを公開せず、nginxのみが外部からのリクエストを受け付けます。これにより、IP制限を確実に適用できます。
nginx の設定ファイルを作成する
nginxの envsubst 機能を使って、環境変数 ADMIN_IP から許可するIPアドレスを設定ファイルに埋め込みます。
server {
listen 80;
# 管理画面: 特定IPのみ許可
location /admin/ {
allow ${ADMIN_IP};
deny all;
proxy_pass http://web:8000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
location /manage/ {
allow ${ADMIN_IP};
deny all;
proxy_pass http://web:8000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
# 一般ページ: 全員アクセス可
location / {
proxy_pass http://web:8000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}
設定のポイント
allowに許可するIPアドレスを指定し、deny allでそれ以外を拒否locationブロックはパスごとに独立して設定できる\${ADMIN_IP}はnginxコンテナ起動時に環境変数から置換される- 制限したいパスが増えた場合は、
locationブロックを追加するだけ
nginx の Dockerfile
テンプレートファイルを /etc/nginx/templates/ に配置すると、nginxの公式Dockerイメージが起動時に自動で envsubst を実行してくれます。
FROM nginx:alpine
RUN rm /etc/nginx/conf.d/default.conf
COPY nginx.conf.template /etc/nginx/templates/default.conf.template
Docker Compose での構成
Docker Compose を使ってnginxとWebアプリケーションを連携させます。
# 管理画面にアクセスを許可するIPアドレス
ADMIN_IP=あなたのグローバルIPアドレス
services:
web:
build: .
expose:
- "8000" # 外部に公開しない(nginxのみが接続)
nginx:
build: ./nginx
ports:
- "80:80" # nginxだけが外部公開
environment:
- ADMIN_IP=\${ADMIN_IP:-127.0.0.1}
depends_on:
- web
expose はDocker内部のコンテナ間通信のみを許可します。ports はホストマシンのポートにマッピングし、外部からアクセスできるようにします。Webアプリを
expose のみにすることで、全てのリクエストがnginxを経由するようになり、IP制限を確実に適用できます。
自分のグローバルIPアドレスを調べる
以下のコマンドで確認できます。
curl -s ifconfig.me
表示されたIPアドレスを .env ファイルの ADMIN_IP に設定してください。
.env を更新して docker compose restart nginx を実行してください。固定IPが必要な場合は、VPNサービスやプロバイダーの固定IPオプションを検討してください。
IP アドレスの偽装は可能なのか
「IPアドレスを偽装されたら意味がないのでは?」と思うかもしれませんが、結論から言うとIPアドレスの偽装ではこの制限を突破できません。
その理由は、TCP通信の仕組みにあります。
TCP 3ウェイハンドシェイク
HTTPはTCPプロトコル上で動作します。TCP接続は以下の3ステップで確立されます。
- SYN: クライアント → サーバー「接続したい」
- SYN-ACK: サーバー → クライアント「OK、接続を受け入れる」
- ACK: クライアント → サーバー「確認した、通信を開始する」
攻撃者が送信元IPを偽装した場合、サーバーからのSYN-ACK応答は偽装先のIPアドレスに届きます。攻撃者はこの応答を受け取れないため、TCP接続を確立できません。
TCP接続が確立しなければHTTPリクエストは送信できず、管理画面にアクセスすることは不可能です。
多層防御: アプリケーション側の保護
IP制限は強力ですが、これだけに頼るのではなく、アプリケーション側でも保護を行うのが安全です。これを多層防御(Defense in Depth)と呼びます。
Django の場合
管理ビューに @staff_member_required デコレータを追加して、ログイン済みのスタッフユーザーのみがアクセスできるようにします。
from django.contrib.admin.views.decorators import staff_member_required
@staff_member_required
def classification_manage(request):
# 管理画面の処理
...
こうすることで、万が一nginxのIP制限が突破されても、Django側でブロックできます。
| 防御レイヤー | 対策 | ブロックする対象 |
|---|---|---|
| ネットワーク層 | nginx IP制限 | 許可IP以外の全てのリクエスト |
| アプリケーション層 | Django @staff_member_required |
未認証・非スタッフユーザー |
動作確認
設定が正しく動作するか確認します。
許可されていないIPからアクセス
curl -s -o /dev/null -w "%{http_code}" http://サーバーのIP/admin/
403 Forbidden が返れば、IP制限が正しく動作しています。
許可されたIPからアクセス
curl -s -o /dev/null -w "%{http_code}" http://サーバーのIP/admin/
200 または 302(ログイン画面へのリダイレクト)が返れば正常です。
一般ページは誰でもアクセスできるか
curl -s -o /dev/null -w "%{http_code}" http://サーバーのIP/
まとめ
nginxのリバースプロキシでIP制限を設定することで、管理画面への不正アクセスをネットワークレベルで防ぐことができます。
- nginxで管理パス(
/admin//manage/)にIP制限をかける - Webアプリケーションは
exposeのみにし、nginxだけを外部公開する .envファイルで許可IPを管理し、変更時はnginxを再起動する- アプリケーション側でも認証を設定して多層防御にする
- TCP 3ウェイハンドシェイクにより、IP偽装での突破は不可能
SSH のセキュリティ対策と合わせて設定すれば、VPS のセキュリティは大幅に向上します。