LXD コンテナが /dev/net/tun を開けない?unix-char エントリを確認する
VPN クライアントが使えるようになる前に、LXD がホストの TUN デバイスをコンテナへ渡す必要があります。unix-char が何を追加し、何を追加しないのか、どう確認するのかを見ます。
LXD はデバイスノードを追加することでホストのキャラクタデバイスをコンテナに公開しますが、ゲストに作らせるのではありません。コンテナに /dev/net/tun がなければ、ホストにデバイスがあってもゲストは開けないことがあります。unix-char エントリはこの経路の管理者側の半分であり、特権コンテナより意図的に狭い範囲です。だからホストのノードとコンテナのノードは別々に確認する価値があります。
仕組みは短く、見失いません:LXD はホストのパスを解決し、インスタンス内に対応するノードを作り、デバイスをコンテナのデバイス cgroup に加えます。その後、ゲストのプロセスはノードを何かに使うための独自の許可と権能を必要とします。
unix-char エントリが実際に変えるもの
unix-char デバイスは、ホストのキャラクタデバイスを一つ、コンテナ内の選んだパスに公開するよう LXD に指示します。エントリには重要な部分が三つあります:管理者が使うデバイスラベル、ホスト側の source パス、そしてゲストが見る path です。デバイスそのものを説明するのは最後の二つだけです。どちらかを打ち間違えると、クライアントが決して開かない場所にノードができます。
ホストでは、デバイスは通常 /dev/net/tun です。コンテナ内では同じパスを保つことも別のパスを選ぶこともできますが、パスは VPN クライアントが期待するものと一致しなければなりません。/dev/net/tun を保てば、ほとんどのクライアントがそのパスを尋ねずに開くため、二つ目の設定問題を避けられます。
管理者は、インスタンスの起動時またはデバイスのホットプラグ時にエントリを適用します。ゲストにドライバをコピーするわけでも、トンネルインターフェースを作るわけでもありません。ノードを見えるようにし、コンテナのデバイス制御グループ経由のアクセスを許可します。
デバイスはゲストの中ではなく、インスタンスで宣言する
LXD の管理コンテキストからエントリを追加し、インスタンスと後で見分けられるラベルを指定します。vpnbox という名前のコンテナなら、コマンドは次のとおりです:
lxc config device add vpnbox tun unix-char source=/dev/net/tun path=/dev/net/tun
lxc config device show vpnbox
ラベル tun はインスタンス設定に閉じたものです。インターフェース名ではなく、ゲストの何かと一致する必要もありません。プロファイルがすでに同じデバイスを提供している場合、二つ目のエントリは冗長ではなく競合になるため、まず有効な設定を確認してください。
コンテナ内のファイルを編集するのは間違った層です。起動スクリプトの mknod は特権コンテナでは動くように見えても、再作成で消えたり、特権の少ないコンテナで失敗したりします。インスタンスのデバイス集合は管理者が所有するので、ノードも同じく管理者に任せてください。
所有者とモードをゲストのプロセスに合わせる
unix-char エントリは、コンテナ内のノードに uid、gid、mode を設定できます。すべてのユーザーにデバイスを開いたり、コンテナを特権モードに変えたりする代わりに、VPN クライアントを動かすアカウントに合わせて使います。
見えることは許可と同じではありません。root が所有しモード 600 のノードは、非特権ユーザーで動くサービスから見えますが、そのサービスは開けません。まずゲストのアカウントを確認し、次に所有者をそのアカウントにし、モードはクライアントが必要とする以上に広げないでください。
プロセスには トンネルインターフェースを設定する権能 も必要で、通常はコンテナのネットワーク名前空間における CAP_NET_ADMIN です。デバイスを渡してもその権能は与えられず、権能を与えてもデバイスは用意されません——両者は独立した要件です。
エントリが与えないものを知る
見えるノードは動く VPN ではありません。 クライアントのパッケージはまだ必要で、プロファイルはまだ有効でなければならず、サービスはまだ起動しなければなりません。認証、ルート、DNS はクライアントの仕事のままで、そこでの失敗はデバイスエントリが誤っている証拠ではありません。
systemd がさらに制限を加えることがあります。DeviceAllow や強化されたデバイスポリシーを持つユニットは、コンテナ管理者が用意したノードへのアクセスを拒否できます。ノードはあるのにサービスが許可エラーを報告するときは、ユニットのデバイスポリシーを確認し、コンテナを広げるのではなくそのポリシーを変えてください。
ホットプラグは任意で明示的です。required=false を持つエントリはソースパスを必要とし、ホストのデバイスが変わると現れたり消えたりします。そのライフサイクルは出入りのあるハードウェアに向きますが、VPN が起動時に必要とする TUN デバイスでは、任意のエントリだと前提ができる前にサービスが起動しかねません。
見えること、開けること、インターフェースの順に確認する
まず意図したコンテナ内のノードを確認します。パス、メジャー番号とマイナー番号、所有者はホストのデバイスと追加したエントリに一致するはずです。ノードがないなら、管理者がエントリを適用していないか、別のインスタンスに適用しています。
lxc exec vpnbox -- ls -l /dev/net/tun
lxc exec vpnbox -- sh -c 'test -c /dev/net/tun && echo char-device-ok'
次に、サービスのアカウントが開けることを確認します。そのアカウントでの短いテストは、所有者の問題とサービスの問題を切り分けます。最後にクライアントを起動し、トンネルインターフェースが現れることを確認します——最初の二つが通ってもクライアントのプロファイルや権能がまだ誤っていることがあるため、全体の経路を証明するのはこの三番目だけです。
変更が承認されたら、インスタンス、デバイスラベル、ホストのソース、ゲストのパス、所有アカウントを記録します。その五つの値こそ、後の再作成、移行、プロファイル変更が保つべきもので、文脈のない許可エラーよりはるかに比べやすいものです。