Seccomp en Kubernetes: 7 aferoj, kiujn vi devas scii de la komenco

Notu. transl.: Ni prezentas al via atento la tradukon de artikolo de altranga inĝeniero pri sekureca aplikaĵo ĉe la brita kompanio ASOS.com. Per ĝi, li komencas serion de publikaĵoj dediĉitaj al plibonigo de sekureco en Kubernetes per la uzo de seccomp. Se legantoj ŝatas la enkondukon, ni sekvos la aŭtoron kaj daŭrigos kun liaj estontaj materialoj pri ĉi tiu temo.

Seccomp en Kubernetes: 7 aferoj, kiujn vi devas scii de la komenco

Ĉi tiu artikolo estas la unua en serio de afiŝoj pri kiel krei sekcomp-profilojn en la spirito de SecDevOps, sen recurri al magio kaj sorĉado. En Parto XNUMX, mi kovros la bazojn kaj internajn detalojn pri efektivigo de seccomp en Kubernetes.

La Kubernetes-ekosistemo ofertas ampleksan varion de manieroj sekurigi kaj izoli ujojn. La artikolo temas pri Sekura Komputila Reĝimo, ankaŭ konata kiel sekkomp. Ĝia esenco estas filtri la sistemvokojn disponeblajn por ekzekuto per ujoj.

Kial ĝi estas grava? Ujo estas nur procezo funkcianta sur specifa maŝino. Kaj ĝi uzas la kernon same kiel aliaj aplikoj. Se ujoj povus fari iujn ajn sistemajn vokojn, tre baldaŭ malware utiligus tion por preterpasi uj-izolon kaj influi aliajn aplikojn: kapti informojn, ŝanĝi sistemajn agordojn ktp.

seccomp-profiloj difinas kiuj sistemaj vokoj estu permesitaj aŭ malŝaltitaj. La ujo rultempo aktivigas ilin kiam ĝi komenciĝas tiel ke la kerno povas kontroli ilian ekzekuton. Uzado de tiaj profiloj permesas vin limigi la atakvektoron kaj redukti damaĝon se iu programo ene de la ujo (tio estas, viaj dependecoj aŭ iliaj dependecoj) komencas fari ion, kion ĝi ne rajtas fari.

Kompreni la Bazojn

La baza seccomp-profilo inkluzivas tri elementojn: defaultAction, architectures (aŭ archMap) kaj 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 determinas la defaŭltan sorton de iu sistemvoko ne specifita en la sekcio syscalls. Por faciligi aferojn, ni koncentriĝu pri la du ĉefaj valoroj, kiuj estos uzataj:

  • SCMP_ACT_ERRNO — blokas la plenumon de sistemvoko,
  • SCMP_ACT_ALLOW - permesas.

sekcio architectures celaj arkitekturoj estas listigitaj. Ĉi tio estas grava ĉar la filtrilo mem, aplikata ĉe la kernnivelo, dependas de sistemvokaj identigiloj, kaj ne de iliaj nomoj specifitaj en la profilo. La ujo rultempo kongruos ilin al identigiloj antaŭ uzo. La punkto estas, ke sistemaj vokoj povas havi tute malsamajn identigilojn depende de la sistema arkitekturo. Ekzemple, sistemvoko recvfrom (uzata por ricevi informojn de la ingo) havas ID = 64 sur x64-sistemoj kaj ID = 517 sur x86. estas vi povas trovi liston de ĉiuj sistemvokoj por x86-x64-arkitekturoj.

En la sekcio syscalls listigas ĉiujn sistemvokojn kaj specifas kion fari kun ili. Ekzemple, vi povas krei blankan liston per agordo defaultAction sur SCMP_ACT_ERRNO, kaj vokoj en la sekcio syscalls asigni SCMP_ACT_ALLOW. Tiel, vi nur permesas alvokojn specifitajn en la sekcio syscalls, kaj malpermesi ĉiujn aliajn. Por la nigra listo vi devus ŝanĝi la valorojn defaultAction kaj agoj al la malo.

Nun ni diru kelkajn vortojn pri nuancoj, kiuj ne estas tiel evidentaj. Bonvolu noti, ke la malsupraj rekomendoj supozas, ke vi disvastigas linion de komercaj aplikoj sur Kubernetes kaj vi volas, ke ili funkciigu kun la plej malgranda kvanto da eblaj privilegioj.

1. AllowPrivilegeEscalation=malvera

В securityContext ujo havas parametron AllowPrivilegeEscalation. Se ĝi estas instalita en false, ujoj komencos per (on) bit no_new_priv. La signifo de ĉi tiu parametro estas evidenta el la nomo: ĝi malhelpas la ujo lanĉi novajn procezojn kun pli da privilegioj ol ĝi mem havas.

Flanka efiko de ĉi tiu opcio agordita al true (defaŭlte) estas ke la ujo rultempo aplikas la seccomp profilon ĉe la komenco mem de la ekprocezo. Tiel, ĉiuj sistemvokoj necesaj por ruli internajn rultempajn procezojn (ekz. fiksi uzant-/grupajn identigilojn, forlasante certajn kapablojn) devas esti ebligitaj en la profilo.

Al ujo, kiu faras bagatelojn echo hi, la sekvaj permesoj estos postulataj:

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

...anstataŭ ĉi tiuj:

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

Sed denove, kial ĉi tio estas problemo? Persone, mi evitus enlistigi la sekvajn sistemvokojn (krom se estas vera bezono por ili): capset, set_tid_address, setgid, setgroups и setuid. Tamen, la vera defio estas, ke permesante procezojn, kiujn vi tute ne havas kontrolon, vi ligas profilojn al la efektivigo de rultempa ujo. Alivorte, iutage vi eble trovos, ke post ĝisdatigo de la uja rultempa medio (ĉu de vi aŭ, pli verŝajne, de la provizanto de nuba servo), la ujoj subite ĉesas funkcii.

Konsilo # 1: Kuru ujojn kun AllowPrivilegeEscaltion=false. Ĉi tio reduktos la grandecon de seccomp-profiloj kaj igos ilin malpli sentemaj al ŝanĝoj en la ujo rultempa medio.

2. Agordi sekcomp-profilojn ĉe la ujo-nivelo

La sekcomp-profilo povas esti agordita ĉe la podnivelo:

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

...aŭ ĉe la ujo-nivelo:

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

Bonvolu noti, ke la supra sintakso ŝanĝiĝos kiam Kubernetes seccomp fariĝos GA (ĉi tiu evento estas atendata en la venonta eldono de Kubernetes - 1.18 - ĉ. traduk.).

Malmultaj homoj scias, ke Kubernetes ĉiam havis cimokiu igis seccomp-profilojn esti aplikita al paŭzi ujo. La rultempa medio parte kompensas ĉi tiun mankon, sed ĉi tiu ujo ne malaperas el la podoj, ĉar ĝi estas uzata por agordi ilian infrastrukturon.

La problemo estas, ke ĉi tiu ujo ĉiam komenciĝas per AllowPrivilegeEscalation=true, kondukante al la problemoj esprimitaj en paragrafo 1, kaj tio ne estas ŝanĝita.

Uzante sekcomp-profilojn ĉe la ujo-nivelo, vi evitas ĉi tiun malutilon kaj povas krei profilon, kiu estas adaptita al specifa ujo. Ĉi tio devos esti farita ĝis la programistoj riparos la cimon kaj la nova versio (eble 1.18?) fariĝos disponebla por ĉiuj.

Konsilo # 2: Agordu seccomp-profilojn ĉe la ujo-nivelo.

En praktika signifo, ĉi tiu regulo kutime funkcias kiel universala respondo al la demando: "Kial mia sekcomp-profilo funkcias kun docker runsed ne funkcias post deplojiĝo al Kubernetes-grupo?

3. Uzu rultempon/defaŭltan nur kiel lasta rimedo

Kubernetes havas du eblojn por enkonstruitaj profiloj: runtime/default и docker/default. Ambaŭ estas efektivigitaj de la ujo rultempo, ne Kubernetes. Tial, ili povas malsami depende de la rultempa medio uzata kaj ĝia versio.

Alivorte, kiel rezulto de ŝanĝado de rultempo, la ujo povas havi aliron al malsama aro de sistemaj vokoj, kiujn ĝi povas aŭ ne uzi. Plej multaj rultempoj uzas Docker efektivigo. Se vi volas uzi ĉi tiun profilon, bonvolu certigi, ke ĝi taŭgas por vi.

profilo docker/default estas malrekomendita ekde Kubernetes 1.11, do evitu uzi ĝin.

Laŭ mi, profilo runtime/default perfekte taŭga por la celo por kiu ĝi estis kreita: protekti uzantojn kontraŭ la riskoj asociitaj kun ekzekuto de komando docker run sur iliaj aŭtoj. Tamen, se temas pri komercaj aplikaĵoj kurantaj sur Kubernetes-aretoj, mi kuraĝus argumenti, ke tia profilo estas tro malferma kaj programistoj devus koncentriĝi pri kreado de profiloj por siaj aplikoj (aŭ specoj de aplikoj).

Konsilo # 3: Kreu seccomp-profilojn por specifaj aplikoj. Se ĉi tio ne eblas, kreu profilojn por aplikaĵtipoj, ekzemple, kreu altnivelan profilon, kiu inkluzivas ĉiujn TTT-APIojn de la aplikaĵo Golang. Nur uzu rultempon/defaŭltan kiel lasta rimedo.

En estontaj afiŝoj, mi kovros kiel krei SecDevOps-inspirajn sekcomp-profilojn, aŭtomatigi ilin kaj testi ilin en duktoj. Alivorte, vi ne havos pretekston ne ĝisdatigi al aplikaĵ-specifaj profiloj.

4. Senlima NE estas elekto.

De unua sekureca revizio de Kubernetes montriĝis ke defaŭlte seccomp malŝaltita. Ĉi tio signifas, ke se vi ne fiksas PodSecurityPolicy, kiu ebligos ĝin en la areto, ĉiuj podoj por kiuj la sekcomp-profilo ne estas difinita funkcios seccomp=unconfined.

Funkcianta en ĉi tiu reĝimo signifas ke tuta tavolo de izolajzo estas perdita kiu protektas la areton. Ĉi tiu aliro ne estas rekomendita de fakuloj pri sekureco.

Konsilo # 4: Neniu ujo en la areto devus funkcii seccomp=unconfined, precipe en produktadmedioj.

5. "Revizia reĝimo"

Ĉi tiu punkto ne estas unika por Kubernetes, sed ankoraŭ falas en la kategorion "aferojn por scii antaŭ ol komenci".

Kiel okazas, krei sekcomp-profilojn ĉiam estis malfacila kaj dependas multe de provo kaj eraro. La fakto estas, ke uzantoj simple ne havas la ŝancon testi ilin en produktadmedioj sen riski "faligi" la aplikaĵon.

Post la liberigo de la Linukso-kerno 4.14, eblis ruli partojn de profilo en revizia reĝimo, registrante informojn pri ĉiuj sistemvokoj en syslog, sed sen bloki ilin. Vi povas aktivigi ĉi tiun reĝimon uzante la parametron SCMT_ACT_LOG:

SCMP_ACT_LOG: seccomp ne influos la fadenon farantan la sistemvokon se ĝi ne kongruas kun iu ajn regulo en la filtrilo, sed informoj pri la sistemvoko estos registritaj.

Jen tipa strategio por uzi ĉi tiun funkcion:

  1. Permesu sistemajn vokojn, kiuj estas bezonataj.
  2. Bloki vokojn de la sistemo, kiun vi scias, ke vi ne estos utila.
  3. Registri informojn pri ĉiuj aliaj vokoj en la protokolo.

Simpligita ekzemplo aspektas jene:

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

Sed memoru, ke vi devas bloki ĉiujn vokojn, kiujn vi scias, ke vi ne estos uzataj kaj kiuj eble povus damaĝi la areton. Bona bazo por kompili liston estas la oficiala Docker-dokumentado. Ĝi klarigas detale kiuj sistemaj vokoj estas blokitaj en la defaŭlta profilo kaj kial.

Tamen, estas unu kapto. Kvankam SCMT_ACT_LOG subtenata de la Linukso-kerno ekde la fino de 2017, ĝi eniris la ekosistemon Kubernetes nur relative lastatempe. Tial, por uzi ĉi tiun metodon, vi bezonos Linuksan kernon 4.14 kaj runC version ne pli malalta v1.0.0-rc9.

Konsilo # 5: revizia reĝima profilo por testado en produktado povas esti kreita kombinante nigrajn kaj blankajn listojn, kaj ĉiuj esceptoj povas esti registritaj.

6. Uzu blankajn listojn

Blanka listo postulas plian penon ĉar vi devas identigi ĉiun vokon, kiun la aplikaĵo eble bezonos, sed ĉi tiu aliro multe plibonigas sekurecon:

Estas tre rekomendite uzi la aliron de blanka listo ĉar ĝi estas pli simpla kaj pli fidinda. La nigra listo devos esti ĝisdatigita kiam ajn eble danĝera sistemvoko (aŭ danĝera flago/opcio se ĝi estas en la nigra listo) estas aldonita. Krome, estas ofte eble ŝanĝi la reprezentadon de parametro sen ŝanĝi ĝian esencon kaj tiel preteriri la limigojn de la nigra listo.

Por Go-aplikoj, mi evoluigis specialan ilon kiu akompanas la aplikaĵon kaj kolektas ĉiujn vokojn faritajn dum ekzekuto. Ekzemple, por la sekva aplikaĵo:

package main

import "fmt"

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

... ni lanĉu gosystract kiel tia:

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

... kaj ni ricevas la sekvan rezulton:

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

Nuntempe, ĉi tio estas nur ekzemplo—pliaj detaloj pri la iloj sekvos.

Konsilo # 6: Permesu nur tiujn vokojn, kiujn vi vere bezonas, kaj bloku ĉiujn aliajn.

7. Metu la ĝustajn fundamentojn (aŭ preparu por neatendita konduto)

La kerno devigos la profilon sendepende de tio, kion vi skribas en ĝi. Eĉ se ĝi ne estas ĝuste tio, kion vi volis. Ekzemple, se vi blokas aliron al vokoj kiel exitexit_group, la ujo ne povos malŝalti ĝuste kaj eĉ simpla komando kiel echo hi pendigu lino por nedifinita periodo. Kiel rezulto, vi ricevos altan CPU-uzon en la areto:

Seccomp en Kubernetes: 7 aferoj, kiujn vi devas scii de la komenco

En tiaj kazoj, utileco povas veni al la rekupero strace - ĝi montros kia povas esti la problemo:

Seccomp en Kubernetes: 7 aferoj, kiujn vi devas scii de la komenco
sudo strace -c -p 9331

Certigu, ke la profiloj enhavas ĉiujn sistemajn vokojn, kiujn la aplikaĵo bezonas dum rultempo.

Konsilo # 7: Atentu detalojn kaj certigu, ke ĉiuj necesaj sistemvokoj estas blanklistigitaj.

Ĉi tio finas la unuan parton de serio de artikoloj pri uzado de seccomp en Kubernetes en la spirito de SecDevOps. En la sekvaj partoj ni parolos pri kial ĉi tio gravas kaj kiel aŭtomatigi la procezon.

PS de tradukisto

Legu ankaŭ en nia blogo:

fonto: www.habr.com

Aldoni komenton