著者はCOVID-19救済基金を選択し、 Write forDOnationsプログラムの一環として寄付を受け取りました。

序章

ファイアウォールは、間違いなくサイバー攻撃に対する最も重要な防御線の1つです。 ファイアウォールを最初から構成する機能は、管理者がネットワークを制御できるようにするスキルを強化します。

Packet Filter(PF)は、セキュリティ主導のOpenBSDプロジェクトによってアップストリームで維持されている有名なファイアウォールアプリケーションです。 パケットフィルタリングツールとしてより正確に表現されているため、この名前が付けられており、シンプルな構文、使いやすさ、豊富な機能で知られています。 PFはデフォルトでステートフルファイアウォールであり、分析目的でアクセスできる状態テーブルに接続に関する情報を格納します。 PFはFreeBSDベースシステムの一部であり、強力な開発者コミュニティによってサポートされています。 カーネルアーキテクチャに関連するPFのFreeBSDバージョンとOpenBSDバージョンの間には違いがありますが、一般的にそれらの構文は似ています。 複雑さに応じて、一般的なルールセットを変更して、比較的少ない労力でどちらのディストリビューションでも機能するようにすることができます。

このチュートリアルでは、PFを備えたFreeBSD12.1サーバー上でファイアウォールをゼロから構築します。 将来のプロジェクトのテンプレートとして使用できる基本ルールセットを設計します。 また、パケットの衛生管理、ブルートフォース防止、監視とロギング、その他のサードパーティツールなどのPFの高度な機能についても説明します。

前提条件

このチュートリアルを開始する前に、次のものが必要です。

  • 1G FreeBSD 12.1サーバー(ZFSまたはUFSのいずれか)。 FreeBSDチュートリアルを使用して、サーバーを希望の構成に設定できます。
  • FreeBSDでは、デフォルトでファイアウォールが有効になっていません。カスタマイズは、FreeBSDの精神の特徴です。 したがって、サーバーを最初に起動するときは、PFの構成中に一時的な保護が必要です。 DigitalOceanを使用している場合は、サーバーを起動した直後にクラウドファイアウォールを有効にできます。 クラウドファイアウォールの構成手順については、DigitalOceanのファイアウォールクイックスタートを参照してください。 別のクラウドプロバイダーを使用している場合は、開始する前に、即時保護への最速ルートを決定してください。 どちらの方法を選択する場合でも、一時ファイアウォールはインバウンドSSHトラフィックのみを許可する必要があり、すべてのタイプのアウトバウンドトラフィックを許可できます。

ステップ1—予備的なルールセットを構築する

このチュートリアルは、基本的な保護とインターネットからの重要なサービスへのアクセスを提供する予備的なルールセットを作成することから始めます。 この時点で、アクティブなクラウドファイアウォールを備えたFreeBSD12.1サーバーが実行されています。

ファイアウォールを構築するには、デフォルトの拒否デフォルトの許可の2つのアプローチがあります。 デフォルトの拒否アプローチはすべてのトラフィックをブロックし、ルールで指定されたものだけを許可します。 デフォルトの許可アプローチは正反対です。すべてのトラフィックを通過させ、ルールで指定されたものだけをブロックします。 デフォルトの拒否アプローチを使用します。

PFルールセットは、/etc/pf.confという名前の構成ファイルに書き込まれます。これはデフォルトの場所でもあります。 /etc/rc.conf構成ファイルで指定されている限り、このファイルを別の場所に保存してもかまいません。 このチュートリアルでは、デフォルトの場所を使用します。

root以外のユーザーでサーバーにログインします。

  1. ssh [email protected]your_server_ip

次に、/etc/pf.confファイルを作成します。

  1. sudo vi /etc/pf.conf

注:チュートリアルの任意の時点で完全な基本ルールセットを確認したい場合は、ステップ4またはステップ8の例を参照してください。

PFは、blockpass、およびmatchの3つのコアアクションに従ってパケットをフィルタリングします。 他のオプションと組み合わせると、ルールが形成されます。 パケットがルールで指定された基準を満たすと、アクションが実行されます。 ご想像のとおり、passおよびblockルールはpassおよびblockトラフィックになります。 matchルールは、一致する基準を見つけたときにパケットに対してアクションを実行しますが、それを通過またはブロックしません。 たとえば、一致するパケットに対してネットワークアドレス変換(NAT)を渡すこともブロックすることもせずに実行でき、別のルールにルーティングするなど、別のルールで何かを行うように指示するまでそこに留まります。マシンまたはゲートウェイ。

次に、最初のルールを/etc/pf.confファイルに追加します。

/etc/pf.conf
block all

このルールは、あらゆる方向のあらゆる形態のトラフィックをブロックします。 方向を指定しないため、デフォルトではinoutの両方になります。 このルールは、世界から隔離する必要があるローカルワークステーションには有効ですが、ほとんど実用的ではなく、SSHトラフィックを許可しないため、リモートサーバーでは機能しません。 実際、PFを有効にした場合は、サーバーから自分自身をロックアウトしていたでしょう。

/etc/pf.confファイルを修正して、次の強調表示された行でSSHトラフィックを許可します。

/etc/pf.conf
block all
pass in proto tcp to port 22

注:または、プロトコルの名前を使用することもできます。

/etc/pf.conf
block all
pass in proto tcp to port ssh

一貫性を保つために、正当な理由がない限り、ポート番号を使用します。 /etc/servicesファイルには、プロトコルとそれぞれのポート番号の詳細なリストがあり、表示することをお勧めします。

PFはルールを上から下に順番に処理するため、現在のルールセットは最初にすべてのトラフィックをブロックしますが、次の行の基準(この場合はSSHトラフィック)が一致すると、ルールセットを渡します。

これでサーバーにSSHで接続できますが、それでもすべての形式のアウトバウンドトラフィックをブロックしています。 これは、インターネットから重要なサービスにアクセスしてパッケージをインストールしたり、時間設定を更新したりすることができないため、問題があります。

これに対処するには、/etc/pf.confファイルの最後に次の強調表示されたルールを追加します。

/etc/pf.conf
block all
pass in proto tcp to port { 22 }
pass out proto { tcp udp } to port { 22 53 80 123 443 }

ルールセットは、アウトバウンド SSH DNS HTTP NTP 、およびHTTPSトラフィックを許可し、すべてのインワードトラフィックをブロックするようになりました。 SSHを除く)。 ポート番号とプロトコルを中括弧内に配置します。これにより、PF構文のリストが形成され、必要に応じてポート番号を追加できます。 また、DNSとNTPはTCPプロトコルとUDPプロトコルの両方を切り替えることが多いため、ポート53123にUDPプロトコルのパスアウトルールを追加します。 予備のルールセットはほぼ完成しました。基本的な機能を実現するには、いくつかのルールを追加するだけです。

強調表示されたルールを使用して予備ルールセットを完成させます。

予備ルールセット/etc/pf.conf
set skip on lo0
block all
pass in proto tcp to port { 22 }
pass out proto { tcp udp } to port { 22 53 80 123 443 }
pass out inet proto icmp icmp-type { echoreq }

ファイルを保存して終了します。

ループバックデバイスにはset skipルールを作成します。これは、トラフィックをフィルタリングする必要がなく、サーバーをクロールする可能性があるためです。 ICMPプロトコルにpass out inetルールを追加します。これにより、トラブルシューティングに ping(8)ユーティリティを使用できます。 inetオプションは、IPv4アドレスファミリーを表します。

ICMPは、さまざまなタイプの通信のためにネットワークデバイスによって使用される多目的メッセージングプロトコルです。 たとえば、pingユーティリティは、icmp_typeリストに追加したechorequestと呼ばれるタイプのメッセージを使用します。 予防措置として、歓迎されないデバイスがサーバーに接続するのを防ぐために必要なメッセージタイプのみを許可します。 ニーズが増えるにつれて、リストにメッセージタイプを追加できます。

これで、ほとんどのマシンに基本的な機能を提供する実用的なルールセットができました。 次のセクションでは、PFを有効にして予備のルールセットをテストすることにより、すべてが正しく機能していることを確認しましょう。

ステップ2—予備ルールセットをテストする

このステップでは、予備のルールセットをテストし、クラウドファイアウォールからPFファイアウォールに移行して、PFが完全に引き継ぐことができるようにします。 PFの組み込みコマンド行ツールであるpfctlユーティリティー、およびPFとのインターフェースの主要な方法を使用して、ルールセットをアクティブ化します。

PFルールセットはテキストファイルにすぎません。つまり、新しいルールセットのロードに伴う繊細な手順はありません。 新しいルールセットをロードでき、古いルールセットはなくなります。 既存のルールセットをフラッシュする必要はほとんどありません。

FreeBSDは、 rcシステムと呼ばれるシェルスクリプトのWebを使用して、起動時にサービスを開始する方法を管理します。 これらのサービスは、さまざまなrc構成ファイルで指定します。 PFなどのグローバルサービスの場合は、/etc/rc.confファイルを使用します。 rcファイルはFreeBSDシステムの幸福にとって重要であるため、直接編集しないでください。 代わりに、FreeBSDは、これらのファイルを安全に編集するのに役立つように設計されたsysrcと呼ばれるコマンドラインユーティリティを提供します。

sysrcコマンドラインユーティリティを使用してPFを有効にしましょう。

  1. sudo sysrc pf_enable="YES"
  2. sudo sysrc pflog_enable="YES"

/etc/rc.confファイルの内容を印刷して、これらの変更を確認します。

  1. sudo cat /etc/rc.conf

次の出力が表示されます。

Output
pf_enable="YES" pflog_enable="YES"

また、pflogサービスを有効にします。これにより、pflogdデーモンがPFにログインできるようになります(後の手順でログインを使用します)。

/etc/rc.confファイルで2つのグローバルサービスを指定しますが、サーバーを再起動するか手動で起動するまで、それらは初期化されません。 サーバーを再起動して、SSHアクセスもテストできるようにします。

サーバーを再起動してPFを開始します。

  1. sudo reboot

接続が切断されます。 更新するのに数分かかります。

次に、SSHでサーバーに戻ります。

  1. ssh [email protected]your_server_ip

PFサービスを初期化しましたが、実際には/etc/pf.confルールセットをロードしていません。つまり、ファイアウォールはまだアクティブではありません。

pfctlを使用してルールセットをロードします。

  1. sudo pfctl -f /etc/pf.conf

エラーやメッセージがない場合は、ルールセットにエラーがなく、ファイアウォールがアクティブになっていることを意味します。

PFが実行されたので、サーバーをクラウドファイアウォールから切り離すことができます。 これは、DigitalOceanアカウントのコントロールパネルで、クラウドファイアウォールのポータルからDropletを削除することで実行できます。 別のクラウドプロバイダーを使用している場合は、一時的な保護に使用しているものがすべて無効になっていることを確認してください。 サーバーで2つの異なるファイアウォールを実行すると、ほぼ確実に問題が発生します。

適切な方法として、サーバーを再起動します。

  1. sudo reboot

数分後、SSHでサーバーに戻ります。

  1. ssh [email protected]your_server_ip

これで、PFがファイアウォールとして機能します。 pfctlユーティリティーを使用していくつかのデータにアクセスすることにより、それが実行されていることを確認できます。

pfctl -siを使用していくつかの統計とカウンターを表示してみましょう。

  1. sudo pfctl -si

showinfoを表す-siフラグを渡します。 これは、ファイアウォールアクティビティに関するデータを解析するためにpfctlで使用できる多くのフィルターパラメーターの組み合わせの1つです。

次の表形式のデータが表示されます(値はマシンごとに異なります)。

Output
Status: Enabled for 0 days 00:01:53 Debug: Urgent State Table Total Rate current entries 5 searches 144 1.3/s inserts 11 0.1/s removals 6 0.1/s Counters match 23 0.2/s bad-offset 0 0.0/s fragment 0 0.0/s short 0 0.0/s normalize 0 0.0/s memory 0 0.0/s bad-timestamp 0 0.0/s congestion 0 0.0/s ip-option 0 0.0/s proto-cksum 0 0.0/s state-insert 0 0.0/s state-limit 0 0.0/s src-limit 0 0.0/s synproxy 0 0.0/s map-failed 0 0.0/s

ルールセットをアクティブ化したばかりなので、まだ多くの情報は表示されません。 ただし、この出力は、PFがすでに23の一致したルールを記録したことを示しています。これは、ルールセットの基準が23回一致したことを意味します。 出力は、ファイアウォールが機能していることも確認します。

ルールセットは、pingユーティリティを含むインターネットからのいくつかの重要なサービスにアウトバウンドトラフィックがアクセスすることも許可します。

google.comに対してpingを実行して、インターネット接続とDNSサービスを確認しましょう。

  1. ping -c 3 google.com

カウントフラグ-c 3を実行したので、3つの成功した接続応答が表示されます。

Output
PING google.com (172.217.0.46): 56 data bytes 64 bytes from 172.217.0.46: icmp_seq=0 ttl=56 time=2.088 ms 64 bytes from 172.217.0.46: icmp_seq=1 ttl=56 time=1.469 ms 64 bytes from 172.217.0.46: icmp_seq=2 ttl=56 time=1.466 ms --- google.com ping statistics --- 3 packets transmitted, 3 packets received, 0.0% packet loss round-trip min/avg/max/stddev = 1.466/1.674/2.088/0.293 ms

次のコマンドを使用して、pkgsリポジトリにアクセスできることを確認します。

  1. sudo pkg upgrade

アップグレードするパッケージがある場合は、先に進んでアップグレードしてください。

これらのサービスの両方が機能している場合は、ファイアウォールが機能していることを意味し、続行できます。 予備のルールセットは保護と機能を提供しますが、それでも基本的なルールセットであり、いくつかの拡張機能を使用できます。 残りのセクションでは、基本ルールセットを完成させ、PFの高度な機能のいくつかを使用します。

ステップ3—基本ルールセットを完成させる

このステップでは、予備ルールセットを構築して、基本ルールセットを完成させます。 ルールの一部を再編成し、より高度な概念で作業します。

マクロとテーブルの組み込み

予備のルールセットでは、すべてのパラメーターを各ルール、つまりリストを構成するポート番号にハードコーディングしました。 ネットワークの性質によっては、これは将来管理できなくなる可能性があります。 組織的な目的で、PFにはマクロリスト、およびテーブルが含まれます。 すでにルールに直接リストを含めていますが、それらをルールから分離し、マクロを使用して変数に割り当てることもできます。

ファイルを開いて、パラメータの一部をマクロに転送します。

  1. sudo vi /etc/pf.conf

次に、ルールセットの最上部に次のコンテンツを追加します。

/etc/pf.conf
vtnet0 = "vtnet0"
icmp_types = "{ echoreq }"
. . .

以前のSSHおよびICMPルールを新しい変数で変更します。

/etc/pf.conf
. . .
pass in on $vtnet0 proto tcp to port { 22 }
. . .
pass inet proto icmp icmp-type $icmp_types
. . .

以前のSSHおよびICMPルールはマクロを使用するようになりました。 変数名は、PFのドル記号構文で示されます。 vtnet0インターフェースを、形式と同じ名前の変数に割り当てます。これにより、必要に応じて、将来名前を変更することができます。 公開インターフェースのその他の一般的な変数名には、$pub_ifまたは$ext_ifがあります。

次に、 table を実装します。これは、 macro に似ていますが、IPアドレスのグループを保持するように設計されています。 サービス拒否攻撃(DOS)でしばしば役割を果たす、ルーティング不可能なIPアドレスのテーブルを作成しましょう。 RFC6890 で指定されたIPアドレスを使用できます。これは、専用のIPアドレスレジストリを定義します。 サーバーは、公開インターフェースを介してこれらのアドレスとの間でパケットを送受信しないでください。

icmp_typesマクロのすぐ下に次のコンテンツを追加して、このテーブルを作成します。

/etc/pf.conf
. . .
table <rfc6890> { 0.0.0.0/8 10.0.0.0/8 100.64.0.0/10 127.0.0.0/8 169.254.0.0/16          \
                  172.16.0.0/12 192.0.0.0/24 192.0.0.0/29 192.0.2.0/24 192.88.99.0/24    \
                  192.168.0.0/16 198.18.0.0/15 198.51.100.0/24 203.0.113.0/24            \
                  240.0.0.0/4 255.255.255.255/32 }
. . .

次に、set skip on lo0ルールの下に<rfc6890>テーブルのルールを追加します。

/etc/pf.conf
. . .
set skip on lo0
block in quick on egress from <rfc6890>
block return out quick on egress to <rfc6890>
. . .

ここでは、block outルールを補完するreturnオプションを紹介します。 これにより、パケットがドロップされ、 RSTメッセージがそれらの接続を試みたホストに送信されます。これは、ホストアクティビティの分析に役立ちます。 次に、egressキーワードを追加します。これにより、任意のインターフェイスでデフォルトルートが自動的に検出されます。 これは通常、特に複雑なネットワークでは、デフォルトルートを見つけるためのよりクリーンな方法です。 quickキーワードは、残りのルールセットを考慮せずにルールをすぐに実行します。 たとえば、非論理的なIPアドレスを持つパケットがサーバーに接続しようとした場合、接続をすぐに切断する必要があり、そのパケットを残りのルールセットで実行する理由はありません。

SSHポートの保護

SSHポートは一般に公開されているため、悪用される可能性があります。 攻撃者のより明白な警告サインの1つは、大量のログイン試行です。 たとえば、同じIPアドレスが1秒間に10回サーバーにログインしようとした場合、それは人間の手ではなく、ログインパスワードを解読しようとしたコンピューターソフトウェアを使用して行われたと見なすことができます。 これらのタイプの体系的なエクスプロイトは、ブルートフォース攻撃と呼ばれることが多く、通常、サーバーのパスワードが弱い場合に成功します。

警告:すべてのサーバーで公開鍵認証を使用することを強くお勧めします。 キーベースの認証に関するDigitalOceanのチュートリアルを参照してください。

PFには、ブルートフォースやその他の同様の攻撃を処理するための機能が組み込まれています。 PFを使用すると、単一のホストで許可される同時接続試行の数を制限できます。 ホストがこれらの制限を超えると、接続が切断され、サーバーからの接続が禁止されます。 これを実現するには、禁止されたIPアドレスのテーブルを維持するPFのオーバーロードメカニズムを使用します。

次のように、以前のSSHルールを変更して、単一のホストからの同時接続の数を制限します。

/etc/pf.conf
. . .
pass in on $vtnet0 proto tcp to port { 22 } \
    keep state (max-src-conn 15, max-src-conn-rate 3/1, \
        overload <bruteforce> flush global)
. . .

keep stateオプションを追加して、過負荷テーブルの状態基準を定義できるようにします。 max-src-connパラメーターを渡して、1秒あたりの単一ホストから許可される同時接続の数を指定し、max-src-conn-rateパラメーターを渡して、1秒あたりの単一ホストから許可される新しい接続の数を指定します。 max-src-connには15接続を指定し、max-src-conn-rateには3接続を指定します。 ホストがこれらの制限を超えた場合、overloadメカニズムはソースIPを<bruteforce>テーブルに追加し、サーバーからの制限を禁止します。 最後に、flush globalオプションはすぐに接続を切断します。

SSHルールでオーバーロードテーブルを定義しましたが、ルールセットでそのテーブルを宣言していません。

icmp_typesマクロの下に<bruteforce>テーブルを追加します。

/etc/pf.conf
. . .
icmp_types = "{ echoreq }"
table <bruteforce> persist
. . .

persistキーワードを使用すると、ルールセットに空のテーブルを含めることができます。 これがないと、PFはテーブルにIPアドレスがないと文句を言います。

これらの対策により、SSHポートが強力なセキュリティメカニズムによって確実に保護されます。 PFを使用すると、悲惨な形の悪用から保護するための迅速なソリューションを構成できます。 次のセクションでは、パケットがサーバーに到着したときにパケットをクリーンアップする手順を実行します。

トラフィックのサニタイズ

注:次のセクションでは、TCP/IPプロトコルスイートの基本的な基本事項について説明します。 Webアプリケーションまたはネットワークの構築を計画している場合は、これらの概念を習得することが最も重要です。 DigitalOceanのネットワーク用語、インターフェイス、およびプロトコルの概要チュートリアルをご覧ください。

TCP / IPプロトコルスイートの複雑さと悪意のある攻撃者の忍耐力により、パケットは、IPフラグメントの重複、偽のIPアドレスなどの不一致やあいまいさを伴って到着することがよくあります。 トラフィックがシステムに入る前に、トラフィックをサニタイズすることが不可欠です。 このプロセスの専門用語は、正規化です。

データがインターネットを通過するとき、データは通常、ソースで小さなフラグメントに分割され、ターゲットホストの送信パラメータに対応し、完全なパケットに再構成されます。 残念ながら、侵入者はこのチュートリアルの範囲を超えるさまざまな方法でこのプロセスを乗っ取る可能性があります。 ただし、PFを使用すると、1つのルールで断片化を管理できます。 PFには、パケットを正規化するscrubキーワードが含まれています。

block allルールの直前にscrubキーワードを追加します。

/etc/pf.conf
. . .
set skip on lo0
scrub in all fragment reassemble max-mss 1440
block all
. . .

このルールは、すべての着信トラフィックにスクラビングを適用します。 フラグメントがシステムに入るのを防ぐfragment reassembleオプションを含めます。 代わりに、完全なパケットに再アセンブルされるまでメモリにキャッシュされます。つまり、フィルタルールは均一なパケットとのみ競合する必要があります。 また、max-mss 1440オプションを含めます。これは、ペイロードとも呼ばれる、再構成されたTCPパケットの最大セグメントサイズを表します。 サイズとパフォーマンスのバランスを取り、ヘッダー用に十分なスペースを残して、1440バイトの値を指定します。

フラグメンテーションのもう1つの重要な側面は、最大伝送ユニット(MTU)として知られる用語です。 TCP / IPプロトコルを使用すると、デバイスは接続を確立するためにパケットサイズをネゴシエートできます。 ターゲットホストはICMPメッセージを使用して、ソースIPにMTUを通知します。これは、MTUパス検出と呼ばれるプロセスです。 特定のICMPメッセージタイプは、宛先到達不能です。 unreachメッセージタイプをicmp_typesリストに追加することにより、MTUパス検出を有効にします。

サーバーのデフォルトのMTUである1500バイトを使用します。これは、ifconfigコマンドで決定できます。

  1. ifconfig

現在のMTUを含む次の出力が表示されます。

Output
vtnet0: flags=8843<UP,BROADCAST,RUNNING,SIMPLEX,MULTICAST> metric 0 mtu 1500 options=6c07bb<RXCSUM,TXCSUM,VLAN_MTU,VLAN_HWTAGGING,JUMBO_MTU,VLAN_HWCSUM,TSO4,TSO6,LRO,VLAN_HWTSO,LINKSTATE,RXCSUM_IPV6,TXCSUM_IPV6> . . .

icmp_typesリストを更新して、 destinationunreachableメッセージタイプを含めます。

/etc/pf.conf
vtnet0 = "vtnet0"
icmp_types = "{ echoreq unreach}"
. . .

断片化を処理するためのポリシーが設定されたので、システムに入るパケットは均一で一貫性があります。 インターネットを介してデータを交換するデバイスが非常に多いため、これは望ましいことです。

ここで、IPスプーフィングと呼ばれる別のセキュリティ上の懸念を防ぐために作業します。 攻撃者は、ソースIPを変更して、組織内の信頼できるノードに存在するように見せかけることがよくあります。 PFには、スプーフィングされたソースIPを処理するためのantispoofingディレクティブが含まれています。 特定のインターフェイスに適用されると、スプーフィング防止は、そのインターフェイスのネットワークからのすべてのトラフィックをブロックします(そのインターフェイスから発信された場合を除く)。 たとえば、5.5.5.1/24にあるインターフェイスにスプーフィング防止を適用すると、5.5.5.0/24ネットワークからのすべてのトラフィックは、そのインターフェイスから発信されない限り、システムと通信できません。

次の強調表示されたコンテンツを追加して、vtnet0インターフェイスにスプーフィング防止を適用します。

/etc/pf.conf
. . .
set skip on lo0
scrub in
antispoof quick for $vtnet0
block all
. . .

ファイルを保存して終了します。

このなりすまし防止ルールは、vtnet0のネットワークからのすべてのトラフィックは、vtnet0インターフェイスのみを通過できるか、quickキーワードですぐにドロップされることを示しています。 悪意のある攻撃者は、vtnet0のネットワークに隠れて、他のノードと通信することはできません。

なりすまし防止ルールを示すために、ルールセットを詳細な形式で画面に印刷します。 PFのルールは通常、短縮形で記述されますが、詳細形式で記述されることもあります。 この方法でルールを作成することは一般的に実用的ではありませんが、テストの目的で役立つ場合があります。

次のコマンドでpfctlを使用して、/etc/pf.confの内容を印刷します。

  1. sudo pfctl -nvf /etc/pf.conf

このpfctlコマンドは、-nvfフラグを受け取ります。このフラグは、ルールセットを出力し、実際には何もロードせずにテストします。これは、ドライランとも呼ばれます。 これで、/etc/pf.confの内容全体が詳細な形式で表示されます。

なりすまし防止部分には、次のような出力が表示されます。

Output
. . . block drop in quick on ! vtnet0 inet from your_server_ip/20 to any block drop in quick on ! vtnet0 inet from network_address/16 to any block drop in quick inet from your_server_ip to any block drop in quick inet from network_address to any block drop in quick on vtnet0 inet6 from your_IPv6_address to any . . .

なりすまし防止ルールにより、それがyour_server_ip/20ネットワークの一部であることがわかりました。 また、(このチュートリアルの例では)サーバーがnetwork_address/16ネットワークの一部であり、追加のIPv6アドレスを持っていることも検出しました。 スプーフィング防止は、トラフィックがvtnet0インターフェイスを通過しない限り、これらすべてのネットワークがシステムと通信するのをブロックします。

なりすまし防止ルールは、基本ルールセットへの最後の追加です。 次のステップでは、これらの変更を開始し、いくつかのテストを実行します。

ステップ4—基本ルールセットのテスト

このステップでは、基本ルールセットを確認およびテストして、すべてが正しく機能していることを確認します。 テストせずに一度に多くのルールを実装することは避けるのが最善です。 ベストプラクティスは、基本事項から始めて、段階的に拡張し、構成を変更しながら作業をバックアップすることです。

完全な基本ルールセットは次のとおりです。

基本ルールセット/etc/pf.conf
vtnet0 = "vtnet0"
icmp_types = "{ echoreq unreach }"
table <bruteforce> persist
table <rfc6890> { 0.0.0.0/8 10.0.0.0/8 100.64.0.0/10 127.0.0.0/8 169.254.0.0/16          \
                  172.16.0.0/12 192.0.0.0/24 192.0.0.0/29 192.0.2.0/24 192.88.99.0/24    \
                  192.168.0.0/16 198.18.0.0/15 198.51.100.0/24 203.0.113.0/24            \
                  240.0.0.0/4 255.255.255.255/32 }

set skip on lo0
scrub in all fragment reassemble max-mss 1440
antispoof quick for $vtnet0
block in quick on $vtnet0 from <rfc6890>
block return out quick on egress to <rfc6890>
block all
pass in on $vtnet0 proto tcp to port { 22 } \
    keep state (max-src-conn 15, max-src-conn-rate 3/1, \
        overload <bruteforce> flush global)
pass out proto { tcp udp } to port { 22 53 80 123 443 }
pass inet proto icmp icmp-type $icmp_types

続行する前に、/etc/pf.confファイルがここにある完全な基本ルールセットと同一であることを確認してください。 次に、ファイルを保存して終了します。

完全な基本ルールセットは、次のものを提供します。

  • 主要なサービスとデバイスを定義できるマクロのコレクション。
  • パケットの断片化と非論理的なIPアドレスに対処するためのネットワーク衛生ポリシー。
  • デフォルトのdenyフィルタリング構造。すべてをブロックし、指定したものだけを許可します。
  • ホストが作成できる同時接続の数に制限があるインバウンドSSHアクセス。
  • インターネットからいくつかの重要なサービスへのアクセスを提供するアウトバウンドトラフィックポリシー。
  • pingユーティリティとMTUパス検出へのアクセスを提供するICMPポリシー。

次のpfctlコマンドを実行して、ドライランを実行します。

  1. sudo pfctl -nf /etc/pf.conf

ルールセットをロードせずに実行するようにpfctlに指示する-nfフラグを渡します。これにより、何か問題がある場合にエラーがスローされます。

ここで、エラーが発生することなく、ルールセットをロードします。

  1. sudo pfctl -f /etc/pf.conf

エラーがない場合は、基本ルールセットがアクティブであり、正しく機能していることを意味します。 チュートリアルの前半と同様に、ルールセットに対していくつかのテストを実行します。

インターネット接続とDNSサービスの最初のテスト:

  1. ping -c 3 google.com

次の出力が表示されます。

Output
PING google.com (172.217.0.46): 56 data bytes 64 bytes from 172.217.0.46: icmp_seq=0 ttl=56 time=2.088 ms 64 bytes from 172.217.0.46: icmp_seq=1 ttl=56 time=1.469 ms 64 bytes from 172.217.0.46: icmp_seq=2 ttl=56 time=1.466 ms --- google.com ping statistics --- 3 packets transmitted, 3 packets received, 0.0% packet loss round-trip min/avg/max/stddev = 1.466/1.674/2.088/0.293 ms

次に、pkgsリポジトリに到達したことを確認します。

  1. sudo pkg upgrade

もう一度、必要に応じてパッケージをアップグレードします。

最後に、サーバーを再起動します。

  1. sudo reboot

サーバーを数分待ってから再起動します。 基本ルールセットを完成させて実装しました。これは、進捗状況の観点から重要なステップです。 これで、PFの高度な機能のいくつかを探索する準備が整いました。 次のステップでは、ブルートフォース攻撃を引き続き防止します。

ステップ5—過負荷テーブルの管理

時間の経過とともに、<bruteforce>過負荷テーブルは悪意のあるIPアドレスでいっぱいになり、定期的にクリアする必要があります。 攻撃者が同じIPアドレスを使用し続ける可能性は低いため、それらを過負荷テーブルに長期間保存することは直感に反します。

pfctlを使用して、次のコマンドを使用して、過負荷テーブルに48時間以上保存されているIPアドレスを手動でクリアします。

  1. sudo pfctl -t bruteforce -T expire 172800

次のような出力が表示されます。

Output
0/0 addresses expired.

table bruteforceを表す-t bruteforceフラグと、いくつかの組み込みコマンドを実行できる-Tフラグを渡します。 この場合、expireコマンドを実行して、-t bruteforceからすべてのエントリを秒単位の時間値でクリアします。 新しいサーバーで作業しているため、過負荷テーブルにはまだIPアドレスがない可能性があります。

このルールは迅速な修正に有効ですが、より堅牢な解決策は、FreeBSDのジョブスケジューラであるcronを使用してプロセスを自動化することです。 代わりに、このコマンドシーケンスを実行するシェルスクリプトを作成しましょう。

/usr/local/binディレクトリにシェルスクリプトファイルを作成します。

  1. sudo vi /usr/local/bin/clear_overload.sh

次のコンテンツをシェルスクリプトに追加します。

/usr/local/bin/clear_overload.sh
#!/bin/sh

pfctl -t bruteforce -T expire 172800

次のコマンドを使用してファイルを実行可能にします。

  1. sudo chmod 755 /usr/local/bin/clear_overload.sh

次に、cronジョブを作成します。 これらは、指定した時間に従って繰り返し実行されるジョブです。 これらは通常、バックアップ、または毎日同時に実行する必要のあるプロセスに使用されます。 crontabファイルを使用してcronジョブを作成します。 cron(8)および crontab(5)の詳細については、マニュアルページを参照してください。

次のコマンドを使用して、rootユーザーのcrontabファイルを作成します。

  1. sudo crontab -e

次に、次の内容をcrontabファイルに追加します。

crontab
# minute	hour	mday	month	wday	command

  *				0     *       *     *	  /usr/local/bin/clear_overload.sh

ファイルを保存して終了します。

注:コンテンツを追加するときに物事が正しく整列しない場合は、読みやすくするために、すべての値を対応するテーブルエントリに整列させてください。

このcronジョブは、毎日深夜にclear_overload.shスクリプトを実行し、オーバーロードテーブル<bruteforce>から48時間経過したIPアドレスを削除します。 次に、ルールセットにアンカーを追加します。

ステップ6—ルールセットにアンカーを導入する

このステップでは、アンカーを紹介します。これは、手動または外部テキストファイルからメインルールセットにルールをソーシングするために使用されます。 アンカーには、ルールスニペット、テーブル、さらにはネストされたアンカーと呼ばれる他のアンカーを含めることができます。 テーブルを外部ファイルに追加し、それを基本ルールセットにソースすることによって、アンカーがどのように機能するかを示しましょう。 テーブルには、外部への接続を防止する内部ホストのグループが含まれます。

/etc/blocked-hosts-anchorという名前のファイルを作成します。

  1. sudo vi /etc/blocked-hosts-anchor

次の内容をファイルに追加します。

/ etc /blocked-hosts-anchor
table <blocked-hosts> { 192.168.47.1 192.168.47.2 192.168.47.3 }

block return out quick on egress from <blocked-hosts>

ファイルを保存して終了します。

これらのルールは、<blocked-hosts>テーブルを宣言および定義し、<blocked-hosts>テーブル内のすべてのIPアドレスが外部からのサービスにアクセスするのを防ぎます。 egressキーワードは、インターネットへのデフォルトルートまたはルートを見つけるための推奨される方法として使用します。

/etc/pf.confファイルでアンカーを宣言する必要があります。

  1. sudo vi /etc/pf.conf

次に、block allルールの後に次のアンカールールを追加します。

/etc/pf.conf
. . .
block all
anchor blocked_hosts
load anchor blocked_hosts from "/etc/blocked-hosts-anchor"
. . .

ファイルを保存して終了します。

これらのルールはblocked_hostsを宣言し、/etc/blocked-hosts-anchorファイルからメインルールセットにアンカールールをロードします。

次に、pfctlを使用してルールセットをリロードして、これらの変更を開始します。

  1. sudo pfctl -f /etc/pf.conf

エラーがない場合は、ルールセットにエラーがなく、変更がアクティブであることを意味します。

pfctlを使用して、アンカーが実行されていることを確認します。

  1. sudo pfctl -s Anchors

-s Anchorsフラグは、「アンカーの表示」を表します。 次の出力が表示されます。

Output
blocked_hosts

pfctlユーティリティは、-aおよび-sフラグを使用してアンカーの特定のルールを解析することもできます。

  1. sudo pfctl -a blocked_hosts -s rules

次の出力が表示されます。

Output
block return out quick on egress from <blocked-hosts> to any

アンカーのもう1つの機能は、ルールセットをリロードしなくても、オンデマンドでルールを追加できることです。 これは、テスト、クイックフィックス、緊急事態などに役立ちます。 たとえば、内部ホストが特殊な動作をしていて、外部接続をブロックしたい場合は、コマンドラインからすばやく介入できるアンカーを配置できます。

/etc/pf.confを開いて、別のアンカーを追加しましょう。

  1. sudo vi /etc/pf.conf

アンカーにrogue_hostsという名前を付け、block allルールに配置します。

/etc/pf.conf
. . .
block all
anchor rogue_hosts
. . .

ファイルを保存して終了します。

これらの変更を開始するには、pfctlを使用してルールセットをリロードします。

  1. sudo pfctl -f /etc/pf.conf

もう一度、pfctlを使用して、アンカーが実行されていることを確認します。

  1. sudo pfctl -s Anchors

これにより、次の出力が生成されます。

Output
blocked_hosts rogue_hosts

アンカーが実行されているので、いつでもルールを追加できます。 次のルールを追加して、これをテストします。

  1. sudo sh -c 'echo "block return out quick on egress from 192.168.47.4" | pfctl -a rogue_hosts -f -'

これにより、echoコマンドとその文字列コンテンツが呼び出され、|シンボルを使用してpfctlユーティリティにパイプされ、アンカールールに処理されます。 sh -cコマンドを使用して別のシェルセッションを開きます。 これは、2つのプロセス間にパイプを確立するが、コマンドシーケンス全体を通して持続するためにsudo特権が必要なためです。 これを解決するには複数の方法があります。 ここでは、 sudo sh -cを使用して、sudo権限で追加のシェルプロセスを開きます。

ここで、pfctlを再度使用して、これらのルールがアクティブであることを確認します。

  1. sudo pfctl -a rogue_hosts -s rules

これにより、次の出力が生成されます。

Output
block return out quick on egress inet from 192.168.47.4 to any

アンカーの使用は完全に状況に応じて行われ、多くの場合主観的です。 他の機能と同様に、アンカーを使用することには賛否両論があります。 blacklistd などの一部のアプリケーションは、設計上アンカーとインターフェースします。 次に、ネットワークセキュリティの重要な側面であるPFを使用したロギングに焦点を当てます。 ファイアウォールが何をしているのかがわからない場合、ファイアウォールは役に立ちません。

ステップ7—ファイアウォールのアクティビティをログに記録する

このステップでは、pflogという名前の疑似インターフェイスによって管理されるPFロギングを操作します。 手順2で行った/etc/rc.confファイルにpflog_enabled=YESを追加することにより、起動時にロギングが有効になります。 これにより、 pflogd デーモンが有効になり、pflog0という名前のインターフェイスが表示され、/var/log/pflogという名前のファイルにバイナリ形式でログが書き込まれます。 ログは、インターフェイスからリアルタイムで解析するか、 tcpdump(8)ユーティリティを使用して/var/log/pflogファイルから読み取ることができます。

まず、/var/log/pflogファイルからいくつかのログにアクセスします。

  1. sudo tcpdump -ner /var/log/pflog

読みやすくするために出力をフォーマットする-nerフラグを渡し、読み取るファイル(この場合は/var/log/pflog)も指定します。

次の出力が表示されます。

Output
reading from file /var/log/pflog, link-type PFLOG (OpenBSD pflog file)

これらの初期段階では、/var/log/pflogファイルにデータがない可能性があります。 短期間で、ログファイルが大きくなり始めます。

次のコマンドを使用して、pflog0インターフェイスからログをリアルタイムで表示することもできます。

  1. sudo tcpdump -nei pflog0

-neiフラグを渡します。これは、読みやすくするために出力もフォーマットしますが、今回はインターフェイスを指定します。この場合は、pflog0です。

次の出力が表示されます。

Output
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode listening on pflog0, link-type PFLOG (OpenBSD pflog file), capture size 262144 bytes

これで、接続がリアルタイムで表示されます。 可能であれば、リモートマシンからサーバーにpingを実行すると、接続が発生していることがわかります。 サーバーを終了するまで、サーバーはこの状態のままになります。

この状態を終了してコマンドラインに戻るには、CTRL + Zを押します。

tcpdump(8)については、公式の Webサイトを含め、インターネット上に豊富な情報があります。

pftopを使用したログファイルへのアクセス

pftopユーティリティは、ファイアウォールのアクティビティをリアルタイムですばやく表示するためのツールです。 その名前は、よく知られているUnixtopユーティリティの影響を受けています。

使用するには、pftopパッケージをインストールする必要があります。

  1. sudo pkg install pftop

次に、pftopバイナリを実行します。

  1. sudo pftop

これにより、次の出力が生成されます(IPは異なります)。

Output
PR DIR SRC DEST STATE AGE EXP PKTS BYTES tcp In 251.155.237.90:27537 157.225.173.58:22 ESTABLISHED:ESTABLISHED 00:12:35 23:59:55 1890 265K tcp In 222.186.42.15:25884 157.225.173.58:22 TIME_WAIT:TIME_WAIT 00:01:25 00:00:06 22 3801 udp Out 157.245.171.59:4699 67.203.62.5:53 MULTIPLE:SINGLE 00:00:14 00:00:16 2 227

追加のログインターフェイスの作成

他のインターフェイスと同様に、/etc/hostnameファイルを使用して、複数のログインターフェイスを作成して名前を付けることができます。 これは、特定の種類のアクティビティを個別にログに記録する場合など、組織的な目的に役立つ場合があります。

pflog1という名前の追加のロギングインターフェイスを作成します。

  1. sudo vi /etc/hostname.pflog1

/etc/hostname.pflog1ファイルに次の内容を追加します。

/etc/hostname.pflog1
up

次に、/etc/rc.confファイルで起動時にデバイスを有効にします。

  1. sudo sysrc pflog1_enable="YES"

これで、ファイアウォールアクティビティを監視およびログに記録できます。 これにより、サーバーに接続しているユーザーと接続の種類を確認できます。

このチュートリアル全体を通して、いくつかの高度な概念をPFルールセットに組み込んでいます。 必要に応じて高度な機能を実装するだけで済みます。 そうは言っても、次のステップでは、基本ルールセットに戻ります。

ステップ8—基本ルールセットに戻す

この最後のセクションでは、基本ルールセットに戻ります。 これは、機能の最小限の状態に戻るための簡単な手順です。

次のコマンドで基本ルールセットを開きます。

  1. sudo vi /etc/pf.conf

ファイル内の現在のルールセットを削除し、次の基本ルールセットに置き換えます。

/etc/pf.conf
vtnet0 = "vtnet0"
icmp_types = "{ echoreq unreach }"
table <bruteforce> persist
table <rfc6890> { 0.0.0.0/8 10.0.0.0/8 100.64.0.0/10 127.0.0.0/8 169.254.0.0/16          \
                  172.16.0.0/12 192.0.0.0/24 192.0.0.0/29 192.0.2.0/24 192.88.99.0/24    \
                  192.168.0.0/16 198.18.0.0/15 198.51.100.0/24 203.0.113.0/24            \
                  240.0.0.0/4 255.255.255.255/32 }

set skip on lo0
scrub in all fragment reassemble max-mss 1440
antispoof quick for $vtnet0
block in quick on $vtnet0 from <rfc6890>
block return out quick on egress to <rfc6890>
block all
pass in on $vtnet0 proto tcp to port { 22 } \
    keep state (max-src-conn 15, max-src-conn-rate 3/1, \
        overload <bruteforce> flush global)
pass out proto { tcp udp } to port { 22 53 80 123 443 }
pass inet proto icmp icmp-type $icmp_types

ファイルを保存して終了します。

ルールセットをリロードします。

  1. sudo pfctl -f /etc/pf.conf

コマンドからのエラーがない場合、ルールセットにエラーはなく、ファイアウォールは正しく機能しています。

また、作成したpflog1インターフェースを無効にする必要があります。 まだ必要かどうかわからない場合があるため、sysrcユーティリティを使用してpflog1を無効にすることができます。

  1. sudo sysrc pflog1_enable="NO"

次に、/etc/hostname.pflog1ファイルを/etcディレクトリから削除します。

  1. sudo rm /etc/hostname.pflog1

サインオフする前に、サーバーをもう一度再起動して、すべての変更がアクティブで永続的であることを確認します。

  1. sudo reboot

サーバーにログインする前に、数分待ちます。

オプションで、WebサーバーでPFを実装する場合、このシナリオのルールセットは次のとおりです。 このルールセットは、ほとんどのWebアプリケーションにとって十分な出発点です。

シンプルなWebサーバールールセット
vtnet0 = "vtnet0"
icmp_types = "{ echoreq unreach }"
table <bruteforce> persist
table <webcrawlers> persist
table <rfc6890> { 0.0.0.0/8 10.0.0.0/8 100.64.0.0/10 127.0.0.0/8 169.254.0.0/16          \
                  172.16.0.0/12 192.0.0.0/24 192.0.0.0/29 192.0.2.0/24 192.88.99.0/24    \
                  192.168.0.0/16 198.18.0.0/15 198.51.100.0/24 203.0.113.0/24            \
                  240.0.0.0/4 255.255.255.255/32 }

set skip on lo0
scrub in all fragment reassemble max-mss 1440
antispoof quick for $vtnet0
block in quick on $vtnet0 from <rfc6890>
block return out quick on egress to <rfc6890>
block all
pass in on $vtnet0 proto tcp to port { 22 } \
    keep state (max-src-conn 15, max-src-conn-rate 3/1, \
        overload <bruteforce> flush global)
pass in on $vtnet0 proto tcp to port { 80 443 } \
    keep state (max-src-conn 45, max-src-conn-rate 9/1, \
        overload <webcrawlers> flush global)
pass out proto { tcp udp } to port { 22 53 80 123 443 }
pass inet proto icmp icmp-type $icmp_types

これにより、<webcrawlers>という名前のオーバーロードテーブルが作成されます。このテーブルには、max-src-conn 45およびmax-src-conn-rateの値に基づいて、SSHポートよりも自由なオーバーロードポリシーがあります。 これは、すべてのオーバーロードが悪意のあるアクターによるものではないためです。 また、悪意のないネットボットから発生する可能性があるため、ポート80および443での過度のセキュリティ対策を回避します。 Webサーバールールセットを実装する場合は、<webcrawlers>テーブルを/etc/pf.confに追加し、定期的にテーブルからIPをクリアする必要があります。 これについては、ステップ5を参照してください。

結論

このチュートリアルでは、FreeBSD12.1でPFを設定しました。 これで、すべてのFreeBSDプロジェクトの開始点として機能できる基本ルールセットができました。 PFの詳細については、 pf.conf(5)のマニュアルページを参照してください。

私たちをご覧ください FreeBSDトピックページその他のチュートリアルとQ&Aについては。