PerlのFastCGIアプリケーション。 パート2

前の記事で作成されたFastCGIアプリケーションは、起動元の端末とチェーン化されています 。 ターミナルが開いている限り、アプリケーションはスムーズに実行されます。 ターミナルを閉じるとすぐに、アプリケーションはすぐに強制終了されます。



その理由は、端末から起動されたプログラムはこの端末の子孫になり、端末はそれぞれこのプログラムの親になるためです。 親のない子孫の存在は許可されていません。 したがって、親端末が閉じられると、それに依存するすべての子孫、特にFastCGIアプリケーションがすぐに閉じられます。



FastCGIアプリケーションが親端末に依存しなくなるには、単純なスクリプトからデーモンに変換する必要があります。



これは一体何?



デーモンは、ユーザーが直接操作することなくバックグラウンドで実行されるプログラムです。 デーモンの親はinitです。これは、オペレーティングシステムが起動して他のすべてのプログラムを起動するときに起動する最初のプログラムです。 initはブートからシャットダウンまで機能するため、デーモン(もちろん、kill intervenesを削除しない限り)もスリープや休息なしで機能します。



悪魔になるためには、アプリケーションは一連のアクションを実行する必要があります。その主な目的は、あらゆる面で保護者のケアを取り除くことです。



前の記事のスクリプトを使用して、悪魔化を実行するコードブロックを追加します。



 #!/ usr / bin / perl

 #物を整理する
厳格な使用;
警告を使用します。

 #このモジュールはFastCGIプロトコルを実装します
 FCGIを使用します。

 #悪魔{
     #このモジュールは、概念についてOSと話すためのものです:)
     POSIXを使用します。
   
     #フォーク
     #親を取り除く
     fork_proc()&& exit 0;
   
     #新しいセッションを開始
     #私たちの悪魔は新しいセッションの創設者になります
     POSIX :: setsid()またはdie "sidを設定できません:$!";
   
     #ルートディレクトリに移動
     #ファイルシステムのアンマウントを妨げないように
     chdir '/'またはdie "Ca n't chdir:$!";
   
     #ユーザーをnobodyに変更
     #私たちは妄想ですよね?
     POSIX :: setuid(65534)またはdie "Ca n't set uid:$!";

     #/ dev / nullで標準記述子を再度開きます
     #ユーザーと会話しなくなった
     reopen_std();
 #}

 #ソケットを開く
 #デーモンはポート9000でリッスンします
 #要求キューの長さ-5個
 my $ socket = FCGI :: OpenSocket( ":9000"、5);

 #聞き始める
 #デーモンは標準記述子をインターセプトします
 my $ request = FCGI :: Request(\ * STDIN、\ * STDOUT、\ * STDERR、\%ENV、$ socket);

私の$カウント= 1;

 #無限ループ
 #受信したリクエストごとに、サイクルの1つの「革命」が実行されます。
 while($ request-> Accept()> = 0){
     #ループ内で、必要なすべてのアクションが実行されます
     print "Content-Type:text / plain \ r \ n \ r \ n";
    印刷$カウント++;
 };

 #フォーク
 sub fork_proc {
    私の$ pid;
   
    フォーク:{
         if(defined($ pid = fork)){
             return pid;
         }
         elsif($!=〜/これ以上のプロセスはありません/){
            睡眠5;
             REDO FORK;
         }
        その他{
             die "フォークできません:$!";
         };
     };
 };

 #/ dev / nullで標準記述子を再度開きます
 sub reopen_std {   
     open(STDIN、 "+> / dev / null")またはdie "Ca n't open STDIN:$!";
     open(STDOUT、 "+>&STDIN")またはdie "Ca n't open STDOUT:$!";
     open(STDERR、 "+>&STDIN")またはdie "Ca n't open STDERR:$!";
 };


このトピックの人たちには、疑問が生じるかもしれません-既製のProc :: Daemonモジュールを使用する代わりに、なぜ手動で悪魔化したのですか?



実際、悪魔化に必要なコマンドのシーケンスは、Proc :: Daemonモジュールで1つの関数にまとめられています。 将来、並列化を悪魔に固定するとき、これは私たちにトリックをかけることができます。 悪魔化プロセスを2段階に分ける必要があり、これは手動でのみ可能です。



一般に、悪魔化自体にはクックブックの例との特別な違いは含まれておらず、fork_proc関数の実装はキャメルブックの1つにほぼ1つ含まれています。



それとは別に、ユーザーが誰にも変更されないことに注意する価値があります。



ユーザーnobodyは、システムに対する特権を持たない特別なユーザーです。 ユーザーnobodyに代わってデーモンが動作することにより、攻撃者がデーモンを制御できる場合に追加の保護が提供されます。 この場合、誰にも代わってデーモンが動作するため、攻撃者はシステムの残りのリソースにアクセスできなくなります。



setuidの番号65534は、nobodyのuidです。 特別なコマンドを使用してuidを調整できます。



$ id nobody



はい、rootのみがuidを変更できるため、rootもデーモンを実行する必要があることに注意してください。



特に腐食性があります。もう1つ質問があります。正しい完了のためにシグナルハンドラを提供しなかったのはなぜですか



実際、この特定のケースでは、シグナルハンドラを作成する必要はありません。 むしろ、ハンドラーは、並列化を台無しにするときに、いわば魔法のように自分で実装されます。



悪魔を開始します。 エラーがない場合、デーモンはサイレントに起動し、何も表示せず、コンソールから切り離されます。 これで端末を閉じることができます。デーモンはこれに注意を払わずに動作を続けます。



ブラウザでtest.host/fcgi-bin/を開きます-デーモンは以前のように応答します。 これで、killコマンドでのみシャットダウンできます。



次の部分である並列化では、デーモンにいくつかのリクエストに同時に応答するように教えます。



オリジナル記事



All Articles