プロセスは、自分がサブネームスペースにあるかどうかをどのように検出しますか?

プロセスは、自分がサブネームスペースにあるかどうかをどのように検出しますか?

このトピックを調査した結果、Githubで次のコードが見つかりました。

// HasNamespace determines if a container is using a particular namespace or the
// host namespace.
// The device number of an unnamespaced /proc/1/ns/{ns} is 4 and anything else is
// higher.
// Only works from inside a container.

https://github.com/genuinetools/amicontained/blob/568b0d35e60cb2bfc228ecade8b0ba62c49a906a/vendor/github.com/jessfraz/bpfd/proc/proc.go#L461

しかし、この意見は次のように古いものです。

$ docker run -ti --rm --pid host debian
root@e29ab2d7176b:/# stat --format="%d" /proc/self/ns/net
58
root@e29ab2d7176b:/# stat --format="%d" /proc/self/ns/pid
58

その説明が正しい場合、結果はstat --format="%d" /proc/self/ns/pid4になるはずです。

プロセスがサブネームスペースにあるかどうかをどのように検出しますか?

ベストアンサー1

しばらく前に、このトピックを直接調査しながら、私が見つけた内容を共有できます。確かに権威あるか徹底的ではありませんが、役に立つかもしれません。

技術的な説明

婦人声明

私自身は、私が説明したい方法を実際に実装したことはありません。これは私がしばらく前に参加したカスタムコンテナ化プロジェクトの可能性でしたが、名前空間の検出を完全に放棄することにしました。興味があれば、読んでください。


これは、カーネルで観察される動作を利用して初期名前空間を検出する方法です。 (しかしこれらの動作は公式のAPIではありません。)。この方法は最も一般的で合理的な設定で動作しますが、必ずしもそうではありません。

「スタートアップ」ネームスペース

から始まるカーネル v3.8最新の安定版でv5.11(現在 v5.12-rc),初期のIPC、UTS、ユーザー、PID、cgroup、およびタイムネームスペースには、常に以下のように特定のハードコーディングされたIDがあります。したがって、私たちは次のように安全に仮定することができます。それら名前空間タイプ固定IDより大きい名前空間IDは、子名前空間と見なすことができます。

IPC    = 0xEFFFFFFF
UTS    = 0xEFFFFFFE
USER   = 0xEFFFFFFD
PID    = 0xEFFFFFFC
CGROUP = 0xEFFFFFFB
TIME   = 0xEFFFFFFA

上記のリストはv5.12-rc6ソースコードから得られたものですが、値はv3.8でまったく存在しなかった名前空間( "cgroup")を除いて、v3.8以降は常に同じでした。 v4.6に追加され、v5では6)。

値が下向きに「成長」するにつれて、これらの初期名前空間がどのように(長年にわたって)追加されるかを確認してください。代わりに、すべてのサブネームスペースは順次(要求時に)値を持ちます。上に成長する0xF0000000

だから、初期名前空間の設定をオーバーライドする親エントリがないプロセスの場合、これらの固定値は、これらの名前空間の「検出操作」を非常にきれいに解決できます。

しかし、これらの値は別の言葉ユーザースペースに公開された公式APIの一部(カーネル空間AFAICTでもない)したがって、今後は変更される可能性があります。

カーネル開発者はそれらをすべて動的にするか、ランダムにすることもできます。実際には、マウントとネットワークの名前空間がこのリストにないことがわかります。みんなネットワークとマウントの名前空間、含むオリジナルはすでに完全に動的でしたいつも0xF0000000サブネームスペースと同様に、IDで始まります。したがって、最も友好的な条件でも、マウントとネットワークの初期名前空間に対していくつかの経験的な方法を実行する必要があります。

マウントネームスペース

これまでの経験では、最初のマウントネームスペースIDは常に最初の動的値(0xF0000000)を取得します。これはおそらく、最初のPID名前空間が共通のファイルprocシステムをインスタンス化し、最初のマウント名前空間もインポートするためです。それにもかかわらず、初期マウント名前空間のIDはかなり予測可能であるように見え、実際には動的範囲内でも固定されています。

ネットワークネームスペース

一方、構成変更が動的に生成された inode 番号のシーケンスに影響を与える場合、初期ネットワーク ネームスペース ID は、同じオペレーティング システムを使用する同じコンピュータの以前のブートとはるかに異なる値を取得できます。したがって、最初のネットワークネームスペースを検出することが実際の宝くじになる可能性があります。あなたはしばしば「勝利」することができますが、そのためには一般的に正気な環境では真実ですが、必ずしも真実ではないかもしれないいくつかのことを仮定する必要があります。

最初のネットワーク名前空間は、システムの起動後に最初のネットワーク操作を要求するプロセスの結果としてインスタンス化されます(通常はPID 1)。したがって/proc/net/、ディレクトリが使用可能になり、その中にファイル/ディレクトリが作成され、それぞれは、名前空間IDに使用されているのと同じ(動的)値から割り当てられた一意のinode番号を持ちます。偶然にも(この記事を書く時点までの私の経験によると)そこで生成された最初の名前はディレクトリですstat。したがって、このディレクトリは最後生成されたinode番号直前ネットワーク名前空間のインスタンス化。したがって、ネットワークネームスペースの独自のIDは/proc/net/statinode番号+ 1です。

もちろん/proc/net/stat、ディレクトリは実際にすべてのプロセスで見られるように「名前空間」名自体であり、必ずしも以下を参照するわけではありません。初期のネットワークネームスペース。プロセスがそのディレクトリにアクセスすると、初期ネットワーク名前空間を参照します。生活初期ネームスペースでは(つまり、コンテナ化されていないプロセス)、コンテナ化された環境では、初期ネットワークネームスペースではなく、プロセスが属するプライベートネットワークネームスペースを参照する可能性が高くなります。

尋ねる:それでは、プロセスは通常、自分のネットワーク名前空間が実際に最初のネットワーク名前空間であるかどうかを推測しようとしますか?

ㅏ:列挙で再帰的すべて表示PIDではなくinode番号が見つかる/procまで、そのディレクトリのファイル/ディレクトリでinode番号を見つけます。0xF0000001最初少なくとも2inode番号がありません。

pid以外の多くのファイル/ディレクトリは/proc(irq統計など)カーネルのコア機能に関連しているため、(これまで)すべてのPID名前空間に共通です。 inode番号の穴は次のようになります。少なくとも2近い1つは/proc/net/stat初期ネットワーク名前空間用に作成されたディレクトリ用で、もう1つは初期ネットワーク名前空間自体用です(2つの間のアトミック割り当ても想定しています)。最初のホールでは会議初期ネットワーク名前空間のID。そのID(脆弱性)をプロセス自体(または他の)ネットワーク名前空間のIDと比較すると(最も一般的な場合)、最終的にすべての設定が完了します。

しかし、一般的な場合でも、pidではなく名前に依存することは明らかです。いつもで見ることができるみんなPID名前空間そして名前空間のIDと同じ番号を共有します。そして(ほぼ)完璧な順序でそしてinode番号は、/proc/net/*名前空間の独自のIDとともに自動的に割り当てられます。これらすべての仮定可能現時点では本当ですが、この振る舞いは公式のAPIではないので、今後はもうそうではありません。

また、これがどれほど難しいかをより詳細に指摘するために、以下の内容を参照してください/procいつもプロセスのPID名前空間インストール済みそれ特定 /procディレクトリなので、必ずしもプロセスのPID名前空間である必要はありません。読むその/procディレクトリ。通常の実行では、/proc「インストール者」と「読者」の間に差/procが生じる可能性はほとんどありませんが、まだ完全に可能で一貫性のない分析に簡単につながる可能性があります。


いくつかの独占的な考慮事項

初期以外ユーザー検出が非常に簡単な名前空間1公式APIの一部である名前空間検出は、それをサポートする実用的で包括的なAPIがないため(可能であれば)解決するために多くの労力が必要な問題です。そして意図的)。数年前にいくつかありました。ioctl(2)タスクが名前空間のリストに追加されました。しかし、それでも非常に制限的であり、それを使用する方法(さえも言うべきではない方法)を理解することはできません。明確なテスト目的。

実際には、PID名前空間を検出するいくつかの簡単なトリックがありますが、公式のAPIではありません。systemd人々の例を見てください最近議論されたそれらのためのツール。明らかに、彼らはまた、デバイス番号が「3または4」であることを調査しましたが、それほどあまりproc保持していないことがわかったので、そのアイデアを放棄しました(いくら一般的にも「晴れた日」の条件でのみ維持された可能性があります)。彼らは常にカーネルスレッドが存在するPID 2を調べました。[kthreadd]これは初期のPIDネームスペースの確実な信号ですが、マウントを使用するとその検査に完全に違反するため、procそのアイデアもあきらめました。hidepid=[12]

名前空間検出の基本的な問題は、名前空間が本質的に任意であり、他の名前空間に完全に置き換えることができることです。カーネルにはすべてのネームスペースタイプに対していわゆる「初期」ネームスペースがありますが、最初のPID 1プロセス(またはその中のプロセス)は、始める前に単に-ingを実行してそれを別のプロセスにオーバーライドすることによってこれを実行することをinitramfs選択しますできます。unshare(2)プロセス。明らかに、この(そうでない)仮想条件で初期名前空間を検出するためのナビゲーションは有用な意味を失います。関連するのは「ホスト」ネームスペースだからです。名前空間です。ないオペレーティングシステム(例:基本PID 1initプロセス)仕事一度起動すると、これらの「ホスト」ネームスペースもすでに子供カーネルに関する限り、名前空間です。initプロセスが実際には常に初期名前空間を上書きするというわけではありませんが、原則としては可能です。これは名前空間検出ツールを損傷するのに十分です。

私が見ると、ほとんどの実際のユースケースではそうではありません。本物興味がある普段着名前空間。 UTS、IPC、cgroup、time名前空間にはまったく興味がないことはほとんど明らかで、おそらくuserとPID名前空間にも興味がないかもしれません。そうであれば、データと接続のアクセスに関連するマウントとネットワークの名前空間にのみ興味があります。 PIDネームスペースは、ユーザーネームスペースよりもはるかに多く検索されるため、頻繁に検索されます。ヒントより広い意味のコンテナと「より広い」コンテナは、興味深いマウントとネットワークの名前空間を提供します。残念ながら、後者は見つけるのが最も難しいです。これはおそらく検出ツールが緩くバインドされていますが、マウント/ネットワークネームスペース間の良好な関係を確立することを望んでいる間にPIDネームスペースを見つけることを好む理由です。

全体的に、これらのすべての「ifs」、「buts」、および注意事項とともに、問題は「初期または子」名前空間を検索しようとする努力がそれほど価値があるかどうかです。私は一般的にそうではないと言いたいです。まったく検出されない、または明確に定義された特定のユースケースに対して、「ホスト」ネームスペースの狭い定義のみを検出する方が良いでしょう。

ファタイ


/proc/self/uid_map1. ファイルを読み、レポートが 0 0 4294967295正しいことを確認します。

おすすめ記事