Seccomp sa Kubernetes: 7 bagay na kailangan mong malaman sa simula pa lang

Tandaan. transl.: Ipinakikita namin sa iyong atensyon ang pagsasalin ng isang artikulo ng isang senior application security engineer sa British company na ASOS.com. Sa pamamagitan nito, sinimulan niya ang isang serye ng mga publikasyon na nakatuon sa pagpapabuti ng seguridad sa Kubernetes sa pamamagitan ng paggamit ng seccomp. Kung gusto ng mga mambabasa ang panimula, susundan namin ang may-akda at magpapatuloy sa kanyang mga materyal sa hinaharap sa paksang ito.

Seccomp sa Kubernetes: 7 bagay na kailangan mong malaman sa simula pa lang

Ang artikulong ito ay ang una sa isang serye ng mga post kung paano lumikha ng mga profile ng seccomp sa diwa ng SecDevOps, nang hindi gumagamit ng mahika at pangkukulam. Sa Bahagi 1, sasakupin ko ang mga pangunahing kaalaman at panloob na detalye ng pagpapatupad ng seccom sa Kubernetes.

Nag-aalok ang Kubernetes ecosystem ng malawak na iba't ibang paraan upang ma-secure at ihiwalay ang mga container. Ang artikulo ay tungkol sa Secure Computing Mode, na kilala rin bilang seccom. Ang kakanyahan nito ay i-filter ang mga tawag sa system na magagamit para sa pagpapatupad ng mga lalagyan.

Bakit ito mahalaga? Ang lalagyan ay isang proseso lamang na tumatakbo sa isang partikular na makina. At ginagamit nito ang kernel tulad ng iba pang mga application. Kung ang mga container ay maaaring magsagawa ng anumang mga tawag sa system, sa lalong madaling panahon ay sasamantalahin ito ng malware upang i-bypass ang paghihiwalay ng lalagyan at maapektuhan ang iba pang mga application: humarang ng impormasyon, baguhin ang mga setting ng system, atbp.

Tinutukoy ng mga profile ng seccom kung aling mga system call ang dapat payagan o huwag paganahin. Ina-activate sila ng container runtime kapag nagsimula ito para masubaybayan ng kernel ang kanilang execution. Ang paggamit ng mga ganoong profile ay nagbibigay-daan sa iyong limitahan ang attack vector at bawasan ang pinsala kung ang anumang programa sa loob ng container (iyon ay, ang iyong mga dependency, o ang kanilang mga dependency) ay magsisimulang gumawa ng isang bagay na hindi pinapayagang gawin.

Pagkuha sa mga pangunahing kaalaman

Kasama sa pangunahing profile ng seccom ang tatlong elemento: defaultAction, architectures (O archMap) At 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 tinutukoy ang default na kapalaran ng anumang tawag sa system na hindi tinukoy sa seksyon syscalls. Upang gawing mas madali ang mga bagay, tumuon tayo sa dalawang pangunahing halaga na gagamitin:

  • SCMP_ACT_ERRNO — hinaharangan ang pagsasagawa ng isang system call,
  • SCMP_ACT_ALLOW - nagpapahintulot.

Sa seksyon architectures nakalista ang mga target na arkitektura. Mahalaga ito dahil ang filter mismo, na inilapat sa antas ng kernel, ay nakadepende sa mga system call identifier, at hindi sa kanilang mga pangalan na tinukoy sa profile. Ang runtime ng container ay tutugma sa mga ito sa mga identifier bago gamitin. Ang ideya ay ang mga system call ay maaaring magkaroon ng ganap na magkakaibang mga ID depende sa arkitektura ng system. Halimbawa, system call recvfrom (ginagamit upang makatanggap ng impormasyon mula sa socket) ay may ID = 64 sa x64 system at ID = 517 sa x86. Dito makakahanap ka ng listahan ng lahat ng system call para sa x86-x64 na mga arkitektura.

Sa seksyon syscalls naglilista ng lahat ng mga tawag sa system at tinutukoy kung ano ang gagawin sa mga ito. Halimbawa, maaari kang lumikha ng isang whitelist sa pamamagitan ng pagtatakda defaultAction sa SCMP_ACT_ERRNO, at mga tawag sa seksyon syscalls italaga SCMP_ACT_ALLOW. Kaya, pinapayagan mo lamang ang mga tawag na tinukoy sa seksyon syscalls, at ipagbawal ang lahat ng iba pa. Para sa blacklist dapat mong baguhin ang mga halaga defaultAction at mga aksyon sa kabaligtaran.

Ngayon dapat nating sabihin ang ilang mga salita tungkol sa mga nuances na hindi masyadong halata. Pakitandaan na ipinapalagay ng mga rekomendasyon sa ibaba na nagde-deploy ka ng isang linya ng mga application ng negosyo sa Kubernetes at gusto mong tumakbo ang mga ito nang may pinakamababang halaga ng mga pribilehiyong posible.

1. AllowPrivilegeEscalation=false

В securityContext may parameter ang container AllowPrivilegeEscalation. Kung ito ay naka-install sa false, magsisimula ang mga lalagyan sa (on) bit no_new_priv. Ang kahulugan ng parameter na ito ay kitang-kita mula sa pangalan: pinipigilan nito ang container na maglunsad ng mga bagong proseso na may higit pang mga pribilehiyo kaysa sa mayroon ito.

Isang side effect ng opsyong ito na nakatakda sa true (default) ay ang container runtime ay inilalapat ang seccom profile sa pinakasimula ng proseso ng startup. Kaya, ang lahat ng mga tawag sa system na kinakailangan upang patakbuhin ang mga panloob na proseso ng runtime (hal. pagtatakda ng mga user/group ID, pag-drop ng ilang mga kakayahan) ay dapat na paganahin sa profile.

Sa isang lalagyan na gumagawa ng mga bagay na walang kabuluhan echo hi, kakailanganin ang mga sumusunod na pahintulot:

{
    "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-seccom.json)

...sa halip ng mga ito:

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

Ngunit muli, bakit ito isang problema? Sa personal, iiwasan kong i-whitelist ang mga sumusunod na tawag sa system (maliban kung talagang kailangan ang mga ito): capset, set_tid_address, setgid, setgroups и setuid. Gayunpaman, ang tunay na hamon ay sa pamamagitan ng pagpayag sa mga prosesong wala kang kontrol, tinatali mo ang mga profile sa pagpapatupad ng runtime ng container. Sa madaling salita, isang araw ay maaari mong makita na pagkatapos i-update ang kapaligiran ng runtime ng container (alinman sa iyo o, mas malamang, ng cloud service provider), biglang huminto sa pagtakbo ang mga container.

Tip # 1: Magpatakbo ng mga lalagyan na may AllowPrivilegeEscaltion=false. Babawasan nito ang laki ng mga profile ng seccom at gagawing hindi gaanong sensitibo ang mga ito sa mga pagbabago sa kapaligiran ng runtime ng container.

2. Pagtatakda ng mga profile ng seccom sa antas ng container

Maaaring itakda ang seccom profile sa antas ng pod:

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

...o sa antas ng lalagyan:

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

Pakitandaan na ang syntax sa itaas ay magbabago kapag Kubernetes seccomp magiging GA (Inaasahan ang kaganapang ito sa susunod na paglabas ng Kubernetes - 1.18 - tinatayang transl.).

Ilang tao ang nakakaalam na palaging mayroon ang Kubernetes surotna naging dahilan upang mailapat ang mga profile ng seccom i-pause ang lalagyan. Bahagyang nababayaran ng runtime environment ang pagkukulang na ito, ngunit hindi nawawala ang container na ito sa mga pod, dahil ginagamit ito para i-configure ang kanilang imprastraktura.

Ang problema ay ang lalagyang ito ay palaging nagsisimula sa AllowPrivilegeEscalation=true, na humahantong sa mga problemang ipinahayag sa talata 1, at hindi ito mababago.

Sa pamamagitan ng paggamit ng mga profile ng seccomp sa antas ng container, maiiwasan mo ang pitfall na ito at makakagawa ka ng profile na iniayon sa isang partikular na container. Kailangan itong gawin hanggang sa ayusin ng mga developer ang bug at maging available sa lahat ang bagong bersyon (marahil 1.18?).

Tip # 2: Itakda ang mga profile ng seccom sa antas ng container.

Sa praktikal na kahulugan, ang panuntunang ito ay karaniwang nagsisilbing pangkalahatang sagot sa tanong na: "Bakit gumagana ang aking profile sa seccomp docker runngunit hindi gumagana pagkatapos mag-deploy sa isang Kubernetes cluster?

3. Gamitin lamang ang runtime/default bilang huling paraan

May dalawang opsyon ang Kubernetes para sa mga built-in na profile: runtime/default и docker/default. Parehong ipinatupad ng container runtime, hindi ng Kubernetes. Samakatuwid, maaaring mag-iba ang mga ito depende sa runtime environment na ginamit at sa bersyon nito.

Sa madaling salita, bilang resulta ng pagpapalit ng runtime, maaaring magkaroon ng access ang container sa ibang hanay ng mga system call, na maaaring gamitin o hindi nito. Karamihan sa mga runtime ay gumagamit Pagpapatupad ng Docker. Kung nais mong gamitin ang profile na ito, pakitiyak na ito ay angkop para sa iyo.

Profile docker/default ay hindi na ginagamit mula noong Kubernetes 1.11, kaya iwasang gamitin ito.

Sa aking opinyon, profile runtime/default perpektong angkop para sa layunin kung saan ito nilikha: pagprotekta sa mga gumagamit mula sa mga panganib na nauugnay sa pagpapatupad ng isang utos docker run sa kanilang mga sasakyan. Gayunpaman, pagdating sa mga application ng negosyo na tumatakbo sa mga kumpol ng Kubernetes, maglakas-loob akong magtaltalan na ang ganoong profile ay masyadong bukas at dapat tumuon ang mga developer sa paglikha ng mga profile para sa kanilang mga application (o mga uri ng mga application).

Tip # 3: Lumikha ng mga profile ng seccom para sa mga partikular na application. Kung hindi ito posible, gumawa ng mga profile para sa mga uri ng application, halimbawa, gumawa ng advanced na profile na kinabibilangan ng lahat ng web API ng Golang application. Gumamit lamang ng runtime/default bilang huling paraan.

Sa mga susunod na post, sasakupin ko kung paano gumawa ng mga profile ng seccomp na inspirasyon ng SecDevOps, i-automate ang mga ito, at subukan ang mga ito sa mga pipeline. Sa madaling salita, wala kang dahilan upang hindi mag-upgrade sa mga profile na partikular sa application.

4. Ang hindi nakakulong ay HINDI isang opsyon.

Ng unang pag-audit sa seguridad ng Kubernetes ito pala sa default hindi pinagana ang seccom. Nangangahulugan ito na kung hindi mo itinakda PodSecurityPolicy, na magbibigay-daan dito sa cluster, gagana ang lahat ng pod kung saan hindi tinukoy ang seccom profile seccomp=unconfined.

Ang pagpapatakbo sa mode na ito ay nangangahulugan na ang isang buong layer ng insulation ay nawala na nagpoprotekta sa cluster. Ang pamamaraang ito ay hindi inirerekomenda ng mga eksperto sa seguridad.

Tip # 4: Walang lalagyan sa cluster ang dapat na tumatakbo seccomp=unconfined, lalo na sa mga kapaligiran ng produksyon.

5. "Audit mode"

Ang puntong ito ay hindi natatangi sa Kubernetes, ngunit nahuhulog pa rin sa kategoryang "mga bagay na dapat malaman bago ka magsimula".

Habang nangyayari ito, ang paggawa ng mga profile ng seccomp ay palaging mahirap at lubos na umaasa sa pagsubok at pagkakamali. Ang katotohanan ay ang mga gumagamit ay walang pagkakataon na subukan ang mga ito sa mga kapaligiran ng produksyon nang hindi nanganganib na "i-drop" ang application.

Matapos ang paglabas ng Linux kernel 4.14, naging posible na magpatakbo ng mga bahagi ng isang profile sa audit mode, nagre-record ng impormasyon tungkol sa lahat ng mga tawag sa system sa syslog, ngunit nang hindi hinaharangan ang mga ito. Maaari mong i-activate ang mode na ito gamit ang parameter SCMT_ACT_LOG:

SCMP_ACT_LOG: hindi makakaapekto ang seccomp sa thread na gumagawa ng system call kung hindi ito tumutugma sa anumang panuntunan sa filter, ngunit ang impormasyon tungkol sa system call ay ila-log.

Narito ang isang karaniwang diskarte para sa paggamit ng feature na ito:

  1. Payagan ang mga system call na kailangan.
  2. I-block ang mga tawag mula sa system na alam mong hindi magiging kapaki-pakinabang.
  3. Magtala ng impormasyon tungkol sa lahat ng iba pang mga tawag sa log.

Ang isang pinasimpleng halimbawa ay ganito ang hitsura:

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

(medium-mixed-seccomp.json)

Ngunit tandaan na kailangan mong i-block ang lahat ng tawag na alam mong hindi gagamitin at posibleng makapinsala sa cluster. Ang isang magandang batayan para sa pag-iipon ng isang listahan ay ang opisyal Dokumentasyon ng Docker. Ipinapaliwanag nito nang detalyado kung aling mga system call ang naka-block sa default na profile at kung bakit.

Gayunpaman, mayroong isang catch. Bagaman SCMT_ACT_LOG suportado ng Linux kernel mula noong katapusan ng 2017, ito ay pumasok sa Kubernetes ecosystem kamakailan lamang. Samakatuwid, para magamit ang paraang ito kakailanganin mo ng Linux kernel 4.14 at runC na bersyon na hindi bababa v1.0.0-rc9.

Tip # 5: Ang profile ng audit mode para sa pagsubok sa produksyon ay maaaring gawin sa pamamagitan ng pagsasama-sama ng mga itim at puti na listahan, at lahat ng mga pagbubukod ay maaaring mai-log.

6. Gumamit ng mga whitelist

Ang pag-whitelist ay nangangailangan ng karagdagang pagsisikap dahil kailangan mong tukuyin ang bawat tawag na maaaring kailanganin ng application, ngunit ang diskarteng ito ay lubos na nagpapabuti sa seguridad:

Lubos na inirerekomendang gamitin ang diskarte sa whitelist dahil ito ay mas simple at mas maaasahan. Ang blacklist ay kailangang i-update sa tuwing may idaragdag na potensyal na mapanganib na tawag sa system (o isang mapanganib na bandila/opsyon kung ito ay nasa blacklist). Bilang karagdagan, madalas na posible na baguhin ang representasyon ng isang parameter nang hindi binabago ang kakanyahan nito at sa gayon ay lampasan ang mga paghihigpit ng blacklist.

Para sa mga application ng Go, bumuo ako ng isang espesyal na tool na kasama ng application at kinokolekta ang lahat ng mga tawag na ginawa sa panahon ng pagpapatupad. Halimbawa, para sa sumusunod na aplikasyon:

package main

import "fmt"

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

... ilunsad natin gosystract ganito:

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

... at makuha namin ang sumusunod na resulta:

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

Sa ngayon, ito ay isang halimbawa lamang—mas maraming detalye tungkol sa mga tool ang susunod.

Tip # 6: Payagan lamang ang mga tawag na talagang kailangan mo at i-block ang lahat ng iba pa.

7. Maglagay ng mga tamang pundasyon (o maghanda para sa hindi inaasahang pag-uugali)

Ipapatupad ng kernel ang profile anuman ang isusulat mo dito. Kahit na hindi ito eksakto kung ano ang gusto mo. Halimbawa, kung i-block mo ang access sa mga tawag tulad ng exit o exit_group, ang lalagyan ay hindi makakapag-shut down ng tama at kahit isang simpleng command tulad ng echo hi bitin siyao para sa isang hindi tiyak na panahon. Bilang resulta, makakakuha ka ng mataas na paggamit ng CPU sa cluster:

Seccomp sa Kubernetes: 7 bagay na kailangan mong malaman sa simula pa lang

Sa ganitong mga kaso, ang isang utility ay maaaring dumating upang iligtas strace - ipapakita nito kung ano ang maaaring problema:

Seccomp sa Kubernetes: 7 bagay na kailangan mong malaman sa simula pa lang
sudo strace -c -p 9331

Tiyaking naglalaman ang mga profile ng lahat ng mga tawag sa system na kailangan ng application sa runtime.

Tip # 7: Bigyang-pansin ang detalye at tiyaking naka-whitelist ang lahat ng kinakailangang tawag sa system.

Ito ay nagtatapos sa unang bahagi ng isang serye ng mga artikulo sa paggamit ng seccomp sa Kubernetes sa diwa ng SecDevOps. Sa mga sumusunod na bahagi ay pag-uusapan natin kung bakit ito mahalaga at kung paano i-automate ang proseso.

PS mula sa tagasalin

Basahin din sa aming blog:

Pinagmulan: www.habr.com

Magdagdag ng komento