見出し画像

Webアプリケーションの特定のパスに対し、NGINXでBasic認証を行う


はじめに


こんにちは。SHIFTの片山です。
PoC で開発していた Web アプリを社内で公開することになった際、運営・運用を行う管理者のみが開ける画面を保護したいという要求が出てきた。

アプリケーション側で制御することも考えたが、アプリケーションがユーザー認証なく利用できるタイプであったという点と、PoC であるという点で NGINX をリバースプロキシとして挟んで、NGINX で Basic 認証を行うことを考えた。

今回はどのようにすれば NGINX で Basic 認証を行えるか?についてまとめてみたいと思う。

Web アプリケーションの構成と Deploy 先について


まず今回、Basic 認証を設けようとしているアプリについて軽く紹介する。アプリは SveltKit で実装されたユーザーからの質問に回答する Bot。社内からの問い合わせを過去の Q&A データや FAQ サイトのデータと ChatGPT を組み合わせて、人間への問い合わせを減らす目的で作成したものになる。

以下の図の通り、SvelteKit のアプリは MySQL・Redis に依存するサービスとして実装されている。ただ、PoC という事で低コストで運用できる AWS EC2 上に Deploy し、かつ雑に docker-compose で各依存するサービスを立ち上げる方法を取ることにした。

NGINX で Basic 認証を設定する


NGINX で Basic 認証を行うには、auth_basic ディレクティブを利用する。詳細は公式のリファレンスに記載がある通り。

今回は"/admin"のパスにおいて認証をかけたいので、以下のような設定になった。

server {
    listen 80;
    server_name example.com;

    location / {
        proxy_pass_header Server;
        proxy_set_header X-Forwarded-Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_pass http://app:3000;
    }

    location /admin {
        auth_basic           "Administrator’s Area";
        auth_basic_user_file /etc/apache2/.htpasswd;
        proxy_pass_header Server;
        proxy_set_header X-Forwarded-Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_pass http://app:3000;
    }

    access_log /var/log/nginx/access.log;
    error_log /var/log/nginx/error.log;
}

EC2 の上段には Cloudfront などの CDN を置き、そこまでを HTTPS としていたので NGINX へのリクエストは HTTP で到達する。リクエストは 80 ポートで受け付け、"/admin"のパスの時だけ auth_basic ディレクティブで Basic 認証を有効にしている。

auth_basic_user_file が Basic 認証時の認証情報を持つファイルを指定するディレクティブで、ファイルの中身は以下のようなユーザー名・パスワードを記載したものになっている(以下はテスト用に作成した admin@admin のデータ)。

$ cat config/nginx/.htpasswd
admin:$apr1$L6c5yCmZ$gVMgoZUwFGFqzmUefsagD.

このユーザー名・パスワードの設定ファイルは、以下のように htpasswd コマンドで作成できる。

$ sudo htpasswd -c ./config/nginx/.htpasswd admin
New password:
Re-type new password:
Adding password for user admin

あとは、NGINX をアプリと同時に起動できるように docker-compose.yaml の設定をするだけ。最終的な docker-compose.yaml のイメージとしては以下のようになる。

version: '3.9'
services:
  app:
    container_name: app
    image: app:latest
    build:
      context: .
      dockerfile: Dockerfile
    healthcheck:
      test: ['CMD', 'node', './healthcheck.js']
      interval: 10s
      timeout: 5s
      retries: 3
    depends_on:
      mysql:
        condition: service_healthy
      redis:
        condition: service_healthy
    ports:
      - 3000:3000
  redis:
    image: redis:7.2.1-alpine3.18
    container_name: redis
    healthcheck:
      test: ['CMD', 'redis-cli', 'ping']
      interval: 10s
      timeout: 5s
      retries: 3
    environment:
      TZ: 'Asia/Tokyo'
    volumes:
      - ./data/redis:/data
    ports:
      - 6379:6379
  mysql:
    image: mysql:8.0.32
    container_name: mysql
    healthcheck:
      test: ['CMD', 'mysqladmin', 'ping', '-h', 'localhost']
      interval: 10s
      timeout: 5s
      retries: 3
    environment:
      MYSQL_ROOT_PASSWORD: ''
      MYSQL_ALLOW_EMPTY_PASSWORD: 'yes'
      TZ: 'Asia/Tokyo'
    ports:
      - 3306:3306
    volumes:
      - ./data/mysql:/var/lib/mysql
      - ./mysql/sql:/docker-entrypoint-initdb.d
      - ./mysql/my.cnf:/etc/mysql/conf.d/my.cnf
  nginx:
    image: nginx:latest
    container_name: nginx
    depends_on:
      app:
        condition: service_healthy
    ports:
      - 8080:80
    volumes:
      - ./config/nginx/nginx.conf:/etc/nginx/conf.d/default.conf
      - ./config/nginx/.htpasswd:/etc/apache2/.htpasswd
      - ./data/nginx:/var/log/nginx

docker-compose.yaml の depends_on.○○○.condition の設定は、コンテナで起動するサービスの起動順序を制御するための設定で、詳細はdocker compose で依存するサービスが起動するまで待機するなどを参照ください。今回、同一ネットワークに SvelteKit のアプリも属させることで簡単にアプリが利用できるようにしたかったので、EC2 上で初回の"docker compose up"をした際に SvelteKit のビルドが走るような設定になっていた関係で、NGINX の起動順序などを制御する必要があった。

最終的に Basic 認証を取り入れた後の簡易的な構成図は以下のようになる。

実際にブラウザ上で開いて確認してみると、Basic 認証が行われることが確認できる。

仮にキャンセルすると、401 になることも確認できた。

※nginx.conf の他の設定内容については、「おまけ」の「nginx.conf の他の設定について」を参照ください。

まとめとして


今回はアプリケーション側の改修なく、手軽に Basic 認証を取り入れる方法についてみてきた。アプリで実装するより、上段のリバースプロキシ等で制御をするというアプリケーションエンジニアの領域の外?に踏み入れることで、楽ができることもあるなと感じたので、今後もそういう機能を利用して開発を楽に早くしていくという事をやっていければいいなと思った。

おまけ


nginx.conf の他の設定について

  • proxy_set_header
    これは上段の CDN 等へのリクエスト内容を、プロキシ配下にいるアプリケーションサーバーに伝えるための設定。よくあるのは secure cookie を使うために、X-Forwarded-Proto でプロトコル情報を渡すことだろう。

  • proxy_pass プロキシする先の URL を指定し、NGINX での処理が成功したらリクエストが到達する。

Basic 認証のキャッシュを削除する方法

基本的に、Basic 認証を 1 度行うとブラウザ上でキャッシュされるので、Basic 認証で保護されたパスにアクセスするたびに認証する必要はない。ただ、開発をする際にはあえてログアウト(キャッシュを削除)して再度認証を行いたい場面もあると思う。

その場合の方法については、以下の記事が参考になると思う。


■この公式ブロガーの記事一覧


執筆者プロフィール:Katayama Yuta
認証認可(SHIFTアカウント)や課金決済のプラットフォーム開発に従事。リードエンジニア。 経歴としては、SaaS ERPパッケージベンダーにて開発を2年経験。 SHIFTでは、GUIテストの自動化やUnitテストの実装などテスト関係の案件に従事したり、DevOpsの一環でCICD導入支援をする案件にも従事。その後現在のプラットフォーム開発に参画。

お問合せはお気軽に

SHIFTについて(コーポレートサイト)
https://www.shiftinc.jp/

SHIFTのサービスについて(サービスサイト)
https://service.shiftinc.jp/

SHIFTの導入事例
https://service.shiftinc.jp/case/

お役立ち資料はこちら
https://service.shiftinc.jp/resources/

SHIFTの採用情報はこちら

PHOTO:UnsplashChristopher Gower