web-dev-qa-db-ja.com

bashスクリプトの実行を高速化

Iptablesルールの大きなファイルをロードするこのbashスクリプトがあります。これらのiptableルールはすべて、すべてのUSAIPアドレスをブロックすることです。このファイルのサイズは約9MBです。実行しようとすると、実行に40分ほどかかりました。

このスクリプトの実行を高速化する方法はありますか?

# head -n 4 iptables_block_usa.sh 

iptables -A INPUT -s 216.187.112.96/27 -j DROP
iptables -A INPUT -s 216.187.112.128/27 -j DROP
iptables -A INPUT -s 216.187.112.160/31 -j DROP
iptables -A INPUT -s 216.187.112.163/32 -j DROP

......
2
Marco Leslie

iptablesには、その概念による制限があります。 iptablesがルールを変更すると、実際に何が起こるかを次に示します。

  • iptablesはカーネルに全体ルールセットを要求します
  • iptablesはルールセットを(ユーザースペースで)変更し、通常はoneエントリを追加します
  • iptablesはカーネルにwholeルールセットを返します

したがって、100000ルールのルールセットに1つのルールを追加すると、CPUが大量に浪費されるため、これが発生しないようにする必要があります。

また、ルールは直線的に記述されているため、パケット(ステートフルルールを使用している場合はNEW状態のパケット)がこのリストに含まれないたびに、allルール誰も一致せず、それを受け入れることを理解するためにトラバースされます。したがって、パケットルックアップはリストのサイズに比例しますが、これは適切ではありません。

してはいけないこと:

  • 複数のiptablesを並行して実行しようとしないでください。カーネルロックが行われず、ルールが欠落することになります。 iptables --waitを使用すると(ユーザースペースで)ロックされますが、並列化されなくなります。

代わりに、以下のいずれかを実行して問題を解決できます。

  • iptables-restore を使用して、ルールセット全体を1回のショットでロードします。

    Bashスクリプトを1回実行して(そして待機して)、最終結果をiptables-saveでダンプし、iptables-restoreで再利用できるようにするか、sedなどを使用してスクリプトを編集して準備ファイルを作成します。 iptables-restore形式。これがデフォルトのフィルターテーブルである場合は、次のような形式にする必要があります。

    *filter
    :INPUT ACCEPT [0:0]
    :FORWARD ACCEPT [0:0]
    :OUTPUT ACCEPT [0:0]
    -A INPUT -s 216.187.112.96/27 -j DROP
    -A INPUT -s 216.187.112.128/27 -j DROP
    -A INPUT -s 216.187.112.160/31 -j DROP
    -A INPUT -s 216.187.112.163/32 -j DROP
    [...]
    -A INPUT -s 192.0.2.0/24 -j DROP
    COMMIT
    
  • または、アドレスのリストをロードするために直接iptablesを使用せず、そのコンパニオンを使用します ipset これは、ハッシュテーブルを使用し、追加するエントリをカーネルにプッシュするだけで、iptablesのように最初にリストを要求するのではなく、これを処理するように最適化されています。

    あなたがしたい場合は

    • 複数のIPアドレスまたはポート番号を保存し、一挙にiptablesによるコレクションと照合します。
    • iPアドレスまたはポートに対してiptablesルールを動的に更新しますパフォーマンスペナルティなし;
    • 複雑なIPアドレスとポートベースのルールセットを単一のiptablesルールで表現し、IPセットの速度の恩恵を受ける

    それならipsetがあなたにとって適切なツールかもしれません。

    要素の最大数を定義する必要があります(デフォルトは「のみ」65536)。ハッシュサイズはipsetによって動的にサイズ変更されるため、必要ありません(または、少なくともhashsize 65536を追加することを検討してください)。単純なケース(ソースIPネットワークのみ)の場合、次のようにします。

    ipset create usa_ips hash:net maxelem 300000 hashsize 65536
    

    次に、ipsetを使用してリストをループします。リストは次のようなファイルusa_ips.txtである可能性があります。

    216.187.112.96/27
    216.187.112.128/27
    216.187.112.160/31
    216.187.112.163/32
    [...]
    

    これは、次のようなループでおそらく10分かかるはずです。

    while read net; do
        ipset add usa_ips $net
    done < usa_ips.txt
    

    最後または最後の前に、たとえば次のようにリストがどれだけ大きくなったかを確認できます。

    # ipset -t list usa_ips
    Name: usa_ips
    Type: hash:net
    Revision: 6
    Header: family inet hashsize 65536 maxelem 300000
    Size in memory: 3847384
    References: 0
    Number of entries: 131076
    

    そして今、あなたはこのリストに一致するすべてをこの単一のiptablesルールで削除することができます:

    iptables -A INPUT -m set --match-set usa_ips src -j DROP
    

    Iptablesが使用しているため、上記のReferencesエントリを1に設定します。

  • 上記で作成した現在のセットは、ipset save usa_ips > ipset_usa_ips.txtおよびipset restore usa_ips < ipset_usa_ips.txtを使用して保存および再ロードできます。出力形式を見ると、上記のようにワンショットファイルを準備することもできます。これははるかに高速です:〜130000エントリをロードするのに1秒未満かかります。形式は次のとおりです。

    更新:セットが現在使用されているため、実際にはエラーが発生します。そのため、ipsetswapコマンドを使用できます。リストを別のセットにロードして、前のセットと交換する必要があります。

    create usa_ips_new hash:net family inet hashsize 65536 maxelem 300000
    add usa_ips_new 216.187.112.96/27
    add usa_ips_new 216.187.112.128/27
    add usa_ips_new 216.187.112.160/31
    add usa_ips_new 216.187.112.163/32
    [...]
    

    また、以前に作成したセットを次のものに置き換えることができます。

    # ipset restore < usa_ips_new.txt
    # ipset swap usa_ips usa_ips_new
    # ipset destroy usa_ips_new
    
  • nftablesまたは少なくともiptables-over-nftables API(Debian10およびRHEL8/CentOS 8ではデフォルト)に切り替えます

    nftablesは、iptablesで行われた多くの間違いに対処するために作成されました。ルールを追加すると、このデルタのみがカーネルに送信されます。iptablesによって実行されるルールセット/編集/返信全体を要求する必要はありません。 iptables-over-nftables互換性レイヤーは、nftables(および主にiptablesの特別な一致とターゲット用の互換性レイヤーipsetで実装されているため、同じことを行っています。翻訳されません)。

    ここでやめて、演習をお任せします。最近のカーネルで使用されている最近のnftablesには ネイティブセットのサポート があり、flags interval(および重複を避けるためにauto-merge)と一緒に使用すると完全にできることを知っておいてくださいケースのipsetの使用を置き換えます。

4
A.B

,で区切って複数のソースを指定できます

iptables(8) を参照してください

[!] -s、-source address [/ mask][、...]ソース仕様。アドレスは、ネットワーク名、ホスト名、ネットワークIPアドレス(/ mask付き)、またはプレーンIPアドレスのいずれかです。ルールがカーネルに送信される前に、ホスト名は1回だけ解決されます。 DNSなどのリモートクエリで解決する名前を指定することは、非常に悪い考えであることに注意してください。マスクは、ipv4ネットワークマスク(iptablesの場合)またはプレーン番号のいずれかで、ネットワークマスクの左側にある1の数を指定できます。したがって、24のiptablesマスクは255.255.255.0と同等です。 「!」アドレス指定の前の引数は、アドレスの意味を反転させます。フラグ--srcは、このオプションのエイリアスです。複数のアドレスを指定できますが、これは複数のルールに拡張されるか(-Aで追加する場合)、または複数のルールが削除される(-Dで)。

次に、OSによって異なる可能性のあるbashコマンドラインの最大長によって制限されます。リミットランを見つけるには

getconf ARG_MAX

これを行うと、iptablesコマンドを何度も実行する手間が省けます。

質問の例は次のとおりです。

iptables -A INPUT -s 216.187.112.96/27,216.187.112.128/27,216.187.112.160/31,216.187.112.163/32 -j DROP

読みやすくするには次のように行を分割できるはずです。

iptables -A INPUT -s\
  216.187.112.96/27,\
  216.187.112.128/27,\
  216.187.112.160/31,\
  216.187.112.163/32 \
  -j DROP

数学をやってみましょう:

  • ファイルの容量は9MBです。行は約48Bで、約9 * 1024^2 / 48 ≈ between 190000 and 200000行です(1行= iptablesの1回の呼び出し)。
  • UbuntuにはARG_MAX2097152があります。9 * 1024^2 / 2097152 ≈ 5
0
Jakub Jindra