🕌

ステージングにIP制限を実装 (Next.js & Vercel)

2023/08/02に公開

はじめに

  • 特定の IP からのアクセスを ブロック/許可する簡易方法を紹介します。
  • ステージングのURLなどで、特定の IP からのアクセスを許可する場合などに利用できます。
  • 実装はmiddleware.tsと環境変数.env.localの設定のみで完了します。
  • 作業したコードは以下になります。

https://github.com/hayato94087/nextjs-ip-block-sample

結論

  • middleware.ts.env.localの設定のみで実装できます。
  • 環境変数のIP_WHITE_LISTからホワイトリストに追加されたIPを取得します。
  • 環境変数のACCESS_RESTRICTION_FQDN_LISTからアクセス制限対象のFQDNを取得します。
  • request.ipからアクセス元のIPを取得します。
  • request.nextUrl.hostからアクセス先のFQDNを取得します。
  • アクセス元のIPがホワイトリストに入っていれば、ステージングのURLにアクセスできます。
middleware.ts
// middleware.ts
import { NextResponse, type NextRequest } from "next/server";

// ホワイトリストに登録されたIPリストを取得します。
const ipWhiteList = new Set(
  process.env.IP_WHITE_LIST?.split(",").map((item: string) => {
    return item.trim();
  })
);

// アクセス制限対象のFQDNを取得します。
const accessRestrictionFqdnList = new Set(
  process.env.ACCESS_RESTRICTION_FQDN_LIST?.split(",").map((item: string) => {
    return item.trim();
  })
);

export function middleware(request: NextRequest) {
  if (process.env.NODE_ENV === "production") {
    if (
      !ipWhiteList.has(request.ip as string) &&
      accessRestrictionFqdnList.has(request.nextUrl.host)
    ) {
      console.info(
        `ホワイトリストに追加されていないIPアドレスからアクセスされたため、アクセスを拒否しました。[request.ip = ${request.ip}, request.nextUrl.host = ${request.nextUrl.host}]`
      );
      return new NextResponse(null, { status: 401 });
    } else {
      console.info(
        `ホワイトリストに追加されているIPアドレスからアクセスされました。[request.ip = ${request.ip}, request.nextUrl.host = ${request.nextUrl.host}]`
      );
    }
  }
}

実装

実際に、Next.js, Vercel で実装してみます。

Next.jsプロジェクトの新規作成

作業するプロジェクトを新規に作成していきます。

長いので、折りたたんでおきます。

新規プロジェクト作成と初期環境構築の手順詳細
$ pnpm create next-app@latest nextjs-ip-block-sample --typescript --eslint --import-alias "@/*" --src-dir --use-pnpm --tailwind --app
$ cd nextjs-ip-block-sample

以下の通り不要な設定を削除し、プロジェクトの初期環境を構築します。

$ mkdir src/styles
$ mv src/app/globals.css src/styles/globals.css
src/styles/globals.css
@tailwind base;
@tailwind components;
@tailwind utilities;
src/app/page.tsx
export default function Home() {
  return (
    <main className="text-lg">
      テストページ
    </main>
  )
}
src/app/layout.tsx
import '@/styles/globals.css'

export const metadata = {
  title: "Create Next App",
  description: "Generated by create next app",
};

export default function RootLayout({
  children,
}: {
  children: React.ReactNode;
}) {
  return (
    <html lang="ja">
      <body className="">{children}</body>
    </html>
  );
}
tailwind.config.js
/** @type {import('tailwindcss').Config} */
module.exports = {
  content: [
    "./src/components/**/*.{js,ts,jsx,tsx,mdx}",
    "./src/app/**/*.{js,ts,jsx,tsx,mdx}",
  ],
  plugins: [],
};
tsconfig.json
{
  "compilerOptions": {
    "target": "es5",
    "lib": ["dom", "dom.iterable", "esnext"],
    "allowJs": true,
    "skipLibCheck": true,
    "strict": true,
    "forceConsistentCasingInFileNames": true,
    "noEmit": true,
    "esModuleInterop": true,
    "module": "esnext",
    "moduleResolution": "node",
    "resolveJsonModule": true,
    "isolatedModules": true,
    "jsx": "preserve",
    "incremental": true,
    "plugins": [
      {
        "name": "next"
      }
    ],
+   "baseUrl": ".",
    "paths": {
      "@/*": ["./src/*"],
    }
  },
  "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
  "exclude": ["node_modules"]
}

コミットします。

$ pnpm build
$ git add .
$ git commit -m "新規にプロジェクトを作成し, 作業環境を構築"

環境変数にアクセス制限をかけるステージング環境のFQDNを追加

環境変数にアクセス制限をかけるステージング環境のFQDNを追加します。https://www.google.com の場合、 FGDNはwww.google.comになります。

$ touch .env.local
.env.local

環境変数にステージング環境へのアクセスを許可するIPを追加

環境変数にステージング環境へのアクセスを許可するIPを追加します。

.env.local
STG_DOMAIN_LIST=stg.vercelstudy1akdl.com
+IP_WHITE_LIST=XXX.XX.XXX.XXX, YYYY.YY.YYY.YYY

↑ 実際は自身のIPを追加してください。自分のIPは以下から確認できます。

https://www.cman.jp/network/support/go_access.cgi

特定のIPからのみステージング環境へのアクセスを許可

  • 環境変数のIP_WHITE_LISTからホワイトリストに追加されたIPを取得します。
  • 環境変数のACCESS_RESTRICTION_FQDN_LISTからアクセス制限対象のFQDNを取得します。
  • request.ipからアクセス元のIPを取得します。
  • request.nextUrl.hostからアクセス先のFQDNを取得します。
$ touch src/middleware.ts
middleware.ts
// middleware.ts
import { NextResponse, type NextRequest } from "next/server";

// ホワイトリストに登録されたIPリストを取得します。
const ipWhiteList = new Set(
  process.env.IP_WHITE_LIST?.split(",").map((item: string) => {
    return item.trim();
  })
);

// アクセス制限対象のFQDNを取得します。
const accessRestrictionFqdnList = new Set(
  process.env.ACCESS_RESTRICTION_FQDN_LIST?.split(",").map((item: string) => {
    return item.trim();
  })
);

export function middleware(request: NextRequest) {
  if (process.env.NODE_ENV === "production") {
    if (
      !ipWhiteList.has(request.ip as string) &&
      accessRestrictionFqdnList.has(request.nextUrl.host)
    ) {
      console.info(
        `ホワイトリストに追加されていないIPアドレスからアクセスされたため、アクセスを拒否しました。[request.ip = ${request.ip}, request.nextUrl.host = ${request.nextUrl.host}]`
      );
      return new NextResponse(null, { status: 401 });
    } else {
      console.info(
        `ホワイトリストに追加されているIPアドレスからアクセスされました。[request.ip = ${request.ip}, request.nextUrl.host = ${request.nextUrl.host}]`
      );
    }
  }
}

コミットします。

$ pnpm build
$ git add .
$ git commit -m "middleware.tsを追加"

動作確認

動作確認するためには、Vercel などにデプロイする必要があります。ローカル環境では、pnpm dev あるいは pnpm start のいずれでも、request.ipundefined になってしまうためです。

  1. GitHub にリポジトリを作成します。

https://github.com/hayato94087/nextjs-ip-block-sample

  1. Vercel にデプロイします。

  1. 環境変数を追加します。

  1. ドメインを追加します。自前で取得したドメインを利用しています。

  1. 環境変数を修正したので、再度デプロイします。

  2. ブラウザで確認します。

ホワイトリストに追加されているIPからのアクセスは許可されます。


ホワイトリストに追加されていないIPからのアクセスは拒否されます。


Vercel のログを確認します。

ホワイトリストに追加されているIPからのアクセスは許可されます。

ホワイトリストに追加されていないIPからのアクセスは拒否されます。

まとめ

  • 特定の IP からのアクセスを ブロック/許可する簡易方法を紹介しました。
  • ステージング環境などで、特定の IP からのアクセスを許可する場合などに利用できます。
  • 実装はmiddleware.tsと環境変数の設定のみで完了します。
  • 作業したコードは以下になります。

https://github.com/hayato94087/nextjs-ip-block-sample

Discussion