Linuxでプロセスが新しいファイル記述子を開かないようにするが、ソケット経由でファイル記述子を受信できるようにする


9

私は現在、ソケットペアをセットアップしてフォークし、このソケットペアを使用して通信する親プロセスがあるプロジェクトに取り組んでいます。子は、ファイル(またはその他のファイル記述子ベースのリソース)を開きたい場合は常に親に移動し、リソースを要求しfdて、socketpair経由で送信されます。さらに、子供が自分でファイル記述子を開かないようにしたいと思います。

私はつまずきましsetrlimitたが、子が新しいファイル記述子を開くことができなくなりましたが、最初のソケット接続で送信されたファイル記述子が無効になるようです。単一のプロセスが任意のファイルを開き、そのファイル記述子を他のプロセスに送信し、これらの他のプロセスが自分でファイル記述子を開くことを許可せずにそれらを使用できるようにするLinuxのメソッドはありますか?

私のユースケースでは、フォーク後に適用でき、すべてのファイル記述子(ファイルだけでなく、ソケット、ソケットペアなど)にも適用できる限り、カーネル構成、システムコールなどを使用できます。


1
seccompに興味があるかもしれません。
user253751

回答:


6

ここにあるのは、まさにseccompの使用例です。

seccompを使用すると、さまざまな方法でsyscallをフィルタリングできます。あなたはこのような状況でやりたいことは、右の後、あるfork()インストールするには、seccompの使用を禁止するフィルタをopen(2)openat(2)socket(2)(およびそれ以上)。これを行うには、次のようにします。

  1. まず、seccomp_init(3)のデフォルトの動作でを使用して、seccompコンテキストを作成しますSCMP_ACT_ALLOW
  2. 次に、seccomp_rule_add(3)拒否する各syscallを使用して、コンテキストにルールを追加します。を使用SCMP_ACT_KILLして、syscallが試行された場合にプロセスをSCMP_ACT_ERRNO(val)強制終了しerrnoたり、syscallを失敗させたりして、指定された値またはactionマニュアルページで定義されている他の値を返すことができます。
  3. を使用seccomp_load(3)してコンテキストをロードして有効にします。

続行する前に、このようなブラックリストアプローチは一般にホワイトリストアプローチよりも弱いこと注意してください。これにより、明示的に許可されていないシステムコールが許可され、フィルターがバイパスされる可能性があります。実行したい子プロセスが悪意を持ってフィルターを回避しようとしていると思われる場合、またはどのsyscallが子に必要かをすでに知っている場合は、ホワイトリストアプローチの方が適切であり、上記の反対を行う必要があります。のデフォルトアクションでフィルタを作成しSCMP_ACT_KILL、必要なsyscallsを許可しますSCMP_ACT_ALLOW。コードに関しては、違いはごくわずかです(ホワイトリストはおそらく長くなりますが、手順は同じです)。

上記の例を次に示します(exit(-1)単純にするために、エラーが発生した場合に備えています)。

#include <stdlib.h>
#include <seccomp.h>

static void secure(void) {
    int err;
    scmp_filter_ctx ctx;

    int blacklist[] = {
        SCMP_SYS(open),
        SCMP_SYS(openat),
        SCMP_SYS(creat),
        SCMP_SYS(socket),
        SCMP_SYS(open_by_handle_at),
        // ... possibly more ...
    };

    // Create a new seccomp context, allowing every syscall by default.
    ctx = seccomp_init(SCMP_ACT_ALLOW);
    if (ctx == NULL)
        exit(-1);

    /* Now add a filter for each syscall that you want to disallow.
       In this case, we'll use SCMP_ACT_KILL to kill the process if it
       attempts to execute the specified syscall. */

    for (unsigned i = 0; i < sizeof(blacklist) / sizeof(blacklist[0]); i++) {
        err = seccomp_rule_add(ctx, SCMP_ACT_KILL, blacklist[i], 0);
        if (err)
            exit(-1);
    }

    // Load the context making it effective.
    err = seccomp_load(ctx);
    if (err)
        exit(-1);
}

これで、プログラムで上記の関数を呼び出して、seccompフィルターを直後に適用できます。 fork()

child_pid = fork();
if (child_pid == -1)
    exit(-1);

if (child_pid == 0) {
    secure();

    // Child code here...

    exit(0);
} else {
    // Parent code here...
}

seccompに関するいくつかの重要な注意事項:

  • seccompフィルターは、一度適用されると、プロセスによって削除または変更することはできません。
  • もしfork(2)またはclone(2)フィルターによって許可されている許可されている、すべての子プロセスは同じフィルターによって制約されます。
  • 場合はexecve(2)許可され、既存のフィルタが呼び出しに渡って維持されますexecve(2)
  • 場合はprctl(2)システムコールが許可され、プロセスは、さらにフィルタを適用することができます。

2
サンドボックスのブラックリスト登録?一般的には悪い考えですが、代わりにホワイトリストを作成する必要があります。
Deduplicator

@Deduplicatorは知っていますが、OPの状況にはホワイトリストアプローチはあまり当てはまりません。OPが新しいファイル記述子を開くことを許可しないためです。最後にメモを追加します。
Marco Bonelli

答えてくれてありがとう、それが私が必要なことです。ホワイトリストは確かに、私が最初に意図したアプリケーションに適しています。ファイル記述子を開くだけでは制限すべきことが他にもあることを私は思いもしませんでした。
jklmnn

@jklmnnそうですね。実際、socket(2)も作成できることを忘れたfdので、それもブロックする必要があります。子プロセスを知っている場合は、ホワイトリストアプローチの方が適しています。
Marco Bonelli

@MarcoBonelliホワイトリストは間違いなく優れています。ぶっきらぼう、creat()dup()、およびdup2()すべてのLinuxシステムコールそのリターン・ファイル記述子があります。ブラックリストにはさまざまな方法があります...
Andrew Henle
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.