Bashスクリプトの実行速度を向上

Bashスクリプトの実行速度を向上

大きなiptablesルールファイルをロードするbashスクリプトがあります。これらのすべてのiptableルールは、すべての米国のIPアドレスをブロックするように設計されています。ファイルサイズは約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

......

答え1

iptablesには概念上の制限があります。いつiptablesルールを破ると実際に起こることは次のとおりです。

  • iptablesカーネルに尋ねるみんなルールセット
  • iptables(ユーザー空間で)ルールセットを変更します。通常、次を追加します。一つ入り口
  • iptablesカーネルに戻るみんなルールセット

したがって、100,000のルールで構成されるルールセットにルールを追加すると、多くのCPUが無駄になり、これが発生しないようにする必要があります。

さらに、ルールは線形に作成されるため、各パケット(これを支援するためにステートフルルールを使用している場合は新しい状態のパケットである可能性があります)はこのリストにはありません。みんなルールを繰り返して、一致しないルールを見つけて受け入れます。したがって、パケットルックアップはリストのサイズに比例しますが、これは良くありません。

してはいけないこと:

  • 複数を並列に実行しようとしないでくださいiptables。カーネルロックがないと、最終的にルールが失われます。 (ユーザースペース)ロックを使用しますiptables --waitが、もはや並列ではありません。

問題を解決するには、次のいずれかを実行してください。

  • 使用iptables-restoreルールセット全体を一度にロードします。

    bashスクリプトを1回実行し(待ってから)、次を使用して最終iptables-save結果をダンプします。iptables-restoresedスクリプトが準備された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アドレス一覧を読み込むには、そのコンパニオンを使用してください。IPセットiptablesのように最初にリストを要求するのではなく、ハッシュテーブルを使用し、カーネルに追加する項目だけをプッシュしてそれを処理するように最適化されています。

    欲しいなら

    • 複数のIPアドレスまたはポート番号を保存し、一度にiptablesコレクションと一致させます。
    • IPアドレスまたはポートのiptablesルールを動的に更新します。性能低下なし;
    • 単一のiptablesルールを使用して、複雑なIPアドレスとポートベースのルールセットを表現し、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が使用しているので1です。

  • ipset save usa_ips > ipset_usa_ips.txt上記で指定した現在の設定を使用、保存、および再読み込みできますipset restore usa_ips < ipset_usa_ips.txt。出力形式を見ると、上記のように使い捨てファイルを準備することができ、はるかに高速であることがわかります。〜130000項目の読み込みに1秒未満かかります。形式は次のとおりです。

    更新:実際にセットが現在使用中であるためエラーが発生するため、swap使用可能なコマンドがあります。IPセット。そのリストを代替セットにロードし、前のセットと交換する必要があります。

    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 (Debian 10 および RHEL 8/CentOS 8 のデフォルト)

    nftablesすでに多くのバグを解決するために作られています。iptables。ルールを追加すると、このデルタだけがカーネルに送信されます。完了のためにルールセット全体をリクエスト/編集/再送信する必要はありません。iptables。 iptables-over-nftables互換レイヤーはnftablesとして実装されているため、同じことを行います(主にiptablesの特別な一致とターゲティングのための互換性レイヤーも含みます)。IPセット翻訳できません。)

    ここで止めて運動は皆さんにお任せします。最近は分かったnftables最近のカーネルで使用されている機能は次のとおりです。ネイティブセットのサポートと一緒に使用するとflags intervalauto-merge重複を避けることができます)、次のように完全に置き換えることができます。IPセットあなたの場合。

答え2

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

バラよりiptables(8)

[!] -s, --ソースアドレス[/mask][,...] ソース仕様。アドレスは、ネットワーク名、ホスト名、ネットワークIPアドレス(/マスクを使用)、または通常のIPアドレスです。ホスト名は、ルールがカーネルに送信される前に一度だけ解決されます。リモートクエリ(DNSなど)を使用して解決する名前を指定するのは非常に悪い考えです。マスクは、ipv4ネットマスク(iptablesの場合)であるか、ネットマスクの左側に1の数を指定する一般的な数値です。したがって、iptables マスク 24 は 255.255.255.0 と同じです。アドレス指定の前にある「!」引数はアドレスの意味を反転します。 --srcフラグはこのオプションのエイリアスです。複数のアドレスを指定できますが、これにより複数のルールに展開されるか(-Aを追加する場合)、複数のルールが削除されます(-Dで使用する場合)。

その後、オペレーティングシステムによって異なり得る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行= 1 iptables呼び出し)。
  • UbuntuにはARG_MAX 2097152があります。9 * 1024^2 / 2097152 ≈ 5いいね

関連情報