Seccomp a Kubernetes: 7 coses que cal saber des del principi

Nota. transl.: Presentem a la vostra atenció la traducció d'un article d'un enginyer sènior de seguretat d'aplicacions de l'empresa britànica ASOS.com. Amb ell, inicia una sèrie de publicacions dedicades a millorar la seguretat a Kubernetes mitjançant l'ús de seccomp. Si als lectors els agrada la introducció, seguirem l'autor i continuarem amb els seus futurs materials sobre aquest tema.

Seccomp a Kubernetes: 7 coses que cal saber des del principi

Aquest article és el primer d'una sèrie de publicacions sobre com crear perfils seccomp amb l'esperit de SecDevOps, sense recórrer a la màgia i la bruixeria. A la part XNUMX, tractaré els conceptes bàsics i els detalls interns de la implementació de seccomp a Kubernetes.

L'ecosistema Kubernetes ofereix una gran varietat de maneres de protegir i aïllar els contenidors. L'article tracta sobre el mode informàtic segur, també conegut com seccomp. La seva essència és filtrar les trucades al sistema disponibles per a l'execució per contenidors.

Per què és important? Un contenidor és només un procés que s'executa en una màquina específica. I utilitza el nucli igual que altres aplicacions. Si els contenidors poguessin fer alguna trucada al sistema, molt aviat el programari maliciós ho aprofitaria per evitar l'aïllament dels contenidors i afectar altres aplicacions: interceptar informació, canviar la configuració del sistema, etc.

Els perfils seccomp defineixen quines trucades al sistema s'han de permetre o desactivar. El temps d'execució del contenidor els activa quan s'inicia perquè el nucli pugui supervisar-ne l'execució. L'ús d'aquests perfils us permet limitar el vector d'atac i reduir els danys si algun programa dins del contenidor (és a dir, les vostres dependències o les seves dependències) comença a fer alguna cosa que no està permès fer.

Arribar als fonaments bàsics

El perfil bàsic seccomp inclou tres elements: defaultAction, architectures (O archMap) I 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"
        }
    ]
}

(mitjà-bàsic-seccomp.json)

defaultAction determina el destí predeterminat de qualsevol trucada al sistema no especificada a la secció syscalls. Per facilitar les coses, centrem-nos en els dos valors principals que s'utilitzaran:

  • SCMP_ACT_ERRNO — bloqueja l'execució d'una trucada al sistema,
  • SCMP_ACT_ALLOW - permet.

A la secció architectures s'enumeren les arquitectures de destinació. Això és important perquè el propi filtre, aplicat al nivell del nucli, depèn dels identificadors de trucades del sistema, i no dels seus noms especificats al perfil. El temps d'execució del contenidor els relacionarà amb els identificadors abans d'utilitzar-los. La idea és que les trucades al sistema poden tenir ID completament diferents segons l'arquitectura del sistema. Per exemple, trucada al sistema recvfrom (utilitzat per rebre informació del sòcol) té ID = 64 en sistemes x64 i ID = 517 en x86. Aquí podeu trobar una llista de totes les trucades del sistema per a arquitectures x86-x64.

A la secció syscalls enumera totes les trucades del sistema i especifica què fer-hi. Per exemple, podeu crear una llista blanca mitjançant la configuració defaultAction en SCMP_ACT_ERRNO, i trucades a la secció syscalls assignar SCMP_ACT_ALLOW. Per tant, només permeteu les trucades especificades a la secció syscalls, i prohibir totes les altres. Per a la llista negra, hauríeu de canviar els valors defaultAction i accions al contrari.

Ara hauríem de dir unes paraules sobre matisos que no són tan evidents. Tingueu en compte que les recomanacions següents suposen que esteu desplegant una línia d'aplicacions empresarials a Kubernetes i voleu que s'executin amb la menor quantitat de privilegis possible.

1. AllowPrivilegeEscalation=fals

В securityContext El contenidor té un paràmetre AllowPrivilegeEscalation. Si està instal·lat a false, els contenidors començaran amb (on) bit no_new_priv. El significat d'aquest paràmetre és evident pel nom: impedeix que el contenidor iniciï nous processos amb més privilegis dels que té ell mateix.

Un efecte secundari d'aquesta opció que s'està configurant true (per defecte) és que el temps d'execució del contenidor aplica el perfil seccomp al principi del procés d'inici. Per tant, totes les trucades al sistema necessàries per executar processos interns en temps d'execució (per exemple, establir ID d'usuari/grup, eliminar determinades capacitats) s'han d'habilitar al perfil.

A un contenidor que fa coses trivials echo hi, seran necessaris els permisos següents:

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

...en lloc d'aquests:

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

Però de nou, per què és un problema? Personalment, evitaria incloure a la llista blanca les trucades del sistema següents (tret que hi hagi una necessitat real): capset, set_tid_address, setgid, setgroups и setuid. Tanmateix, el veritable repte és que en permetre processos sobre els quals no teniu cap control, esteu lligant els perfils a la implementació del temps d'execució del contenidor. És a dir, un dia podeu trobar que després d'actualitzar l'entorn d'execució del contenidor (ja sigui per vosaltres o, més probablement, pel proveïdor de serveis al núvol), els contenidors deixen de funcionar de sobte.

Consell núm. 1: Executar contenidors amb AllowPrivilegeEscaltion=false. Això reduirà la mida dels perfils seccomp i els farà menys sensibles als canvis en l'entorn d'execució del contenidor.

2. Configuració de perfils seccomp a nivell de contenidor

El perfil seccomp es pot configurar al nivell de pod:

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

...o a nivell de contenidor:

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

Tingueu en compte que la sintaxi anterior canviarà quan Kubernetes seccomp es convertirà en GA (aquest esdeveniment s'espera a la propera versió de Kubernetes - 1.18 - traducció aprox.).

Poca gent sap que Kubernetes sempre ha tingut errorque va provocar que s'apliquéssin perfils seccomp contenidor de pausa. L'entorn d'execució compensa parcialment aquesta mancança, però aquest contenidor no desapareix dels pods, ja que s'utilitza per configurar la seva infraestructura.

El problema és que aquest contenidor sempre comença amb AllowPrivilegeEscalation=true, donant lloc als problemes expressats a l'apartat 1, i això no es pot canviar.

Si utilitzeu perfils seccomp a nivell de contenidor, eviteu aquest error i podeu crear un perfil adaptat a un contenidor específic. Això s'haurà de fer fins que els desenvolupadors solucionin l'error i la nova versió (potser 1.18?) estigui disponible per a tothom.

Consell núm. 2: Establiu perfils seccomp al nivell del contenidor.

En un sentit pràctic, aquesta regla sol servir com a resposta universal a la pregunta: "Per què el meu perfil seccomp funciona amb docker runperò no funciona després de desplegar-se en un clúster de Kubernetes?

3. Utilitzeu el temps d'execució/predeterminat només com a últim recurs

Kubernetes té dues opcions per als perfils integrats: runtime/default и docker/default. Tots dos s'implementen pel temps d'execució del contenidor, no pel Kubernetes. Per tant, poden diferir segons l'entorn d'execució utilitzat i la seva versió.

En altres paraules, com a resultat del canvi de temps d'execució, el contenidor pot tenir accés a un conjunt diferent de trucades al sistema, que pot utilitzar o no. La majoria dels temps d'execució s'utilitzen Implementació de Docker. Si voleu utilitzar aquest perfil, assegureu-vos que és adequat per a vosaltres.

Perfil docker/default ha estat obsolet des de Kubernetes 1.11, així que eviteu utilitzar-lo.

Al meu entendre, perfil runtime/default perfectament adequat per al propòsit per al qual va ser creat: protegir els usuaris dels riscos associats a l'execució d'una ordre docker run als seus cotxes. Tanmateix, quan es tracta d'aplicacions empresarials que s'executen en clústers de Kubernetes, m'atreviria a argumentar que aquest perfil és massa obert i els desenvolupadors haurien de centrar-se a crear perfils per a les seves aplicacions (o tipus d'aplicacions).

Consell núm. 3: Creeu perfils seccomp per a aplicacions específiques. Si això no és possible, creeu perfils per a tipus d'aplicació, per exemple, creeu un perfil avançat que inclogui totes les API web de l'aplicació Golang. Utilitzeu només el temps d'execució/predeterminat com a últim recurs.

En futures publicacions, explicaré com crear perfils seccomp inspirats en SecDevOps, automatitzar-los i provar-los en pipelines. En altres paraules, no tindreu excusa per no actualitzar-se a perfils específics de l'aplicació.

4. Sense confinar NO és una opció.

D' primera auditoria de seguretat de Kubernetes va resultar que per defecte seccomp desactivat. Això vol dir que si no es configura PodSecurityPolicy, que l'habilitarà al clúster, funcionaran tots els pods per als quals no estigui definit el perfil seccomp seccomp=unconfined.

Funcionar en aquest mode significa que es perd tota una capa d'aïllament que protegeix el clúster. Aquest enfocament no és recomanat pels experts en seguretat.

Consell núm. 4: no s'hauria d'executar cap contenidor del clúster seccomp=unconfined, especialment en entorns de producció.

5. "Mode d'auditoria"

Aquest punt no és exclusiu de Kubernetes, però encara entra a la categoria "Coses que cal saber abans de començar".

Com passa, la creació de perfils seccomp sempre ha estat un repte i depèn en gran mesura de la prova i error. El fet és que els usuaris simplement no tenen l'oportunitat de provar-los en entorns de producció sense arriscar-se a "caure" l'aplicació.

Després del llançament del nucli de Linux 4.14, es va fer possible executar parts d'un perfil en mode d'auditoria, registrant informació sobre totes les trucades al sistema al syslog, però sense bloquejar-les. Podeu activar aquest mode mitjançant el paràmetre SCMT_ACT_LOG:

SCMP_ACT_LOG: seccomp no afectarà el fil que fa la trucada al sistema si no coincideix amb cap regla del filtre, però es registrarà la informació sobre la trucada al sistema.

Aquí teniu una estratègia típica per utilitzar aquesta funció:

  1. Permet les trucades al sistema que siguin necessàries.
  2. Bloqueja les trucades del sistema que saps que no seran útils.
  3. Enregistreu informació sobre totes les altres trucades al registre.

Un exemple simplificat és el següent:

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

(mitjà-mixt-seccomp.json)

Però recordeu que heu de bloquejar totes les trucades que sabeu que no s'utilitzaran i que podrien danyar el clúster. Una bona base per elaborar una llista és l'oficial Documentació de Docker. Explica detalladament quines trucades al sistema estan bloquejades al perfil predeterminat i per què.

No obstant això, hi ha una captura. Encara que SCMT_ACT_LOG suportat pel nucli Linux des de finals de 2017, va entrar a l'ecosistema Kubernetes fa relativament poc temps. Per tant, per utilitzar aquest mètode necessitareu un nucli Linux 4.14 i una versió runC no inferior v1.0.0-rc9.

Consell núm. 5: es pot crear un perfil de mode d'auditoria per provar en producció combinant llistes en blanc i negre i es poden registrar totes les excepcions.

6. Utilitzeu llistes blanques

La incorporació a la llista blanca requereix un esforç addicional perquè heu d'identificar totes les trucades que l'aplicació pugui necessitar, però aquest enfocament millora molt la seguretat:

És molt recomanable utilitzar l'enfocament de llista blanca, ja que és més senzill i fiable. La llista negra s'haurà d'actualitzar sempre que s'afegeixi una trucada al sistema potencialment perillosa (o una marca/opció perillosa si es troba a la llista negra). A més, sovint és possible canviar la representació d'un paràmetre sense canviar la seva essència i, per tant, evitar les restriccions de la llista negra.

Per a les aplicacions Go, vaig desenvolupar una eina especial que acompanya l'aplicació i recull totes les trucades realitzades durant l'execució. Per exemple, per a la següent aplicació:

package main

import "fmt"

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

... anem a llançar gosystract així que:

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

...i obtenim el següent resultat:

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

De moment, això és només un exemple; seguiran més detalls sobre les eines.

Consell núm. 6: permet només aquelles trucades que realment necessites i bloqueja totes les altres.

7. Posa les bases adequades (o prepara't per a un comportament inesperat)

El nucli farà complir el perfil independentment del que hi escriviu. Encara que no sigui exactament el que volies. Per exemple, si bloqueu l'accés a trucades com exit o exit_group, el contenidor no es podrà tancar correctament i fins i tot una simple comanda com echo hi penjar-loo per un període indefinit. Com a resultat, obtindreu un ús elevat de CPU al clúster:

Seccomp a Kubernetes: 7 coses que cal saber des del principi

En aquests casos, una utilitat pot venir al rescat strace - mostrarà quin pot ser el problema:

Seccomp a Kubernetes: 7 coses que cal saber des del principi
sudo strace -c -p 9331

Assegureu-vos que els perfils continguin totes les trucades al sistema que l'aplicació necessita en temps d'execució.

Consell núm. 7: presteu atenció als detalls i assegureu-vos que totes les trucades necessàries del sistema estiguin a la llista blanca.

Això conclou la primera part d'una sèrie d'articles sobre l'ús de seccomp a Kubernetes amb l'esperit de SecDevOps. En les següents parts parlarem de per què això és important i de com automatitzar el procés.

PS del traductor

Llegeix també al nostre blog:

Font: www.habr.com

Afegeix comentari