Seccomp in Kubernetes: 7 dingen die u vanaf het allereerste begin moet weten

Opmerking. vert.: We presenteren onder uw aandacht de vertaling van een artikel van een senior applicatiebeveiligingsingenieur bij het Britse bedrijf ASOS.com. Hiermee begint hij een reeks publicaties gewijd aan het verbeteren van de beveiliging in Kubernetes door het gebruik van seccomp. Als de lezers de inleiding leuk vinden, zullen we de auteur volgen en doorgaan met zijn toekomstige materiaal over dit onderwerp.

Seccomp in Kubernetes: 7 dingen die u vanaf het allereerste begin moet weten

Dit artikel is het eerste in een reeks berichten over hoe je seccomp-profielen kunt maken in de geest van SecDevOps, zonder toevlucht te nemen tot magie en hekserij. In deel XNUMX behandel ik de basisprincipes en interne details van het implementeren van seccomp in Kubernetes.

Het Kubernetes-ecosysteem biedt een grote verscheidenheid aan manieren om containers te beveiligen en te isoleren. Het artikel gaat over Secure Computing Mode, ook wel bekend als sec. De essentie ervan is het filteren van de systeemaanroepen die beschikbaar zijn voor uitvoering door containers.

Waarom is het belangrijk? Een container is slechts een proces dat op een specifieke machine draait. En het gebruikt de kernel net als andere applicaties. Als containers systeemaanroepen zouden kunnen uitvoeren, zou malware hier zeer binnenkort misbruik van maken om de containerisolatie te omzeilen en andere applicaties te beïnvloeden: informatie onderscheppen, systeeminstellingen wijzigen, enz.

seccomp-profielen bepalen welke systeemaanroepen moeten worden toegestaan ​​of uitgeschakeld. De containerruntime activeert ze wanneer deze start, zodat de kernel hun uitvoering kan controleren. Door dergelijke profielen te gebruiken, kunt u de aanvalsvector beperken en de schade verminderen als een programma in de container (dat wil zeggen uw afhankelijkheden of hun afhankelijkheden) iets begint te doen wat het niet mag doen.

Tot de basis komen

Het basisseccomp-profiel omvat drie elementen: defaultAction, architectures (Of archMap) En 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 bepaalt het standaard lot van elke systeemaanroep die niet in de sectie is gespecificeerd syscalls. Laten we, om het gemakkelijker te maken, ons concentreren op de twee belangrijkste waarden die zullen worden gebruikt:

  • SCMP_ACT_ERRNO — blokkeert de uitvoering van een systeemoproep,
  • SCMP_ACT_ALLOW - staat toe.

In paragraaf architectures doelarchitecturen worden vermeld. Dit is belangrijk omdat het filter zelf, toegepast op kernelniveau, afhankelijk is van systeemoproep-ID's, en niet van hun namen die in het profiel zijn gespecificeerd. De containerruntime koppelt ze vóór gebruik aan ID's. Het idee is dat systeemoproepen volledig verschillende ID's kunnen hebben, afhankelijk van de systeemarchitectuur. Systeemoproep bijvoorbeeld recvfrom (gebruikt om informatie van de socket te ontvangen) heeft ID = 64 op x64-systemen en ID = 517 op x86. Hier u kunt een lijst vinden met alle systeemoproepen voor x86-x64-architecturen.

In de sectie syscalls geeft een overzicht van alle systeemoproepen en specificeert wat ermee moet gebeuren. U kunt bijvoorbeeld een witte lijst maken door in te stellen defaultAction op SCMP_ACT_ERRNO, en roept de sectie op syscalls toewijzen SCMP_ACT_ALLOW. U staat dus alleen oproepen toe die in de sectie zijn gespecificeerd syscallsen verbied alle andere. Voor de zwarte lijst moet u de waarden wijzigen defaultAction en acties die het tegenovergestelde doen.

Nu moeten we een paar woorden zeggen over nuances die niet zo voor de hand liggend zijn. Houd er rekening mee dat bij de onderstaande aanbevelingen wordt aangenomen dat u bedrijfsapplicaties op Kubernetes implementeert en wilt dat deze met zo min mogelijk rechten worden uitgevoerd.

1. AllowPrivilegeEscalation=false

В securityContext container heeft een parameter AllowPrivilegeEscalation. Als het is geïnstalleerd in false, beginnen containers met (on) beetje no_new_priv. De betekenis van deze parameter blijkt duidelijk uit de naam: hij voorkomt dat de container nieuwe processen start met meer rechten dan hij zelf heeft.

Een neveneffect van het instellen van deze optie true (standaard) is dat de containerruntime het seccomp-profiel helemaal aan het begin van het opstartproces toepast. Daarom moeten alle systeemaanroepen die nodig zijn om interne runtime-processen uit te voeren (bijvoorbeeld het instellen van gebruikers-/groeps-ID's, het schrappen van bepaalde mogelijkheden) in het profiel worden ingeschakeld.

Naar een container die triviale dingen doet echo hi, zijn de volgende machtigingen vereist:

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

...in plaats van deze:

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

Maar nogmaals: waarom is dit een probleem? Persoonlijk zou ik vermijden om de volgende systeemaanroepen op de witte lijst te zetten (tenzij er echt behoefte aan is): capset, set_tid_address, setgid, setgroups и setuid. De echte uitdaging is echter dat door processen toe te staan ​​waar u absoluut geen controle over heeft, u profielen koppelt aan de containerruntime-implementatie. Met andere woorden: op een dag zul je merken dat na het updaten van de containerruntimeomgeving (door jou of, waarschijnlijker, door de cloudserviceprovider), de containers plotseling niet meer werken.

Tip # 1: Voer containers uit met AllowPrivilegeEscaltion=false. Hierdoor wordt de grootte van seccomp-profielen kleiner en worden ze minder gevoelig voor veranderingen in de containerruntime-omgeving.

2. Seccomp-profielen instellen op containerniveau

Het seccomp-profiel kan op podniveau worden ingesteld:

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

...of op containerniveau:

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

Houd er rekening mee dat de bovenstaande syntaxis verandert wanneer Kubernetes seccomp wordt GA (deze gebeurtenis wordt verwacht in de volgende release van Kubernetes - 1.18 - ca. vert.).

Weinig mensen weten dat Kubernetes dat altijd al heeft gehad beestjewaardoor seccomp-profielen werden toegepast pauzeer container. De runtime-omgeving compenseert deze tekortkoming gedeeltelijk, maar deze container verdwijnt niet uit de pods, omdat deze wordt gebruikt om hun infrastructuur te configureren.

Het probleem is dat deze container altijd begint met AllowPrivilegeEscalation=true, wat heeft geleid tot de problemen die in paragraaf 1 worden genoemd, en dit kan niet worden veranderd.

Door seccomp-profielen op containerniveau te gebruiken, vermijdt u deze valkuil en kunt u een profiel creëren dat is afgestemd op een specifieke container. Dit zal moeten gebeuren totdat de ontwikkelaars de bug hebben opgelost en de nieuwe versie (misschien 1.18?) voor iedereen beschikbaar komt.

Tip # 2: Stel seccomp-profielen in op containerniveau.

In praktische zin dient deze regel meestal als universeel antwoord op de vraag: “Waarom werkt mijn seccomp-profiel mee docker runmaar werkt het niet na implementatie in een Kubernetes-cluster?

3. Gebruik runtime/default alleen als laatste redmiddel

Kubernetes heeft twee opties voor ingebouwde profielen: runtime/default и docker/default. Beide worden geïmplementeerd door de containerruntime, niet door Kubernetes. Daarom kunnen ze verschillen, afhankelijk van de gebruikte runtime-omgeving en de versie ervan.

Met andere woorden: als gevolg van de veranderende runtime kan de container toegang hebben tot een andere set systeemaanroepen, die hij wel of niet kan gebruiken. De meeste runtimes gebruiken Docker-implementatie. Als u dit profiel wilt gebruiken, zorg er dan voor dat het geschikt is voor u.

Profiel docker/default is verouderd sinds Kubernetes 1.11, dus vermijd het gebruik ervan.

Volgens mij profiel runtime/default perfect geschikt voor het doel waarvoor het is gemaakt: gebruikers beschermen tegen de risico's die gepaard gaan met het uitvoeren van een commando docker run op hun auto's. Als het echter gaat om bedrijfsapplicaties die op Kubernetes-clusters draaien, durf ik te betogen dat een dergelijk profiel te open is en dat ontwikkelaars zich moeten concentreren op het maken van profielen voor hun applicaties (of soorten applicaties).

Tip # 3: maak seccomp-profielen voor specifieke toepassingen. Als dit niet mogelijk is, maak dan profielen aan voor applicatietypen. Maak bijvoorbeeld een geavanceerd profiel dat alle web-API's van de Golang-applicatie bevat. Gebruik runtime/default alleen als laatste redmiddel.

In toekomstige berichten zal ik bespreken hoe je op SecDevOps geïnspireerde seccomp-profielen kunt maken, deze kunt automatiseren en ze in pijplijnen kunt testen. Met andere woorden: u heeft geen excuus om niet te upgraden naar applicatiespecifieke profielen.

4. Onbeperkt is GEEN optie.

Van eerste Kubernetes-beveiligingsaudit het bleek standaard seccomp uitgeschakeld. Dit betekent dat als u niet instelt PodSecurityPolicy, waardoor dit in het cluster wordt ingeschakeld, zullen alle pods waarvoor het seccomp-profiel niet is gedefinieerd, werken seccomp=unconfined.

Als u in deze modus werkt, gaat een hele isolatielaag verloren die het cluster beschermt. Deze aanpak wordt niet aanbevolen door beveiligingsexperts.

Tip # 4: Er mag geen container in het cluster worden uitgevoerd seccomp=unconfined, vooral in productieomgevingen.

5. "Auditmodus"

Dit punt is niet uniek voor Kubernetes, maar valt nog steeds in de categorie ‘dingen die u moet weten voordat u begint’.

Toevallig is het maken van seccomp-profielen altijd een uitdaging geweest en is het sterk afhankelijk van vallen en opstaan. Feit is dat gebruikers eenvoudigweg niet de mogelijkheid hebben om ze in productieomgevingen te testen zonder het risico te lopen de applicatie te ‘laten vallen’.

Na de release van de Linux-kernel 4.14 werd het mogelijk om delen van een profiel in auditmodus uit te voeren, waarbij informatie over alle systeemaanroepen in syslog werd vastgelegd, maar zonder deze te blokkeren. U kunt deze modus activeren met behulp van de parameter SCMT_ACT_LOG:

SCMP_ACT_LOG: seccomp heeft geen invloed op de thread die de systeemaanroep doet als deze niet overeenkomt met een regel in het filter, maar informatie over de systeemaanroep wordt geregistreerd.

Hier is een typische strategie voor het gebruik van deze functie:

  1. Sta systeemoproepen toe die nodig zijn.
  2. Blokkeer oproepen van het systeem waarvan u weet dat deze niet nuttig zijn.
  3. Registreer informatie over alle andere oproepen in het logboek.

Een vereenvoudigd voorbeeld ziet er als volgt uit:

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

Maar onthoud dat u alle oproepen moet blokkeren waarvan u weet dat ze niet zullen worden gebruikt en die mogelijk schadelijk kunnen zijn voor het cluster. Een goede basis voor het samenstellen van een lijst is de ambtenaar Docker-documentatie. Er wordt gedetailleerd uitgelegd welke systeemoproepen in het standaardprofiel worden geblokkeerd en waarom.

Er is echter één addertje onder het gras. Hoewel SCMT_ACT_LOG ondersteund door de Linux-kernel sinds eind 2017, kwam het pas relatief recent in het Kubernetes-ecosysteem terecht. Om deze methode te gebruiken heb je daarom een ​​Linux-kernel 4.14 en een runC-versie niet lager nodig v1.0.0-rc9.

Tip # 5: Er kan een auditmodusprofiel voor testen in productie worden gemaakt door zwarte en witte lijsten te combineren, en alle uitzonderingen kunnen worden vastgelegd.

6. Gebruik witte lijsten

Whitelisting vereist extra inspanning omdat u elke oproep moet identificeren die de applicatie mogelijk nodig heeft, maar deze aanpak verbetert de beveiliging aanzienlijk:

Het wordt ten zeerste aanbevolen om de whitelist-aanpak te gebruiken, omdat deze eenvoudiger en betrouwbaarder is. De zwarte lijst moet worden bijgewerkt wanneer een potentieel gevaarlijke systeemaanroep (of een gevaarlijke vlag/optie als deze op de zwarte lijst staat) wordt toegevoegd. Bovendien is het vaak mogelijk om de weergave van een parameter te wijzigen zonder de essentie ervan te veranderen en daarmee de beperkingen van de zwarte lijst te omzeilen.

Voor Go-applicaties heb ik een speciale tool ontwikkeld die bij de applicatie hoort en alle oproepen verzamelt die tijdens de uitvoering worden gedaan. Bijvoorbeeld voor de volgende toepassing:

package main

import "fmt"

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

... laten we lanceren gosystract als volgt:

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

... en we krijgen het volgende resultaat:

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

Voorlopig is dit slechts een voorbeeld; meer details over de tools volgen.

Tip # 6: Sta alleen de oproepen toe die u echt nodig heeft en blokkeer alle andere.

7. Leg de juiste basis (of bereid je voor op onverwacht gedrag)

De kernel zal het profiel afdwingen, ongeacht wat u erin schrijft. Zelfs als het niet precies is wat je wilde. Als u bijvoorbeeld de toegang tot oproepen blokkeert, zoals exit of exit_group, kan de container niet correct worden afgesloten, zelfs niet met een eenvoudig commando als echo hi hang hem opo voor onbepaalde tijd. Als gevolg hiervan krijgt u een hoog CPU-gebruik in het cluster:

Seccomp in Kubernetes: 7 dingen die u vanaf het allereerste begin moet weten

In dergelijke gevallen kan een hulpprogramma te hulp komen strace - het zal laten zien wat het probleem kan zijn:

Seccomp in Kubernetes: 7 dingen die u vanaf het allereerste begin moet weten
sudo strace -c -p 9331

Zorg ervoor dat de profielen alle systeemaanroepen bevatten die de applicatie tijdens runtime nodig heeft.

Tip # 7: Besteed aandacht aan details en zorg ervoor dat alle noodzakelijke systeemaanroepen op de witte lijst staan.

Hiermee is het eerste deel afgesloten van een reeks artikelen over het gebruik van seccomp in Kubernetes in de geest van SecDevOps. In de volgende delen zullen we bespreken waarom dit belangrijk is en hoe u het proces kunt automatiseren.

PS van vertaler

Lees ook op onze blog:

Bron: www.habr.com

Voeg een reactie