私がユーザーID1000であるとします。私はUID名前空間内でプログラムを実行し、名前空間内のUID0を名前空間外のUID1000にマッピングします。次に、プログラムは自身のUIDを50に変更しようとしますが、名前空間の外部に対応するUIDがありません。
プログラムは自身の(カーネル)UIDを正常に変更しますか?もしそうなら、そのUIDは名前空間の外部からのものであるように見えますか?それはまだ1000ですか、それとも他のものですか?
私は(ついに)FedoraRawhideでテストしました。私の最初の印象は正しいようです。名前空間内でseteuid(50)
を実行しても、名前空間内のuid 50が名前空間外のuidに対応していない場合、seteuidの呼び出しは単に失敗します。
ここでは、「kernel uid」を使用して、カーネル(およびユーザー名前空間外のほとんどのプロセス)から見たプロセスのuidを参照し、「subjective uid」を使用して、それ自体(および名前空間内の他のプロセス)。
まず、rootがseteuid(50)
を呼び出すとどうなるか見てみましょう。 Pythonを開いて、そのpidが何であるかを確認します。
[Fedora@ip-0-0-0-0 ~]$ Sudo python
Python 2.7.6 (default, Feb 4 2014, 15:36:52)
[GCC 4.8.2 20140120 (Red Hat 4.8.2-14)] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import os
>>> os.getpid()
1081
2番目のターミナルでは、Pythonのカーネルuidが実際には0(ルート)であることがわかります。
[Fedora@ip-0-0-0-0 ~]$ ls -ld /proc/1081
dr-xr-xr-x. 9 root root 0 Feb 7 03:27 /proc/1081
もちろん、主観的なuidも0です。
>>> os.geteuid()
0
そこで、uidを50に変更します。
>>> os.seteuid(50)
2番目のターミナルでは、カーネルuidが実際に50に変更されていることがわかります。
[Fedora@ip-0-0-0-0 ~]$ ls -ld /proc/1081
dr-xr-xr-x. 9 50 root 0 Feb 7 03:27 /proc/1081
では、「unshare」を使用して新しいユーザー名前空間を作成しましょう。
[Fedora@ip-0-0-0-0 ~]$ unshare --user python
Python 2.7.6 (default, Feb 4 2014, 15:36:52)
[GCC 4.8.2 20140120 (Red Hat 4.8.2-14)] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import os
>>> os.getpid()
1084
2番目のターミナルでは、このプロセスのカーネルuidが1000(Fedora)であることがわかります。
[Fedora@ip-0-0-0-0 ~]$ ls -ld /proc/1084
dr-xr-xr-x. 9 Fedora fedora 0 Feb 7 03:30 /proc/1084
しかし、最初の端末では、その主観的なuidが65534(マップされていない)であることがわかります。
>>> os.geteuid()
65534
したがって、2番目のターミナルでは、pythonマッピングを提供します:
[Fedora@ip-0-0-0-0 ~]$ echo '0 1000 1' > /proc/1084/uid_map
最初のターミナルでは、Pythonの主観的なuidが0になっていることがわかります。
>>> os.geteuid()
0
Pythonのuidを50に設定しようとしましたが、主観的なuid 50にはカーネルuidへのマッピングがないため、これは「無効な引数」(おそらくEINVAL)で失敗します。
>>> os.seteuid(50)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
OSError: [Errno 22] Invalid argument
2番目のターミナルでは、プロセスのカーネルuidが変更されていないことがわかります。
[Fedora@ip-0-0-0-0 ~]$ ls -ld /proc/1084
dr-xr-xr-x. 9 Fedora fedora 0 Feb 7 03:30 /proc/1084
そして最初のターミナルでは、プロセスの主観的なuidも変更されていないことがわかります。
>>> os.geteuid()
0
さて、複数のuidを含むマッピングを定義するとどうなりますか? LWN.netの記事 " 操作中の名前空間、パート5:ユーザー名前空間 "によると、pythonafteruidのマッピング(pythonを実行する前にuidをマッピングせずにこれを試しましたが、seteuid操作で「操作は許可されていません」と表示されました。)最初に、2番目の新しいユーザー名前空間でシェルを実行します。
[Fedora@ip-0-0-0-0 ~]$ unshare --user
id: cannot find name for user ID 65534
id: cannot find name for group ID 65534
id: cannot find name for user ID 65534
[I have no name!@ip-10-239-133-144 ~]$ echo $$
1798
次に、2番目のターミナルで、マッピングを定義するためにrootになる必要があります。
[root@ip-10-239-133-144 Fedora]# echo '0 1000 2' > /proc/1798/uid_map
whoami
コマンドによると、主観的なuidはrootになっているので、Pythonを開いて、主観的なuidを1に設定しましょう。
[I have no name!@ip-0-0-0-0 ~]$ whoami
root
[I have no name!@ip-0-0-0-0 ~]$ python
Python 2.7.6 (default, Feb 4 2014, 15:36:52)
[GCC 4.8.2 20140120 (Red Hat 4.8.2-14)] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import os
>>> os.seteuid(1)
>>> os.getpid()
1837
2番目のターミナルでは、予想どおり、カーネルuidが1001になっていることがわかります。
[root@ip-10-239-133-144 Fedora]# ls -ld /proc/1837
dr-xr-xr-x. 9 1001 Fedora 0 Feb 7 04:00 /proc/1837
テスト3では、seteuid()の呼び出しを成功させるために、Pythonを実行する前にマッピングを定義する必要がありました。テスト2では、これを行いませんでした。したがって、明らかな質問です。つまり、Pythonを実行する前にマッピングを定義した場合、seteuid()の呼び出しは機能しますか?答えはノーであることがわかります。最初のターミナル:
[Fedora@ip-0-0-0-0 ~]$ unshare --user
id: cannot find name for user ID 65534
id: cannot find name for group ID 65534
id: cannot find name for user ID 65534
[I have no name!@ip-0-0-0-0 ~]$ echo $$
1861
2番目のターミナル:
[Fedora@ip-0-0-0-0 ~]$ echo '0 1000 1' > /proc/1861/uid_map
最初のターミナル:
[I have no name!@ip-0-0-0-0 ~]$ whoami
root
[I have no name!@ip-0-0-0-0 ~]$ python
Python 2.7.6 (default, Feb 4 2014, 15:36:52)
[GCC 4.8.2 20140120 (Red Hat 4.8.2-14)] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import os
>>> os.geteuid()
0
>>> os.seteuid(50)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
OSError: [Errno 22] Invalid argument
ユーザー名前空間内でのseteuidの呼び出しを成功させるには、seteuidに渡されるuidに、ユーザー名前空間外のマッピングが必要です。それ以外の場合、呼び出しはEINVALで失敗します。
フォークした後、翻訳を定義する必要があります。
LWN.netのすばらしい記事からの抜粋:「プロセスのユーザーIDとグループIDを返すシステムコール(たとえば、getuid()
やgetgid()
)は、常にユーザー内に表示される資格情報を返します。呼び出しプロセスが存在する名前空間。ユーザーIDに名前空間内のマッピングがない場合、ユーザーIDを返すシステムコールは、ファイル_/proc/sys/kernel/overflowuid
_で定義された値を返します。これは、標準システムではデフォルト値65534です。最初、ユーザー名前空間にはユーザーIDマッピングがないため、名前空間内のすべてのユーザーIDはこの値にマップされます。同様に、新しいユーザー名前空間にはグループIDのマッピングがなく、マップされていないすべてのグループIDは_/proc/sys/kernel/overflowgid
_(デフォルトはoverflowuid
)と同じです。」
基本的に、子孫をclone(2)
オフすると、子のPIDが取得され、_/proc/CHILDPID/uid-map
_および_/proc/CHILDPID/gid-map
_のテーブルに追加して、の定義を開始できるようになります。翻訳。
私が状況を理解しているように、新しい名前空間内で翻訳なしでseteuid(50)
を実行すると、翻訳が定義されるまでeuidは65534のままであることがわかります。上記のように、一致する翻訳がない場合は、overflowuid
/overflowgid
が置き換えられます。
ほとんどの状況を見事に説明しているLWN.netの記事はここにあります: http://lwn.net/Articles/532593/