FreeBSD12.1でパケットフィルタ(PF)を設定する方法
著者は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以外のユーザーでサーバーにログインします。
ssh freebsd@your_server_ip
次に、/etc/pf.conf
ファイルを作成します。
sudo vi /etc/pf.conf
PFは、block
、pass
、およびmatch
の3つのコアアクションに従ってパケットをフィルタリングします。 他のオプションと組み合わせると、ルールが形成されます。 パケットがルールで指定された基準を満たすと、アクションが実行されます。 ご想像のとおり、pass
およびblock
ルールはpass
およびblock
トラフィックになります。 match
ルールは、一致する基準を見つけたときにパケットに対してアクションを実行しますが、それを通過またはブロックしません。 たとえば、一致するパケットに対してネットワークアドレス変換(NAT)を渡すこともブロックすることもせずに実行でき、別のルールにルーティングするなど、別のルールで何かを行うように指示するまでそこに留まります。マシンまたはゲートウェイ。
次に、最初のルールを/etc/pf.conf
ファイルに追加します。
/etc/pf.conf
block all
このルールは、あらゆる方向のあらゆる形態のトラフィックをブロックします。 方向を指定しないため、デフォルトではin
とout
の両方になります。 このルールは、世界から隔離する必要があるローカルワークステーションには有効ですが、ほとんど実用的ではなく、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プロトコルの両方を切り替えることが多いため、ポート53
と123
に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を有効にしましょう。
sudo sysrc pf_enable="YES" sudo sysrc pflog_enable="YES"
/etc/rc.conf
ファイルの内容を印刷して、これらの変更を確認します。
sudo cat /etc/rc.conf
次の出力が表示されます。
Outputpf_enable="YES" pflog_enable="YES"
また、pflog
サービスを有効にします。これにより、pflogd
デーモンがPFにログインできるようになります(後の手順でログインを操作します)。
/etc/rc.conf
ファイルで2つのグローバルサービスを指定しますが、サーバーを再起動するか手動で起動するまで、それらは初期化されません。 サーバーを再起動して、SSHアクセスもテストできるようにします。
サーバーを再起動してPFを開始します。
sudo reboot
接続が切断されます。 更新するのに数分かかります。
次に、SSHでサーバーに戻ります。
ssh freebsd@your_server_ip
PFサービスを初期化しましたが、実際には/etc/pf.conf
ルールセットをロードしていません。つまり、ファイアウォールはまだアクティブではありません。
pfctl
を使用してルールセットをロードします。
sudo pfctl -f /etc/pf.conf
エラーやメッセージがない場合は、ルールセットにエラーがなく、ファイアウォールがアクティブになっていることを意味します。
PFが実行されたので、サーバーをクラウドファイアウォールから切り離すことができます。 これは、DigitalOceanアカウントのコントロールパネルで、クラウドファイアウォールのポータルからDropletを削除することで実行できます。 別のクラウドプロバイダーを使用している場合は、一時的な保護に使用しているものがすべて無効になっていることを確認してください。 サーバーで2つの異なるファイアウォールを実行すると、ほぼ確実に問題が発生します。
適切な方法として、サーバーを再起動します。
sudo reboot
数分後、SSHでサーバーに戻ります。
ssh freebsd@your_server_ip
これで、PFがファイアウォールとして機能します。 pfctlユーティリティーを使用していくつかのデータにアクセスすることにより、それが実行されていることを確認できます。
pfctl -si
を使用していくつかの統計とカウンターを表示してみましょう。
sudo pfctl -si
showinfoを表す-si
フラグを渡します。 これは、ファイアウォールアクティビティに関するデータを解析するためにpfctlで使用できる多くのフィルターパラメーターの組み合わせの1つです。
次の表形式のデータが表示されます(値はマシンごとに異なります)。
OutputStatus: 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サービスを確認しましょう。
ping -c 3 google.com
カウントフラグ-c 3
を実行したので、3つの成功した接続応答が表示されます。
OutputPING 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リポジトリにアクセスできることを確認します。
sudo pkg upgrade
アップグレードするパッケージがある場合は、先に進んでアップグレードしてください。
これらのサービスの両方が機能している場合は、ファイアウォールが機能していることを意味し、続行できます。 予備のルールセットは保護と機能を提供しますが、それでも基本的なルールセットであり、いくつかの拡張機能を使用できます。 残りのセクションでは、基本ルールセットを完成させ、PFの高度な機能のいくつかを使用します。
ステップ3—基本ルールセットを完成させる
このステップでは、予備ルールセットを構築して、基本ルールセットを完成させます。 ルールの一部を再編成し、より高度な概念で作業します。
マクロとテーブルの組み込み
予備のルールセットでは、すべてのパラメーターを各ルール、つまりリストを構成するポート番号にハードコーディングしました。 ネットワークの性質によっては、これは将来管理できなくなる可能性があります。 組織的な目的で、PFにはマクロ、リスト、およびテーブルが含まれています。 すでにルールに直接リストを含めていますが、それらをルールから分離し、マクロを使用して変数に割り当てることもできます。
ファイルを開いて、パラメータの一部をマクロに転送します。
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
コマンドで決定できます。
ifconfig
現在のMTUを含む次の出力が表示されます。
Outputvtnet0: 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
の内容を印刷します。
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
コマンドを実行して、ドライランを実行します。
sudo pfctl -nf /etc/pf.conf
ルールセットをロードせずに実行するようにpfctl
に指示する-nf
フラグを渡します。これにより、何か問題がある場合にエラーがスローされます。
ここで、エラーが発生することなく、ルールセットをロードします。
sudo pfctl -f /etc/pf.conf
エラーがない場合は、基本ルールセットがアクティブであり、正しく機能していることを意味します。 チュートリアルの前半と同様に、ルールセットに対していくつかのテストを実行します。
インターネット接続とDNSサービスの最初のテスト:
ping -c 3 google.com
次の出力が表示されます。
OutputPING 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
リポジトリに到達したことを確認します。
sudo pkg upgrade
もう一度、必要に応じてパッケージをアップグレードします。
最後に、サーバーを再起動します。
sudo reboot
サーバーを数分待ってから再起動します。 基本ルールセットを完成させて実装しました。これは、進捗状況の観点から重要なステップです。 これで、PFの高度な機能のいくつかを探索する準備が整いました。 次のステップでは、ブルートフォース攻撃を引き続き防止します。
ステップ5—過負荷テーブルの管理
時間の経過とともに、<bruteforce>
過負荷テーブルは悪意のあるIPアドレスでいっぱいになり、定期的にクリアする必要があります。 攻撃者が同じIPアドレスを使用し続ける可能性は低いため、それらを過負荷テーブルに長期間保存することは直感に反します。
pfctl
を使用して、次のコマンドを使用して、過負荷テーブルに48時間以上保存されているIPアドレスを手動でクリアします。
sudo pfctl -t bruteforce -T expire 172800
次のような出力が表示されます。
Output0/0 addresses expired.
table bruteforceを表す-t bruteforce
フラグと、いくつかの組み込みコマンドを実行できる-T
フラグを渡します。 この場合、expire
コマンドを実行して、-t bruteforce
からすべてのエントリを秒単位の時間値でクリアします。 新しいサーバーで作業しているため、過負荷テーブルにはまだIPアドレスがない可能性があります。
このルールは迅速な修正に有効ですが、より堅牢な解決策は、FreeBSDのジョブスケジューラであるcronを使用してプロセスを自動化することです。 代わりに、このコマンドシーケンスを実行するシェルスクリプトを作成しましょう。
/usr/local/bin
ディレクトリにシェルスクリプトファイルを作成します。
sudo vi /usr/local/bin/clear_overload.sh
次のコンテンツをシェルスクリプトに追加します。
/usr/local/bin/clear_overload.sh
#!/bin/sh pfctl -t bruteforce -T expire 172800
次のコマンドを使用してファイルを実行可能にします。
sudo chmod 755 /usr/local/bin/clear_overload.sh
次に、cronジョブを作成します。 これらは、指定した時間に従って繰り返し実行されるジョブです。 これらは通常、バックアップ、または毎日同時に実行する必要のあるプロセスに使用されます。 crontabファイルを使用してcronジョブを作成します。 cron(8)および crontab(5)の詳細については、マニュアルページを参照してください。
次のコマンドを使用して、rootユーザーのcrontabファイルを作成します。
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
という名前のファイルを作成します。
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
ファイルでアンカーを宣言する必要があります。
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
を使用してルールセットをリロードして、これらの変更を開始します。
sudo pfctl -f /etc/pf.conf
エラーがない場合は、ルールセットにエラーがなく、変更がアクティブであることを意味します。
pfctl
を使用して、アンカーが実行されていることを確認します。
sudo pfctl -s Anchors
-s Anchors
フラグは、「アンカーの表示」を表します。 次の出力が表示されます。
Outputblocked_hosts
pfctl
ユーティリティは、-a
および-s
フラグを使用してアンカーの特定のルールを解析することもできます。
sudo pfctl -a blocked_hosts -s rules
次の出力が表示されます。
Outputblock return out quick on egress from <blocked-hosts> to any
アンカーのもう1つの機能は、ルールセットをリロードしなくても、オンデマンドでルールを追加できることです。 これは、テスト、クイックフィックス、緊急事態などに役立ちます。 たとえば、内部ホストが特殊な動作をしていて、外部接続をブロックしたい場合は、コマンドラインからすばやく介入できるアンカーを配置できます。
/etc/pf.conf
を開いて、別のアンカーを追加しましょう。
sudo vi /etc/pf.conf
アンカーにrogue_hosts
という名前を付け、block all
ルールに配置します。
/etc/pf.conf
. . . block all anchor rogue_hosts . . .
ファイルを保存して終了します。
これらの変更を開始するには、pfctl
を使用してルールセットをリロードします。
sudo pfctl -f /etc/pf.conf
もう一度、pfctl
を使用して、アンカーが実行されていることを確認します。
sudo pfctl -s Anchors
これにより、次の出力が生成されます。
Outputblocked_hosts rogue_hosts
アンカーが実行されているので、いつでもアンカーにルールを追加できます。 次のルールを追加して、これをテストします。
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
を再度使用して、これらのルールがアクティブであることを確認します。
sudo pfctl -a rogue_hosts -s rules
これにより、次の出力が生成されます。
Outputblock 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
ファイルからいくつかのログにアクセスします。
sudo tcpdump -ner /var/log/pflog
読みやすくするために出力をフォーマットする-ner
フラグを渡し、読み取るファイル(この場合は/var/log/pflog
)も指定します。
次の出力が表示されます。
Outputreading from file /var/log/pflog, link-type PFLOG (OpenBSD pflog file)
これらの初期段階では、/var/log/pflog
ファイルにデータがない可能性があります。 短期間で、ログファイルが大きくなり始めます。
次のコマンドを使用して、pflog0
インターフェイスからリアルタイムでログを表示することもできます。
sudo tcpdump -nei pflog0
-nei
フラグを渡します。これは、読みやすくするために出力もフォーマットしますが、今回はインターフェイスを指定します。この場合は、pflog0
です。
次の出力が表示されます。
Outputtcpdump: 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
パッケージをインストールする必要があります。
sudo pkg install pftop
次に、pftop
バイナリを実行します。
sudo pftop
これにより、次の出力が生成されます(IPは異なります)。
OutputPR 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
という名前の追加のロギングインターフェイスを作成します。
sudo vi /etc/hostname.pflog1
/etc/hostname.pflog1
ファイルに次の内容を追加します。
/etc/hostname.pflog1
up
次に、/etc/rc.conf
ファイルで起動時にデバイスを有効にします。
sudo sysrc pflog1_enable="YES"
これで、ファイアウォールアクティビティを監視およびログに記録できます。 これにより、サーバーに接続しているユーザーと接続の種類を確認できます。
このチュートリアル全体を通して、いくつかの高度な概念をPFルールセットに組み込んでいます。 必要に応じて高度な機能を実装するだけで済みます。 そうは言っても、次のステップでは、基本ルールセットに戻ります。
ステップ8—基本ルールセットに戻す
この最後のセクションでは、基本ルールセットに戻ります。 これは、機能の最小限の状態に戻るための簡単な手順です。
次のコマンドで基本ルールセットを開きます。
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
ファイルを保存して終了します。
ルールセットをリロードします。
sudo pfctl -f /etc/pf.conf
コマンドからのエラーがない場合、ルールセットにエラーはなく、ファイアウォールは正しく機能しています。
また、作成したpflog1
インターフェースを無効にする必要があります。 まだ必要かどうかわからない場合があるため、sysrc
ユーティリティを使用してpflog1
を無効にすることができます。
sudo sysrc pflog1_enable="NO"
次に、/etc/hostname.pflog1
ファイルを/etc
ディレクトリから削除します。
sudo rm /etc/hostname.pflog1
サインオフする前に、サーバーをもう一度再起動して、すべての変更がアクティブで永続的であることを確認します。
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については。