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:
- Izinkan panggilan sistem yang diperlukan.
- Sistem blokir panggilan yang diketahui tidak berguna.
- 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: