朝日ネット 技術者ブログ

朝日ネットのエンジニアによるリレーブログ。今、自分が一番気になるテーマで書きます。

朝日ネットにおけるソフトウエア・ロードバランサーの導入(第3回)

朝日ネットでのインフラ設計・構築を担当しているラピー・ステファンです。 前回の構成説明に続き、今回は具体的な構築について紹介します。

pfSenseの構築

初期インストール

https://www.pfsense.org/download/ からCD ISOまたはUSBメムスティック用インストーラーをダウンロードする。

pfSenseの仮想マシンで立てる場合、下記の設定にします:

  • メモリ:1GB から 2GB (乗るサービスに応じて)
  • OSの種類: FreeBSD 64-bit
  • NIC
    • ドライバー:Intel E1000 (FreeBSDと相性が良い)
    • 3つ準備する:
      • フロントエンド側(WAN)
        • 設定後、該当の仮想インターフェースのポリシーをCARP用に修正
      • 管理側(LAN)
      • バックエンド側(OPT1)

通常の手順に従って、コンソールからOSを入れます。 管理者パスワードもこのタイミングで決めます。 ウェブ管理画面を使うために、LANのみとりあえず設定して、 DHCPで自動的にアドレスを割り当てる様にします。

運用するに当たっての推奨設定とそのテンプレート化

大々的に使う場合、下記を推奨します:

  • 基本設定を済ませた状態の仮想マシンを整備して、「仮想マシンテンプレート」として保存します
    • 認証等(AD連携、管理者グループ等)
    • 参照するNTPサーバー、DNSサーバー
    • 社内で利用する証明局等
    • 社内で簡単に証明書を発行出来るPKI(公開鍵インフラ)がある場合、展開を簡単にするために、「*.admin.internal.example.jp」等のワイルドカード証明書を作り、常備させます
    • 予めパッケージや必要なパッチを適用する
      • VMwareツール
      • HAproxy
      • 管理画面がWANインターフェースにバインドしないためのパッチ(上記のNIC3つ使う構成が前提):
--- pfsense.old/system.inc    2018-09-20 22:02:22.000000000 +0900
+++ pfsense.new/system.inc    2018-10-26 17:04:09.495919000 +0900

@@ -1451,8 +1451,21 @@
     if ($cert <> "" and $key <> "") {
         $nginx_config .= "\n";
         $nginx_config .= "\tserver {\n";
-        $nginx_config .= "\t\tlisten {$nginx_port} ssl http2;\n";
-        $nginx_config .= "\t\tlisten [::]:{$nginx_port} ssl http2;\n";
+        if (($config['interfaces']['lan']['ipaddr'] == "" or
+            $config['interfaces']['lan']['ipaddr'] == "dhcp") and
+            ($config['interfaces']['opt1']['ipaddr'] == "" or
+            $config['interfaces']['opt1']['ipaddr'] == "dhcp")) {
+            $nginx_config .= "\t\tlisten {$nginx_port} ssl http2;\n";
+            $nginx_config .= "\t\tlisten [::]:{$nginx_port} ssl http2;\n";
+        }
+        if ($config['interfaces']['lan']['ipaddr'] <> "" and
+            $config['interfaces']['lan']['ipaddr'] <> "dhcp") {
+            $nginx_config .= "\t\tlisten {$config['interfaces']['lan']['ipaddr']}:{$nginx_port} ssl http2;\n";
+        }
+        if ($config['interfaces']['opt1']['ipaddr'] <> "" and
+            $config['interfaces']['opt1']['ipaddr'] <> "dhcp") {
+            $nginx_config .= "\t\tlisten {$config['interfaces']['opt1']['ipaddr']}:{$nginx_port} ssl http2;\n";
+        }
         $nginx_config .= "\n";
         $nginx_config .= "\t\tssl_certificate         {$g['varetc_path']}/{$cert_location};\n";
         $nginx_config .= "\t\tssl_certificate_key     {$g['varetc_path']}/{$key_location};\n";
@@ -1484,8 +1497,21 @@
     } else {
         $nginx_config .= "\n";
         $nginx_config .= "\tserver {\n";
-        $nginx_config .= "\t\tlisten {$nginx_port};\n";
-        $nginx_config .= "\t\tlisten [::]:{$nginx_port};\n";
+        if (($config['interfaces']['lan']['ipaddr'] == "" or
+            $config['interfaces']['lan']['ipaddr'] == "dhcp") and
+            ($config['interfaces']['opt1']['ipaddr'] == "" or
+            $config['interfaces']['opt1']['ipaddr'] == "dhcp")) {
+            $nginx_config .= "\t\tlisten {$nginx_port};\n";
+            $nginx_config .= "\t\tlisten [::]:{$nginx_port};\n";
+        }
+        if ($config['interfaces']['lan']['ipaddr'] <> "" and
+            $config['interfaces']['lan']['ipaddr'] <> "dhcp") {
+            $nginx_config .= "\t\tlisten {$config['interfaces']['lan']['ipaddr']}:{$nginx_port};\n";
+        }
+        if ($config['interfaces']['opt1']['ipaddr'] <> "" and
+            $config['interfaces']['opt1']['ipaddr'] <> "dhcp") {
+            $nginx_config .= "\t\tlisten {$config['interfaces']['opt1']['ipaddr']}:{$nginx_port};\n";
+        }
     }

     $nginx_config .= <<<EOD
  • Status→System Logs→Settingsで、全体のsyslogサーバーの設定をする
  • Advanced→Firewall & NATで:
    • Firewall Maximum Statesの値が十分(10万以上がまず妥当)
    • Firewall Maximum Table Entriesの値が十分(40万以上がまず妥当)
  • Advanced→Miscellaneous→Cryptographic & Thermal Hardwareで:
    • Cryptographic Hardwareが「AES-NI and BSD Crypto Device」の両方を使う様に設定する
  • Advanced→Notificationsで:
    • メール通知が行ける様に、サーバーと通知先のメールアドレスを設定する
  • HAproxyの事前の微調整:
    • syslogサーバーの設定をする
    • Max SSL Diffie-Hellman size設定を4096にする
    • Global Advanced設定で暗号種類を限定する ssl-default-bind-ciphers EECDH+AESGCM:EDH+AESGCM
    • Internal stats portを2200に定義する
    • Maximum connectionsを定めておく
  • 微調整を経た段階のコンフィグを保存・管理して、簡単に使いまわせる様にする
  • ログインした後に出るダッシュボードに、下記のWidgetを追加する:
    • Interfaces → リンク状態表示
    • Interface Statistics → Packet / Byte / Errorの状態表示
    • Firewall Logs → 直近のブロック等のログ
    • HAProxy → 特にこのwidgetから、バックエンドサーバ−の状態表示だけでなく、簡単にメンテナンスモード切り替えの操作も可能
    • Services Status → 重要プロセスの稼働状態表示
    • CARP Status → MASTER / BACKUP / INIT等の状態表示

2台構成の展開、構成同期の設定

上記の既定設定を反映してテンプレートから、2台の仮想マシンを展開する。 展開後のNIC設定を行います:

  • WAN : 設定なし → 固定のアドレスを割り当てる
  • LAN : DHCP → 固定の管理アドレスを割り当てる
  • OPT1 : 設定なし → 固定のアドレスを割り当てる 1号機の管理画面にアクセスして、System→High Avail. Syncの設定画面でConfiguration Synchronization Settings (XMLRPC Sync)を下記の様に設定する:

  • Synchronize Config to IP : 2号機のLANアドレスを入力する

  • Remote System Username : admin
  • Remote System Password : <設定した管理用パスワード>
  • Select options to sync : 全部選択する 構成を説明している事例においては、pfSenseが担う部分がレイヤー7(L7)の処理のため、pfsyncの設定は不要です。

ゲートウエイやネットワークインターフェースの設定の注意事項

f:id:m-tuchi:20190718163558p:plain
pfSenseロードバランサー設計

ゲートウエイ定義

ゲートウエイは、LANとWANそれぞれ定義します。 なお、デフォルトゲートウエイはLANを採用します。

この構成の理由は、「管理ネットワーク」(LAN)と「サービスネットワーク」(WAN)の通信の切り分けです。 メンテナンス作業の際、WANインターフェースを切ってサービスを受けないようにしても、 pfSenseから発せられた通信はすべてLAN(この場合、管理ネットワーク)から行くことになります。

インターフェース調整

ただし、正しくサービストラフィックを処理するためにWANインターフェースの設定で、IPv4 Upstream gatewayを明示的に定義する必要があります。 そうすることで、HTTP・HTTPSトラフィック等を許可するファイヤウオールのルールを定義する際、自動的に「reply-to (emX 10.0.0.1)」という定義が追加され、 許可されたパケットに対して、返事が入ったルートから行われます。

ポリシーベースルーティングにより、デフォルトゲートウエイがLANでも、WANから入ったサービストラフィックに対する返事はWANインターフェースから出ます。

-pass in quick on em1 inet proto tcp from ! <DenyAccess> to 10.0.0.2 port = http flags S/SA keep state label "USER_RULE: Allow traffic to proxies" 
-pass in quick on em1 inet proto tcp from ! <DenyAccess> to 10.0.0.2 port = https flags S/SA keep state label "USER_RULE: Allow traffic to proxies" 
+pass in quick on em1 reply-to (em1 10.0.0.1) inet proto tcp from ! <DenyAccess> to 10.0.0.2 port = http flags S/SA keep state label "USER_RULE: Allow traffic to proxies" 
+pass in quick on em1 reply-to (em1 10.0.0.1) inet proto tcp from ! <DenyAccess> to 10.0.0.2 port = https flags S/SA keep state label "USER_RULE: Allow traffic to proxies" 

上記事例ですと、WANインターフェースはプライベートアドレスを使い、死活監視も上流装置(ファイヤウオール又はロードバランサー)に行われているので、 インターフェース設定画面の「Block private networks and loopback addresses」を無効にしなければ、正しく動きません。

CARP仮想IPアドレスの設定

CARP を設定する場合は、下記の設定が前提になります:

  • System→High Avail. Syncで1号機から2号機の同期が設定されている
  • 仮想基盤上でWANインターフェースに対して「無差別モード」、「送信パケットの偽造」、「MACアドレスの偽造」を許可する(第1回参照) VMwareの場合、NICの切断をする等してポリシー例外定義は容易に外れる上、複数サービスのトラフィックが集中してしまうと、無駄な負荷が他のサービスにも回ってしまう恐れがある Firewall→Virtual IPsから新規定義をします:
  • Type : CARP
  • Interface : WAN
  • Address : 共有されるアドレス(サブネットマスクの指定も必要)
  • VHIDグループの番号と同期パスワードを定義し、設定する(同じネットワーク内で使用されているものと被ってはならない) 1号機で定義しただけで、2号機に定義が伝播し、自動的に2号機の優先度が低く設定されます。

f:id:slapie:20190718182105p:plain
pfSense CARP冗長設計

ファイヤーウオールのルール定義

管理を簡単にするために、下記の定義を推奨します:

  • CARPに基づいた設計を使う場合、Firewall→Aliases→IPで「VirtualIPs」というアドレス単体のリストを作って、そこにCARPのIPアドレスを入力する (CARPアドレスが複数ある場合、ルールを共通化するためのもの)
  • Firewall→Aliases→IPで「DenyAccess」というネットワークのリストを作って、空っぽにしておく (これは、今後通信を一切遮断したい対象が発生した場合に備えてのもの)
  • Firewall→Aliases→IPで「Crawlers」というネットワークのリストを作って、空っぽにしておく (これは、今後通信にレートリミットをかけたい対象が発生した場合に備えてのもの)
  • Firewall→Aliases→IPで「Monitoring」というネットワークのリストを作って、空っぽにしておく (これは、ICMP死活監視を許可するためのもの、例えば上流の通信装置のファイヤーウオールやロードバランサー等)
  • Firewall→Aliases→Portsで、「HTTPTraffic」というリストを作って、HTTPとHTTPSのポート番号80と443を入力する Firewall→Rules→WANでルールを定義する:

  • 監視通信をCARPアドレス宛に許可するルール:

    • Action / 操作:通過(Pass)
    • Address Family / アドレス形式:IPv4
    • Protocol / プロトコル: ICMP
    • Source / 通信元: Single host or alias → Monitoring
    • Destination / 通信先:
      • CARPの場合: Single host or alias → VirtualIPs
      • そうでない場合: WAN address
  • 通信遮断したい相手からのHTTPトラフィックを拒否するルール:

    • Action / 操作:拒否(Reject)
    • Address Family / アドレス形式:IPv4
    • Protocol / プロトコル: TCP
    • Source / 通信元: Single host or alias → DenyAccess
    • Destination / 通信先:
      • CARPの場合: Single host or alias → VirtualIPs
      • そうでない場合: WAN address
    • Destination Port Range / 通信先ポートレンジ: (other) → HTTPTraffic
  • TCPレートリミットをかけながら、通信許可したい相手(Crawler)からのHTTPトラフィックを許可するルール
    • Action / 操作:通過(Pass)
    • Address Family / アドレス形式:IPv4
    • Protocol / プロトコル: TCP
    • Source / 通信元: Single host or alias → Crawlers
    • Destination / 通信先:
      • CARPの場合: Single host or alias → VirtualIPs
      • そうでない場合: WAN address
    • Destination Port Range / 通信先ポートレンジ: (other) → HTTPTraffic
    • Advanced Options :

      • Max. src. conn. Rate : 100 (ホスト毎の新規接続;こちらはあくまで明らかに過剰なトラフィックの一例)
      • Max. src. conn. Rates : 1 (該当期間、秒単位)
  • 通信許可したい相手からのHTTPトラフィックを許可するルール
    • Action / 操作:通過(Pass)
    • Address Family / アドレス形式:IPv4
    • Protocol / プロトコル: TCP
    • Source / 通信元: Single host or alias → Crawlers
      • Invert matchで条件反転をさせる(Crawlersでないものを許可する)
    • Destination / 通信先:
      • CARPの場合: Single host or alias → VirtualIPs
      • そうでない場合: WAN address
    • Destination Port Range / 通信先ポートレンジ: (other) → HTTPTraffic

次回予告

ここまでで思ったより文量が多くなってしまったため、「CARP+VMware冗長設計の欠点の回避策」については次回の紹介といたします。

採用情報

朝日ネットでは新卒採用・キャリア採用を行っております。