Seccomp di Kubernetes: 7 Hal yang Perlu Anda Ketahui Dari Awal

Catatan perev. : Menyajikan terjemahan sebuah artikel oleh seorang insinyur keamanan aplikasi senior di perusahaan Inggris ASOS.com. Dengan dia, dia memulai serangkaian publikasi tentang peningkatan keamanan di Kubernetes melalui penggunaan seccomp. Jika pembaca akan menyukai pengantar, kami akan mengikuti penulis dan melanjutkan dengan materi masa depannya tentang topik ini.



Artikel ini adalah yang pertama dari serangkaian publikasi tentang cara membuat profil seccomp dalam semangat SecDevOps tanpa menggunakan sihir dan sihir. Pada bagian pertama, saya akan berbicara tentang dasar-dasar dan detail internal implementasi seccomp di Kubernetes.

Ekosistem Kubernetes menawarkan berbagai cara untuk memastikan keamanan dan isolasi wadah. Artikel ini adalah tentang Mode Komputasi Aman, juga dikenal sebagai seccomp . Esensinya terletak pada panggilan sistem penyaringan yang tersedia untuk dijalankan oleh kontainer.

Mengapa ini penting? Wadah hanyalah proses yang berjalan pada mesin tertentu. Dan ia menggunakan kernel yang setara dengan aplikasi lain. Jika wadah dapat melakukan panggilan sistem apa pun, segera malware akan memanfaatkannya untuk memintas isolasi wadah dan memengaruhi aplikasi lain: mencegat informasi, mengubah pengaturan sistem, dll.

Profil seccomp menentukan panggilan sistem mana yang harus diizinkan atau ditolak. Kontainer runtime mengaktifkannya selama peluncurannya, sehingga kernel dapat mengontrol eksekusi mereka. Penggunaan profil semacam itu memungkinkan Anda membatasi vektor serangan dan mengurangi kerusakan jika ada program di dalam wadah (yaitu, dependensi Anda, atau dependensinya) mulai melakukan apa yang tidak boleh dilakukan.

Memahami dasar-dasarnya


Profil dasar seccomp mencakup tiga elemen: defaultAction , architectures (atau archMap ) dan syscalls :

 { "defaultAction": "SCMP_ACT_ERRNO", "architectures": [ "SCMP_ARCH_X86_64", "SCMP_ARCH_X86", "SCMP_ARCH_X32" ], "syscalls": [ { "names": [ "arch_prctl", "sched_yield", "futex", "write", "mmap", "exit_group", "madvise", "rt_sigprocmask", "getpid", "gettid", "tgkill", "rt_sigaction", "read", "getpgrp" ], "action": "SCMP_ACT_ALLOW" } ] } 

( medium-basic-seccomp.json )

defaultAction menentukan nasib default setiap panggilan sistem yang tidak ditentukan di bagian syscalls . Untuk menyederhanakan tugas, kami fokus pada dua nilai utama yang akan digunakan:

  • SCMP_ACT_ERRNO - memblokir eksekusi panggilan sistem,
  • SCMP_ACT_ALLOW - memungkinkan.

Bagian architectures mendaftar arsitektur target. Ini penting, karena filter itu sendiri, diterapkan pada level kernel, tergantung pada pengidentifikasi panggilan sistem, dan bukan pada nama mereka yang terdaftar dalam profil. Sebelum digunakan, runtime kontainer memetakannya ke pengidentifikasi. Intinya adalah bahwa panggilan sistem dapat memiliki ID yang sama sekali berbeda, tergantung pada arsitektur sistem. Misalnya, panggilan sistem recvfrom (digunakan untuk mendapatkan informasi dari soket) memiliki ID = 64 pada sistem x64 dan ID = 517 pada x86. Di sini Anda dapat menemukan daftar semua pemanggilan sistem untuk arsitektur x86-x64.

Bagian syscalls mencantumkan semua panggilan sistem dan menunjukkan apa yang harus dilakukan dengannya. Misalnya, Anda dapat membuat daftar putih dengan menetapkan defaultAction ke SCMP_ACT_ERRNO , dan menetapkan panggilan ke bagian SCMP_ACT_ALLOW ke SCMP_ACT_ALLOW . Dengan demikian, Anda hanya mengizinkan panggilan yang terdaftar di bagian syscalls dan melarang semua yang lain. Untuk daftar hitam, Anda harus mengubah nilai dan tindakan defaultAction ke sebaliknya.

Sekarang beberapa kata harus dikatakan tentang nuansa yang tidak begitu jelas. Harap perhatikan bahwa rekomendasi di bawah ini berasal dari kenyataan bahwa Anda sedang menyebarkan serangkaian aplikasi bisnis di Kubernetes dan penting bagi Anda bahwa mereka bekerja dengan hak istimewa yang paling rendah.

1. AllowPrivilegeEscalation = false


Ada parameter AllowPrivilegeEscalation di securityContext wadah. Jika disetel ke false , wadah akan mulai dengan bit no_new_priv disetel ke ( on ). Arti dari parameter ini jelas dari namanya: itu tidak memungkinkan wadah untuk memulai proses baru dengan hak istimewa lebih besar daripada yang dimilikinya.

Efek samping dari parameter ini disetel ke true (nilai default) adalah bahwa runtime kontainer menerapkan profil seccomp pada awal proses startup. Dengan demikian, semua panggilan sistem yang diperlukan untuk memulai proses internal runtime (misalnya, mengatur pengidentifikasi pengguna / grup, menjatuhkan beberapa kemampuan) harus diizinkan dalam profil.

Wadah yang melakukan echo hi dangkal akan memerlukan izin berikut:

 { "defaultAction": "SCMP_ACT_ERRNO", "architectures": [ "SCMP_ARCH_X86_64", "SCMP_ARCH_X86", "SCMP_ARCH_X32" ], "syscalls": [ { "names": [ "arch_prctl", "brk", "capget", "capset", "chdir", "close", "execve", "exit_group", "fstat", "fstatfs", "futex", "getdents64", "getppid", "lstat", "mprotect", "nanosleep", "newfstatat", "openat", "prctl", "read", "rt_sigaction", "statfs", "setgid", "setgroups", "setuid", "stat", "uname", "write" ], "action": "SCMP_ACT_ALLOW" } ] } 

( hi-pod-seccomp.json )

... alih-alih ini:

 { "defaultAction": "SCMP_ACT_ERRNO", "architectures": [ "SCMP_ARCH_X86_64", "SCMP_ARCH_X86", "SCMP_ARCH_X32" ], "syscalls": [ { "names": [ "arch_prctl", "brk", "close", "execve", "exit_group", "futex", "mprotect", "nanosleep", "stat", "write" ], "action": "SCMP_ACT_ALLOW" } ] } 

( hi-container-seccomp.json )

Tetapi sekali lagi, mengapa ini menjadi masalah? Secara pribadi, saya akan menghindari daftar putih panggilan sistem berikut (jika tidak benar-benar diperlukan): capset , set_tid_address , setgid , setgroups dan setuid . Namun, kesulitan sebenarnya adalah bahwa dengan memungkinkan proses yang sama sekali tidak Anda kontrol, Anda mengikat profil ke implementasi runtime kontainer. Dengan kata lain, suatu hari Anda mungkin menemukan bahwa setelah memperbarui lingkungan runtime wadah (oleh Anda atau, lebih mungkin, oleh penyedia layanan cloud), wadah tiba-tiba berhenti mulai.

Kiat # 1 : Jalankan kontainer dengan AllowPrivilegeEscaltion=false . Ini akan mengurangi ukuran profil seccomp dan membuatnya kurang sensitif terhadap perubahan runtime kontainer.

2. Mengatur profil keamanan tingkat kontainer


Profil seccomp dapat diatur di level pod:

 annotations: seccomp.security.alpha.kubernetes.io/pod: "localhost/profile.json" 

... atau di tingkat kontainer:

 annotations: container.security.alpha.kubernetes.io/<container-name>: "localhost/profile.json" 

Harap perhatikan bahwa sintaks di atas akan berubah ketika Kubernetes seccomp menjadi GA (acara ini diharapkan dalam rilis Kubernetes berikutnya - 1,18 - sekitar Terjemahan.).

Hanya sedikit orang yang tahu bahwa Kubernetes selalu memiliki bug yang menyebabkan profil seccomp diterapkan pada wadah jeda . Runtime sebagian mengkompensasi kelemahan ini, tetapi wadah ini tidak hilang dari polong, karena digunakan untuk mengkonfigurasi infrastruktur mereka.

Masalahnya adalah bahwa wadah ini selalu dimulai dengan AllowPrivilegeEscalation=true , mengarah ke masalah yang AllowPrivilegeEscalation=true dalam paragraf 1, dan ini tidak dapat diubah.

Menerapkan profil keamanan di tingkat wadah, Anda menghindari perangkap ini dan dapat membuat profil yang akan "diasah" untuk wadah tertentu. Ini harus dilakukan sampai pengembang memperbaiki bug dan versi baru (mungkin 1,18?) Tersedia untuk semua orang.

Kiat # 2 : Tetapkan profil seccomp di tingkat wadah.

Dalam arti praktis, aturan ini biasanya berfungsi sebagai jawaban universal untuk pertanyaan: "Mengapa profil seccomp saya berfungsi dengan docker run , tetapi itu tidak berfungsi setelah ditempatkan di cluster Kubernetes?"

3. Gunakan runtime / default sebagai pilihan terakhir


Kubernetes memiliki dua opsi untuk profil runtime/default : runtime/default dan docker/default . Keduanya diimplementasikan oleh runtime kontainer, bukan Kubernetes. Oleh karena itu, mereka mungkin berbeda tergantung pada runtime yang digunakan dan versinya.

Dengan kata lain, sebagai akibat dari mengubah runtime, wadah dapat mengakses serangkaian panggilan sistem lain yang dapat digunakan atau tidak digunakan. Kebanyakan runtime menggunakan implementasi Docker . Jika Anda ingin menggunakan profil ini, pastikan itu cocok untuk Anda.

docker/default profil docker/default sudah tidak digunakan lagi sejak Kubernetes 1.11, jadi hindari menggunakannya.

Menurut pendapat saya, profil runtime/default sangat cocok untuk tujuan yang dibuat: untuk melindungi pengguna dari risiko yang terkait dengan docker run pada mesin mereka. Namun, jika kita berbicara tentang aplikasi bisnis yang berjalan di cluster Kubernetes, saya berani mengklaim bahwa profil seperti itu terlalu terbuka dan pengembang harus berkonsentrasi pada pembuatan profil untuk aplikasi mereka (atau jenis aplikasi).

Kiat # 3 : Buat profil seccomp untuk aplikasi tertentu. Jika ini tidak memungkinkan, berurusan dengan profil untuk jenis aplikasi, misalnya, buat profil lanjutan yang mencakup semua API aplikasi web Golang. Hanya sebagai pilihan terakhir gunakan runtime / default.

Dalam publikasi mendatang, saya akan memberi tahu Anda cara membuat profil secccomp dengan semangat SecDevOps, mengotomatisasi, dan mengujinya dalam saluran pipa. Dengan kata lain, Anda tidak memiliki alasan untuk tidak beralih ke profil untuk aplikasi tertentu.

4. Tidak terbatas BUKAN pilihan


Dari audit keamanan Kubernetes pertama, ternyata seccomp dinonaktifkan secara default. Ini berarti bahwa jika Anda tidak menentukan PodSecurityPolicy yang akan mengaktifkannya di cluster, semua pods yang tidak didefinisikan profil seccomp akan bekerja di seccomp=unconfined .

Bekerja dalam mode ini berarti bahwa seluruh lapisan isolasi hilang, yang memberikan perlindungan cluster. Pendekatan ini tidak direkomendasikan oleh para profesional keamanan.

Kiat # 4 : Tidak ada wadah dalam sebuah cluster yang dapat berfungsi dalam seccomp=unconfined , terutama di lingkungan produksi.

5. "Mode audit"


Poin ini tidak unik untuk Kubernetes, tetapi masih masuk dalam kategori "apa yang harus Anda ketahui sebelum Anda mulai."

Kebetulan membuat profil seccomp selalu merupakan bisnis yang rumit dan sebagian besar didasarkan pada coba-coba. Faktanya adalah bahwa pengguna tidak memiliki kesempatan untuk mengujinya di lingkungan produksi tanpa risiko "menjatuhkan" aplikasi.

Setelah munculnya kernel Linux 4.14, menjadi mungkin untuk menjalankan bagian profil dalam mode audit, merekam informasi tentang semua panggilan sistem di syslog, tetapi tidak memblokirnya. Anda dapat mengaktifkan mode ini menggunakan parameter SCMT_ACT_LOG :

SCMP_ACT_LOG : seccomp tidak akan memengaruhi operasi utas membuat panggilan sistem jika tidak masuk dalam aturan apa pun dari filter, tetapi informasi tentang panggilan sistem akan dicatat.

Berikut adalah contoh strategi untuk menggunakan fitur ini:

  1. Izinkan panggilan sistem yang diperlukan.
  2. Sistem blokir panggilan yang diketahui tidak berguna.
  3. Rekam informasi tentang semua panggilan lain dalam log.

Contoh yang disederhanakan adalah sebagai berikut:

 { "defaultAction": "SCMP_ACT_LOG", "architectures": [ "SCMP_ARCH_X86_64", "SCMP_ARCH_X86", "SCMP_ARCH_X32" ], "syscalls": [ { "names": [ "arch_prctl", "sched_yield", "futex", "write", "mmap", "exit_group", "madvise", "rt_sigprocmask", "getpid", "gettid", "tgkill", "rt_sigaction", "read", "getpgrp" ], "action": "SCMP_ACT_ALLOW" }, { "names": [ "add_key", "keyctl", "ptrace" ], "action": "SCMP_ACT_ERRNO" } ] } 

( menengah-campuran-seccomp.json )

Tetapi ingat bahwa Anda perlu memblokir semua panggilan yang diketahui tidak digunakan dan yang berpotensi membahayakan cluster. Dasar yang baik untuk mendaftar adalah dokumentasi Docker resmi. Ini menjelaskan secara rinci panggilan sistem mana yang diblokir di profil default dan alasannya.

Namun, ada satu tangkapan. Meskipun SCMT_ACT_LOG didukung oleh kernel Linux sejak akhir 2017, ia baru saja memasuki ekosistem Kubernetes. Oleh karena itu, untuk menggunakan metode ini, Anda memerlukan kernel Linux 4.14 dan versi runC tidak lebih rendah dari v1.0.0-rc9 .

Kiat # 5 : Anda dapat membuat profil mode audit untuk pengujian dalam produksi dengan menggabungkan daftar hitam dan putih, dan mencatat semua pengecualian.

6. Gunakan daftar putih


Membuat daftar putih memerlukan upaya tambahan, karena Anda harus mengidentifikasi setiap panggilan yang mungkin diperlukan aplikasi, tetapi pendekatan ini secara signifikan meningkatkan keamanan:

Sangat disarankan agar Anda menggunakan pendekatan daftar putih karena lebih sederhana dan lebih dapat diandalkan. Daftar hitam perlu diperbarui setiap kali panggilan sistem yang berpotensi berbahaya (atau bendera / opsi berbahaya jika ada dalam daftar hitam) ditambahkan. Selain itu, Anda seringkali dapat mengubah penyajian parameter tanpa mengubah esensinya dan dengan demikian menghindari batasan daftar hitam.

Untuk aplikasi Go, saya mengembangkan alat khusus yang menyertai aplikasi dan mengumpulkan semua panggilan yang dilakukan saat runtime. Misalnya, untuk aplikasi berikut:

 package main import "fmt" func main() { fmt.Println("test") } 

... jalankan gosystract seperti ini:

 go install https://github.com/pjbgf/gosystract gosystract --template='{{- range . }}{{printf "\"%s\",\n" .Name}}{{- end}}' application-path 

... dan dapatkan hasil berikut:

 "sched_yield", "futex", "write", "mmap", "exit_group", "madvise", "rt_sigprocmask", "getpid", "gettid", "tgkill", "rt_sigaction", "read", "getpgrp", "arch_prctl", 

Sejauh ini hanya sebuah contoh - detail tentang alat akan lebih jauh.

Kiat # 6 : Hanya izinkan panggilan yang benar-benar Anda butuhkan dan blokir semua orang.

7. Lay foundation


Kernel akan memantau kepatuhan terhadap profil apa pun yang telah Anda daftarkan di dalamnya. Bahkan jika ini tidak seperti yang saya inginkan. Misalnya, jika Anda memblokir akses ke panggilan seperti exit atau exit_group , wadah tidak akan dapat menyelesaikan pekerjaan dengan benar dan bahkan perintah sederhana seperti echo hi menangguhkannya untuk jangka waktu yang tidak ditentukan. Akibatnya, Anda akan mendapatkan utilisasi CPU tinggi di kluster:



Dalam kasus seperti itu, utilitas strace mungkin datang untuk menyelamatkan - itu akan menunjukkan apa masalahnya:


sudo strace -c -p 9331

Pastikan bahwa profil berisi semua panggilan sistem yang dibutuhkan aplikasi saat sedang berjalan.

Tip # 7 : Perhatikan hal-hal kecil dan pastikan bahwa semua panggilan sistem yang diperlukan termasuk dalam daftar putih.

Dengan ini, bagian pertama dari serangkaian artikel tentang menggunakan seccomp di Kubernetes dalam semangat SecDevOps berakhir. Pada bagian berikut ini kita akan membahas mengapa ini penting dan bagaimana mengotomatiskan proses.

PS dari penerjemah


Baca juga di blog kami:

Source: https://habr.com/ru/post/id481114/


All Articles