Mengapa Docker tidak berfungsi di Docker?

Ketika saya mengedit halaman kemampuan kontainer untuk majalah How Containers Work , saya perlu menjelaskan mengapa Docker tidak berfungsi strace. Inilah yang terjadi ketika berjalan stracedi wadah Docker di laptop saya:



$ docker run  -it ubuntu:18.04 /bin/bash
$ # ... install strace ...
root@e27f594da870:/# strace ls
strace: ptrace(PTRACE_TRACEME, ...): Operation not permitted


straceberfungsi melalui panggilan sistem ptrace, sehingga ptracetidak akan berfungsi tanpa izin ! Tapi itu mudah diperbaiki, dan di laptop saya melakukannya seperti ini:



docker run --cap-add=SYS_PTRACE  -it ubuntu:18.04 /bin/bash


Tetapi menarik bagi saya untuk tidak menyelesaikan masalah, tetapi untuk mencari tahu mengapa situasi ini umumnya muncul. Jadi mengapa tidak straceberhasil, tetapi --cap-add=SYS_PTRACEmemperbaiki semuanya?



Hipotesis 1: Proses wadah tidak memiliki hak istimewa mereka sendiri CAP_SYS_PTRACE



Karena masalah ini diselesaikan secara konsisten --cap-add=SYS_PTRACE, bagi saya selalu tampak bahwa proses kontainer Docker, menurut definisi, tidak memiliki hak istimewa sendiri CAP_SYS_PTRACE, tetapi karena dua alasan ada sesuatu yang tidak ditambahkan di sini.



Alasan 1: Sebagai percobaan, saya, yang masuk sebagai pengguna biasa, dapat dengan mudah memulai straceproses apa pun, namun, saya CAP_SYS_PTRACEtidak menemukan apa pun dalam keistimewaan proses saya saat ini :



$ getpcaps $$
Capabilities for `11589': =


Alasan 2: dalam man capabilitieshak istimewa CAP_SYS_PTRACEberbunyi sebagai berikut:



CAP_SYS_PTRACE
       * Trace arbitrary processes using ptrace(2);


Intinya CAP_SYS_PTRACEadalah bahwa, dengan analogi dengan root, kita dapat mengendalikan proses sembarang pengguna. Untuk ptracepengguna Anda, hak istimewa ini tidak memerlukan proses konvensional.



Selain itu, saya melakukan satu pemeriksaan lagi: Saya meluncurkan wadah Docker melalui docker run --cap-add=SYS_PTRACE -it ubuntu:18.04 /bin/bash, kemudian mencabut hak istimewa CAP_SYS_PTRACE- dan straceterus bekerja dengan benar bahkan tanpa hak istimewa. Mengapa?!



Hipotesis 2: Apakah ini namespace khusus?



Hipotesis saya berikutnya (dan jauh lebih tidak beralasan) terdengar seperti "hmm, mungkin prosesnya dalam ruang nama pengguna yang berbeda dan stracetidak berfungsi ... hanya karena?" Sepertinya seperangkat pernyataan yang tidak terlalu koheren, tapi saya masih mencoba melihat masalah dari sudut pandang ini.



Jadi, apakah proses dalam ruang nama yang ditentukan pengguna berbeda? Ini adalah tampilannya di wadah:



root@e27f594da870:/# ls /proc/$$/ns/user -l
... /proc/1/ns/user -> 'user:[4026531837]'


Dan begini tampilannya pada host:



bork@kiwi:~$ ls /proc/$$/ns/user -l
... /proc/12177/ns/user -> 'user:[4026531837]'


root dalam wadah adalah pengguna yang sama dengan root pada host, karena mereka memiliki pengidentifikasi usernamespace yang umum (4026531837), jadi seharusnya tidak ada stracealasan yang mengganggu di sisi itu . Seperti yang Anda lihat, hipotesisnya ternyata begitu-begitu, tetapi kemudian saya belum menyadari bahwa pengguna dalam wadah dan pada host adalah sama, dan pendekatan ini tampak menarik bagi saya.



Hipotesis 3: Panggilan sistem ptracediblokir oleh suatu aturanseccomp-bpf



Saya sudah tahu bahwa ada aturan di Docker untuk membatasi sejumlah besar panggilan sistem untuk dijalankan oleh pemroses kontainer di Docker seccomp-bpf, dan ternyata ada dan dalam daftar panggilannya diblokir menurut definisi ptrace! (Faktanya, daftar doa adalah daftar pengecualian dan ptracetidak masuk ke dalamnya, tetapi hasilnya tidak berubah.)



Sekarang jelas mengapa itu tidak bekerja dalam wadah Docker strace, karena jelas bahwa ptracepanggilan yang diblokir sepenuhnya tidak akan berfungsi.



Mari kita uji hipotesis ini dan lihat apakah kita dapat menggunakan stracewadah Docker jika kita menonaktifkan semua aturan seccomp:



$ docker run --security-opt seccomp=unconfined -it ubuntu:18.04  /bin/bash
$ strace ls
execve("/bin/ls", ["ls"], 0x7ffc69a65580 /* 8 vars */) = 0
... it works fine ...


Baik! Semuanya berfungsi, dan rahasianya terungkap! Itu hanya ...



Mengapa --cap-add=SYS_PTRACEitu memecahkan masalah?



Kami masih belum menjelaskan mengapa ini --cap-add=SYS_PTRACEmemecahkan masalah tantangan yang muncul. Halaman utama docker runmenjelaskan cara kerja argumen sebagai berikut --cap-add:



--cap-add=[]
   Add Linux capabilities


Semua ini tidak ada hubungannya dengan aturan seccomp! Apa masalahnya?



Mari kita lihat kode sumber Docker.



Jika dokumentasi tidak membantu, yang harus kita lakukan adalah menyelami sumbernya.

Satu hal yang menyenangkan tentang Go adalah dengan membatalkan dependensi dalam repositori Go, Anda grepdapat menelusuri seluruh repositori dan menemukan kode yang Anda minati. Jadi saya github.com/moby/mobymengkloning dan menjelajahinya untuk ekspresi semacam itu rg CAP_SYS_PTRACE.



Menurut pendapat saya, inilah yang terjadi: dalam pelaksanaan seccomp di dalam wadah, di bagian contrib / seccomp / seccomp_default.go ada banyak kode yang, melalui aturan seccomp, memeriksa apakah proses dengan hak istimewa memiliki izin untuk menggunakan panggilan sistem sesuai dengan hak istimewa ini.



		case "CAP_SYS_PTRACE":
			s.Syscalls = append(s.Syscalls, specs.LinuxSyscall{
				Names: []string{
					"kcmp",
					"process_vm_readv",
					"process_vm_writev",
					"ptrace",
				},
				Action: specs.ActAllow,
				Args:   []specs.LinuxSeccompArg{},
			})




Ada juga kode di sana, yang di moby dan untuk profil / seccomp / seccomp.go , dan untuk seccomp profil, menurut definisi, melakukan operasi yang sama, jadi kami mungkin menemukan jawaban kami!



Docker --cap-addmampu melakukan lebih dari apa yang dikatakan



Pada akhirnya, tampaknya --cap-addtidak persis seperti yang dikatakan di halaman utama, dan lebih baik terlihat seperti --cap-add-and-also-whitelist-some-extra-system-calls-if-required. Dan tampaknya benar: jika Anda memiliki hak istimewa roh CAP_SYS_PTRACE, yang memungkinkan Anda untuk menggunakan panggilan sistem process_vm_readv, tetapi panggilan tersebut diblokir profil Seccomp, Anda tidak banyak membantu, sehingga otorisasi untuk menggunakan panggilan sistem process_vm_readvdan ptracemelalui CAP_SYS_PTRACEterlihat masuk akal.



Ternyata straceberfungsi di versi terbaru Docker



Untuk versi kernel 4.8 dan lebih tinggi, berkat komit ini , Docker 19.03 akhirnya mengizinkan panggilan sistem ptrace. Kecuali, di laptop saya, Docker masih versi 18.09.7 dan komit ini jelas hilang.



Itu saja!



Ternyata menarik untuk menangani masalah ini, dan saya pikir ini adalah contoh yang baik dari interaksi "pengisian" kontainer secara nontrivial.



Jika Anda menyukai posting ini, Anda mungkin juga menyukai majalah saya How Containers Work , yang menjelaskan fitur penanganan kontainer 24-halaman kernel Linux. Anda juga dapat melihat hak istimewa dan seccomp-bpf di sana .



All Articles