Seccomp di Kubernetes: 7 hal yang perlu Anda ketahui sejak awal

Catatan. terjemahan: Untuk perhatian Anda, kami mempersembahkan terjemahan artikel oleh insinyur keamanan aplikasi senior di perusahaan Inggris ASOS.com. Dengan itu, ia memulai serangkaian publikasi yang didedikasikan untuk meningkatkan keamanan di Kubernetes melalui penggunaan seccomp. Jika pembaca menyukai pendahuluan, kami akan mengikuti penulis dan melanjutkan materi selanjutnya tentang topik ini.

Seccomp di Kubernetes: 7 hal yang perlu Anda ketahui sejak awal

Artikel ini adalah yang pertama dari serangkaian postingan tentang cara membuat profil seccomp dalam semangat SecDevOps, tanpa menggunakan sihir dan sihir. Di Bagian XNUMX, saya akan membahas dasar-dasar dan detail internal penerapan seccomp di Kubernetes.

Ekosistem Kubernetes menawarkan berbagai cara untuk mengamankan dan mengisolasi container. Artikel ini membahas tentang Mode Komputasi Aman, juga dikenal sebagai detik. Esensinya adalah memfilter panggilan sistem yang tersedia untuk dieksekusi oleh container.

Mengapa ini penting? Kontainer hanyalah sebuah proses yang berjalan pada mesin tertentu. Dan menggunakan kernel sama seperti aplikasi lainnya. Jika container dapat melakukan panggilan sistem apa pun, malware akan segera memanfaatkan hal ini untuk melewati isolasi container dan memengaruhi aplikasi lain: mencegat informasi, mengubah pengaturan sistem, dll.

Profil seccomp menentukan panggilan sistem mana yang harus diizinkan atau dinonaktifkan. Runtime container mengaktifkannya saat dijalankan sehingga kernel dapat memantau eksekusinya. Menggunakan profil seperti itu memungkinkan Anda membatasi vektor serangan dan mengurangi kerusakan jika ada program di dalam wadah (yaitu, dependensi Anda, atau dependensinya) mulai melakukan sesuatu yang tidak boleh dilakukan.

Memahami dasar-dasarnya

Profil seccomp dasar 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-dasar-seccomp.json)

defaultAction menentukan nasib default dari setiap panggilan sistem yang tidak ditentukan di bagian tersebut syscalls. Untuk mempermudah, mari kita fokus pada dua nilai utama yang akan digunakan:

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

Pada bagian architectures arsitektur target terdaftar. Hal ini penting karena filter itu sendiri, yang diterapkan pada tingkat kernel, bergantung pada pengidentifikasi panggilan sistem, dan bukan pada namanya yang ditentukan dalam profil. Waktu proses kontainer akan mencocokkannya dengan pengidentifikasi sebelum digunakan. Idenya adalah bahwa panggilan sistem dapat memiliki ID yang sangat berbeda tergantung pada arsitektur sistem. Misalnya, panggilan sistem recvfrom (digunakan untuk menerima informasi dari soket) memiliki ID = 64 pada sistem x64 dan ID = 517 pada x86. Di sini Anda dapat menemukan daftar semua panggilan sistem untuk arsitektur x86-x64.

Di bagian syscalls mencantumkan semua panggilan sistem dan menentukan apa yang harus dilakukan dengannya. Misalnya, Anda dapat membuat daftar putih dengan mengatur defaultAction pada SCMP_ACT_ERRNO, dan panggilan di bagian tersebut syscalls menetapkan SCMP_ACT_ALLOW. Jadi, Anda hanya mengizinkan panggilan yang ditentukan di bagian tersebut syscalls, dan melarang semua yang lain. Untuk daftar hitam Anda harus mengubah nilainya defaultAction dan tindakan sebaliknya.

Sekarang kita harus mengatakan beberapa kata tentang nuansa yang tidak begitu jelas. Harap dicatat bahwa rekomendasi di bawah ini mengasumsikan bahwa Anda menerapkan serangkaian aplikasi bisnis di Kubernetes dan Anda ingin aplikasi tersebut dijalankan dengan hak istimewa sesedikit mungkin.

1. AllowPrivilegeEscalation=salah

В securityContext wadah memiliki parameter AllowPrivilegeEscalation. Jika sudah terpasang di false, container akan dimulai dengan (on) sedikit no_new_priv. Arti dari parameter ini terlihat jelas dari namanya: parameter ini mencegah container meluncurkan proses baru dengan hak istimewa lebih dari yang dimilikinya.

Efek samping dari opsi ini yang disetel true (default) adalah runtime container menerapkan profil seccomp di awal proses startup. Dengan demikian, semua panggilan sistem yang diperlukan untuk menjalankan proses runtime internal (misalnya mengatur ID pengguna/grup, menghilangkan kemampuan tertentu) harus diaktifkan di profil.

Ke wadah yang melakukan hal-hal sepele echo hi, izin berikut akan diperlukan:

{
    "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)

...bukannya 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"
        }
    ]
}

(hai-container-seccomp.json)

Tapi sekali lagi, mengapa ini menjadi masalah? Secara pribadi, saya akan menghindari memasukkan panggilan sistem berikut ke dalam daftar putih (kecuali benar-benar diperlukan): capset, set_tid_address, setgid, setgroups и setuid. Namun, tantangan sebenarnya adalah dengan mengizinkan proses yang sama sekali tidak dapat Anda kendalikan, Anda mengikat profil ke implementasi runtime container. Dengan kata lain, suatu hari Anda mungkin menemukan bahwa setelah memperbarui lingkungan runtime container (baik oleh Anda atau, kemungkinan besar, oleh penyedia layanan cloud), container tiba-tiba berhenti berjalan.

Tips #1: Jalankan container dengan AllowPrivilegeEscaltion=false. Hal ini akan mengurangi ukuran profil seccomp dan membuatnya kurang sensitif terhadap perubahan di lingkungan runtime container.

2. Mengatur profil seccomp di level container

Profil seccomp dapat diatur di level pod:

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

...atau di tingkat penampung:

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

Harap dicatat bahwa sintaks di atas akan berubah ketika Kubernetes dikompilasi akan menjadi GA (peristiwa ini diharapkan pada rilis Kubernetes berikutnya - 1.18 - kira-kira terjemahan).

Hanya sedikit orang yang tahu bahwa Kubernetes selalu memilikinya seranggayang menyebabkan profil seccomp diterapkan jeda wadah. Lingkungan runtime mengkompensasi sebagian kekurangan ini, namun container ini tidak hilang dari pod, karena digunakan untuk mengkonfigurasi infrastrukturnya.

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

Dengan menggunakan profil seccomp di tingkat kontainer, Anda menghindari jebakan ini dan dapat membuat profil yang disesuaikan dengan kontainer tertentu. Ini harus dilakukan sampai pengembang memperbaiki bug dan versi baru (mungkin 1.18?) tersedia untuk semua orang.

Tips #2: Tetapkan profil seccomp di tingkat kontainer.

Dalam arti praktis, aturan ini biasanya berfungsi sebagai jawaban universal terhadap pertanyaan: “Mengapa profil seccomp saya berfungsi docker runtetapi tidak berfungsi setelah diterapkan ke kluster Kubernetes?

3. Gunakan runtime/default hanya sebagai pilihan terakhir

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

Dengan kata lain, sebagai akibat dari perubahan runtime, container mungkin memiliki akses ke serangkaian panggilan sistem yang berbeda, yang mungkin digunakan atau tidak. Kebanyakan runtime menggunakan Implementasi buruh pelabuhan. Jika Anda ingin menggunakan profil ini, pastikan profil tersebut cocok untuk Anda.

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

Menurut pendapat saya, profil runtime/default sangat cocok untuk tujuan pembuatannya: melindungi pengguna dari risiko yang terkait dengan pelaksanaan perintah docker run di mobil mereka. Namun, jika menyangkut aplikasi bisnis yang berjalan di cluster Kubernetes, saya berani berargumen bahwa profil seperti itu terlalu terbuka dan pengembang harus fokus membuat profil untuk aplikasi (atau jenis aplikasi) mereka.

Tips #3: Buat profil seccomp untuk aplikasi tertentu. Jika hal ini tidak memungkinkan, buatlah profil untuk jenis aplikasi, misalnya membuat profil lanjutan yang menyertakan semua API web aplikasi Golang. Hanya gunakan runtime/default sebagai pilihan terakhir.

Di postingan selanjutnya, saya akan membahas cara membuat profil seccomp yang terinspirasi dari SecDevOps, mengotomatiskannya, dan mengujinya dalam pipeline. Dengan kata lain, Anda tidak punya alasan untuk tidak meningkatkan ke profil khusus aplikasi.

4. Tidak dibatasi BUKAN suatu pilihan.

Dari audit keamanan Kubernetes pertama ternyata secara default detik dinonaktifkan. Artinya jika Anda tidak mengaturnya PodSecurityPolicy, yang akan mengaktifkannya di cluster, semua pod yang profil seccompnya tidak ditentukan akan berfungsi seccomp=unconfined.

Beroperasi dalam mode ini berarti seluruh lapisan isolasi yang melindungi cluster hilang. Pendekatan ini tidak direkomendasikan oleh pakar keamanan.

Tips #4: Tidak boleh ada kontainer di cluster yang berjalan seccomp=unconfined, terutama di lingkungan produksi.

5. "Modus audit"

Poin ini tidak hanya terjadi pada Kubernetes, tetapi masih termasuk dalam kategori “hal yang perlu diketahui sebelum Anda memulai”.

Faktanya, membuat profil seccomp selalu menantang dan sangat bergantung pada trial and error. Faktanya adalah bahwa pengguna tidak memiliki kesempatan untuk mengujinya di lingkungan produksi tanpa mengambil risiko “menjatuhkan” aplikasi.

Setelah rilis kernel Linux 4.14, bagian profil dapat dijalankan dalam mode audit, mencatat informasi tentang semua panggilan sistem di syslog, tetapi tanpa memblokirnya. Anda dapat mengaktifkan mode ini menggunakan parameter SCMT_ACT_LOG:

SCMP_ACT_LOG: seccomp tidak akan mempengaruhi thread yang membuat panggilan sistem jika tidak cocok dengan aturan apa pun di filter, namun informasi tentang panggilan sistem akan dicatat.

Berikut strategi umum untuk menggunakan fitur ini:

  1. Izinkan panggilan sistem yang diperlukan.
  2. Blokir panggilan dari sistem yang Anda tahu tidak akan berguna.
  3. Catat informasi tentang semua panggilan lain di log.

Contoh sederhananya terlihat seperti ini:

{
    "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"
        }
    ]
}

(campuran-sedang-seccomp.json)

Namun perlu diingat bahwa Anda perlu memblokir semua panggilan yang Anda tahu tidak akan digunakan dan berpotensi membahayakan cluster. Dasar yang baik untuk menyusun daftar adalah pejabatnya Dokumentasi buruh pelabuhan. Ini menjelaskan secara rinci panggilan sistem mana yang diblokir di profil default dan alasannya.

Namun, ada satu hal yang menarik. Meskipun SCMT_ACT_LOG didukung oleh kernel Linux sejak akhir tahun 2017, kernel ini baru memasuki ekosistem Kubernetes dalam waktu yang relatif baru. Oleh karena itu, untuk menggunakan metode ini Anda memerlukan kernel Linux 4.14 dan versi runC tidak lebih rendah v1.0.0-rc9.

Tips #5: Profil mode audit untuk pengujian dalam produksi dapat dibuat dengan menggabungkan daftar hitam dan putih, dan semua pengecualian dapat dicatat.

6. Gunakan daftar putih

Memasukkan ke daftar putih memerlukan upaya tambahan karena Anda harus mengidentifikasi setiap panggilan yang mungkin diperlukan aplikasi, namun pendekatan ini sangat meningkatkan keamanan:

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

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

package main

import "fmt"

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

... ayo luncurkan gosystract sebagai berikut:

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

... dan kami mendapatkan hasil berikut:

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

Untuk saat ini, ini hanyalah sebuah contoh—detail lebih lanjut tentang alat ini akan menyusul.

Tips #6: Izinkan hanya panggilan yang benar-benar Anda perlukan dan blokir semua panggilan lainnya.

7. Meletakkan fondasi yang tepat (atau bersiap menghadapi perilaku yang tidak terduga)

Kernel akan menerapkan profil terlepas dari apa yang Anda tulis di dalamnya. Meski itu bukan hal yang kamu inginkan. Misalnya, jika Anda memblokir akses ke panggilan seperti exit или exit_group, wadah tidak akan dapat dimatikan dengan benar dan bahkan perintah sederhana seperti echo hi gantung diao untuk jangka waktu tidak terbatas. Hasilnya, Anda akan mendapatkan penggunaan CPU yang tinggi di cluster:

Seccomp di Kubernetes: 7 hal yang perlu Anda ketahui sejak awal

Dalam kasus seperti ini, perusahaan utilitas dapat membantu strace - ini akan menunjukkan apa masalahnya:

Seccomp di Kubernetes: 7 hal yang perlu Anda ketahui sejak awal
sudo strace -c -p 9331

Pastikan profil berisi semua panggilan sistem yang dibutuhkan aplikasi saat runtime.

Tips #7: Perhatikan detailnya dan pastikan semua panggilan sistem yang diperlukan masuk daftar putih.

Ini menyimpulkan bagian pertama dari serangkaian artikel tentang penggunaan seccomp di Kubernetes dalam semangat SecDevOps. Pada bagian berikut kita akan membahas mengapa hal ini penting dan bagaimana mengotomatiskan prosesnya.

PS dari penerjemah

Baca juga di blog kami:

Sumber: www.habr.com

Tambah komentar