Opmerking vertaler.: Wij presenteren de vertaling van een artikel van een senior security engineer van het Britse bedrijf ASOS.com. Hiermee start hij een reeks publicaties die gericht zijn op het verhogen van de beveiliging in Kubernetes door gebruik te maken van seccomp. Als de inleiding de lezers bevalt, zullen we de auteur volgen en zijn toekomstige materialen over dit onderwerp voortzetten.

Dit artikel is het eerste in een serie publicaties over hoe seccomp-profielen te creëren in de geest van SecDevOps, zonder magie en tovenarij. In het eerste deel zal ik de basisprincipes en interne details van de implementatie van seccomp in Kubernetes bespreken.
Het Kubernetes-ecosysteem biedt een voldoende verscheidenheid aan manieren om beveiliging en isolatie van containers te waarborgen. Dit artikel is gewijd aan Secure Computing Mode, ook wel bekend als seccomp. Het idee is om systeemaanroepen te filteren die containerprocessen kunnen uitvoeren.
Waarom is dit belangrijk? Een container is slechts een proces dat draait op een bepaalde machine. En het gebruikt de kernel op gelijke voet met andere applicaties. Als containers elke systeemaanroep konden uitvoeren, zouden kwaadaardige programma's snel gebruikmaken van deze mogelijkheid om de isolatie van de container te omzeilen en andere applicaties te beïnvloeden: informatie onderscheppen, systeeminstellingen wijzigen, enzovoort.
Seccomp-profielen bepalen welke systeemaanroepen zijn toegestaan of verboden. De containeruitvoeringsomgeving activeert ze tijdens de opstart van de container, zodat de kernel toezicht kan houden op hun uitvoering. Het gebruik van dergelijke profielen helpt om de aanvalsvector te beperken en de schade te verminderen in het geval dat een programma binnen de container (dat zijn uw afhankelijkheden, of hun afhankelijkheden) begint te doen wat niet is toegestaan.
Laten we de basisprincipes verkennen
Een basis seccomp-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"
}
]
}()
defaultAction bepaalt de standaardactie voor elke systeemaanroep die niet in het gedeelte is vermeld syscalls. Om de taak te vereenvoudigen, richten we ons op twee belangrijke waarden die zullen worden gebruikt:
-
SCMP_ACT_ERRNO— blokkeert de uitvoering van de systeemaanroep, -
SCMP_ACT_ALLOW— staat deze toe.
In de sectie architectures de doelarchitecturen worden opgesomd. Dit is belangrijk omdat het filter dat op kernniveau wordt toegepast, afhankelijk is van de identificaties van systeemaanroepen en niet van hun namen die in het profiel zijn geschreven. Voor de toepassing zal de container-runtime deze koppelen aan de identificaties. Het idee is dat systeemaanroepen heel verschillende ID's kunnen hebben, afhankelijk van de systeemarchitectuur. Bijvoorbeeld, de systeemaanroep recvfrom (die wordt gebruikt om informatie van een socket te verkrijgen) heeft ID = 64 in x64-systemen en ID = 517 in x86. je kunt een lijst van alle systeemaanroepen voor x86-x64 architecturen vinden.
In de sectie syscalls alle systeemaanroepen worden opgesomd en er wordt aangegeven wat ermee moet gebeuren. Bijvoorbeeld, het is mogelijk om een witte lijst te maken door defaultAction en een werkende opdracht krijgen. SCMP_ACT_ERRNO, en systeemaanroepen in het gedeelte syscalls te koppelen aan SCMP_ACT_ALLOW. Daarmee sta je alleen de aanroepen toe die in het gedeelte zijn vermeld, en verbied je alle andere. Voor een zwarte lijst moet je de waarden omdraaien syscallsen de acties omkeren. defaultAction Nu moet er een paar woorden worden gezegd over de nuances die niet zo voor de hand liggen. Houd er rekening mee dat de onderstaande aanbevelingen ervan uitgaan dat je een reeks bedrijfsapplicaties in Kubernetes uitrolt en dat je belangrijk vindt dat ze met de minste privileges werken.
1. AllowPrivilegeEscalation=false
de container heeft een parameter
In securityContext AllowPrivilegeEscalation . Als deze is ingesteld op, worden containers uitgevoerd met de ingestelde ( false) bitonno_new_priv Een neveneffect van deze parameter, ingesteld op
(de standaardwaarde), is dat de runtime van de container het seccomp-profiel in het begin van het opstartproces toepast. Hierdoor moeten alle systeemaanroepen die nodig zijn voor het opstarten van interne processen in de execution environment (bijvoorbeeld het instellen van gebruikers-/groep-id's, het afwijzen van bepaalde capabilities) in het profiel worden toegestaan. true Een container die een eenvoudige
echo hi , heeft de volgende toestemmingen nodig:, zijn de volgende machtigingen nodig:
{
"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"
}
]
}()
… 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"
}
]
}()
Maar nogmaals, waarom is dit een probleem? Persoonlijk zou ik de volgende systeemaanroepen (tenzij er echt behoefte aan is) niet op de whitelist zetten: capset, set_tid_address, en gekleurde uitvoerbare bestanden. Niet zo'n groot verlies., setgroups en setgid. Echter, de echte uitdaging is dat door processen toe te staan die je absoluut niet controleert, je profielen koppelt aan de implementatie van de container runtime-omgeving. Met andere woorden, op een gegeven moment kun je tegen het probleem aanlopen dat, na het updaten van de container runtime-omgeving (door jou of, waarschijnlijker, door de cloudprovider), de containers plotseling niet meer starten.
Tip #1: Draai containers met AllowPrivilegeEscaltion=false. Dit zal de grootte van de seccomp-profielen verkleinen en ze minder gevoelig maken voor veranderingen in de container runtime-omgeving.
2. Seccomp-profielen op container-niveau instellen
De seccomp-profiel kan op pod-niveau worden ingesteld:
annotations:
seccomp.security.alpha.kubernetes.io/pod: "localhost/profile.json"… of op container-niveau:
annotations:
container.security.alpha.kubernetes.io/<container-name>: "localhost/profile.json"Houd er rekening mee dat de bovenstaande syntaxis zal veranderen wanneer Kubernetes seccomp (dit wordt verwacht in de volgende versie van Kubernetes - 1.18 - opmerking van de vertaler).
Weinig mensen weten dat er altijd een in Kubernetes is geweest, waardoor seccomp-profielen werden toegepast op de De uitvoeringsomgeving compenseert deze tekortkoming gedeeltelijk, maar deze container verdwijnt nergens uit de pod, omdat deze wordt gebruikt om hun infrastructuur in te stellen.
Het probleem is dat deze container altijd wordt gestart met AllowPrivilegeEscalation=true, wat leidt tot problemen die in punt 1 zijn genoemd, en dit kan niet worden gewijzigd.
Door seccomp-profielen op container-niveau toe te passen, vermijd je deze valkuil en kun je een profiel creëren dat is "afgestemd" op een specifieke container. Dit zal moeten blijven gebeuren totdat ontwikkelaars de bug hebben opgelost en een nieuwe versie (misschien 1.18?) beschikbaar komt voor iedereen.
Tip №2: Stel seccomp-profielen in op container-niveau.
In praktische zin is deze regel meestal een algemeen antwoord op de vraag: "Waarom werkt mijn seccomp-profiel met docker run, maar werkt het niet na implementatie in een Kubernetes-cluster?".
3. Gebruik runtime/default alleen in uiterste gevallen
In Kubernetes zijn er twee opties voor ingebouwde profielen: runtime/default en docker/default. Beide worden uitgevoerd door de container-uitvoeringsomgeving en niet door Kubernetes. Daarom kunnen ze verschillen afhankelijk van de gebruikte uitvoeringsomgeving en de versie ervan.
Met andere woorden, als het runtime verandert, kan de container toegang krijgen tot een andere set systeemoproepen die het wel of niet kan gebruiken. De meeste uitvoeringsomgevingen maken gebruik van . Als je dit profiel wilt gebruiken, zorg er dan voor dat het geschikt voor je is.
Het profiel docker/default wordt als verouderd beschouwd sinds Kubernetes 1.11, dus vermijd het gebruik ervan.
Naar mijn mening is het profiel runtime/default uitstekend geschikt voor de doelen waarvoor het is gemaakt: het beschermen van gebruikers tegen risico's met betrekking tot het uitvoeren van opdrachten docker run op hun machines. Maar als we het hebben over zakelijke toepassingen die werken in Kubernetes-clusters, zou ik durven beweren dat zo'n profiel te open is en dat ontwikkelaars zich moeten concentreren op het creëren van profielen voor hun toepassingen (of typen toepassingen).
Tip №3: Creëer seccomp-profielen voor specifieke toepassingen. Als dat niet mogelijk is, werk dan aan profielen voor soorten toepassingen, bijvoorbeeld maak een uitgebreid profiel dat alle web-API's van de toepassingen in Golang omvat. Gebruik runtime/default alleen als uiterste middel.
In toekomstige publicaties zal ik uitleggen hoe je seccomp-profielen kunt maken in de geest van SecDevOps, deze kunt automatiseren en testen in pipelines. Met andere woorden, je hebt geen excuses meer om niet over te stappen op profielen voor specifieke applicaties.
4. Unconfined is GEEN optie
Uit bleek dat standaard . Dit betekent dat als je niet instelt PodSecurityPolicy, die het in de cluster inschakelt, alle pods waarvoor geen seccomp-profiel is gedefinieerd, zullen draaien in de modus seccomp=unconfined.
Werken in zo'n modus betekent dat er een hele laag van isolatie verloren gaat, die de cluster beschermt. Een dergelijke aanpak wordt niet aanbevolen door beveiligingsexperts.
Tip #4: Geen enkele container in de cluster mag draaien in de modus seccomp=unconfined, vooral niet in production-omgevingen.
5. "Auditmodus"
Dit punt is niet uniek voor Kubernetes, maar valt toch onder de categorie "wat je moet weten voordat je begint".
Het is een feit dat het creëren van seccomp-profielen altijd een uitdagende taak is geweest en veelal gebaseerd was op trial-and-error methoden. Het probleem is dat gebruikers simpelweg geen mogelijkheid hebben om ze in production-omgevingen te testen, zonder het risico te lopen de applicatie te "laten vallen".
Na de introductie van kernel Linux 4.14 was het mogelijk om delen van het profiel in auditmodus uit te voeren, waarbij informatie over alle systeemaanroepen naar syslog werd geschreven, maar deze niet werden geblokkeerd. Deze modus kan worden geactiveerd met de parameter SCMT_ACT_LOG:
SCMP_ACT_LOG: seccomp heeft geen invloed op de werking van de thread die de systeemaanroep doet, als deze niet onder een van de regels van de filter valt, maar de informatie over de systeemaanroep zal in het logboek worden opgenomen.
Hier is een typische strategie voor het gebruik van deze mogelijkheid:
- Sta systeemaanroepen toe die nodig zijn.
- Blokkeer systeemaanroepen waarvan bekend is dat ze niet nuttig zijn.
- Informatie over alle andere aanroepen in het logboek opnemen.
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"
}
]
}()
Maar houd er rekening mee dat je alle aanroepen moet blokkeren waarvan bekend is dat ze niet zullen worden gebruikt en die mogelijk schadelijk voor het cluster kunnen zijn. Een goede basis voor het opstellen van de lijst is de officiële . Hierin wordt gedetailleerd uitgelegd welke systeemaanroepen in het standaardprofiel zijn geblokkeerd en waarom.
Er is echter één addertje onder het gras. Hoewel SCMT_ACT_LOG ondersteund wordt door de Linux-kernel sinds eind 2017, is het relatief recent in het Kubernetes-ecosysteem opgenomen. Daarom heb je voor dit type gebruik een Linux-kernel 4.14 en runC versie .
Tip #5: Het auditprofilering voor testen in productie kan worden gemaakt door zwarte en witte lijsten te combineren, en alle uitzonderingen in een logboek te registreren.
6. Gebruik witte lijsten
Het opstellen van witte lijsten vereist extra inspanning, omdat elke aanroep die de applicatie mogelijk nodig heeft moet worden geïdentificeerd, maar deze aanpak verhoogt de veiligheid aanzienlijk:
Het wordt ten zeerste aanbevolen om een op witte lijsten gebaseerde aanpak te gebruiken, aangezien deze eenvoudiger en betrouwbaarder is. De zwarte lijst moet telkens worden bijgewerkt wanneer een potentieel gevaarlijke systeemaanroep (of gevaarlijke vlag/optie als deze op de zwarte lijst staat) wordt toegevoegd. Bovendien kan de weergave van de parameter vaak worden gewijzigd zonder de essentie te veranderen, waardoor de beperkingen van de zwarte lijst kunnen worden omzeild.
Voor Go-applicaties heb ik een speciale tool ontwikkeld die de applicatie begeleidt en alle aanroepen verzamelt die tijdens de uitvoering zijn gedaan. Bijvoorbeeld voor de volgende applicatie:
package main
import "fmt"
func main() {
fmt.Println("test")
} ... laten we de gosystract zo:
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",Dit is momenteel slechts een voorbeeld - details over de toolset volgen later.
Tip #6: Sta alleen de aanroepen toe die u daadwerkelijk nodig heeft en blokkeer alle andere.
7. Leg de juiste basis (of bereid u voor op onvoorspelbaar gedrag)
De kernel zal toezien op het voldoen aan het profiel, ongeacht wat u erin heeft gedefinieerd. Zelfs als dit niet helemaal is wat u wilde. Bijvoorbeeld, als u toegang tot aanroepen zoals exit of exit_group, blokkeert, kan de container niet correct afsluiten en zelfs een eenvoudige opdracht zoals , heeft de volgende toestemmingen nodig: voor onbepaalde tijd. Dit resulteert in een hoge CPU-belasting in het cluster:

In dergelijke gevallen kan de hulpprogramma strace van pas komen - het laat zien waar het probleem kan liggen:

sudo strace -c -p 9331
Zorg ervoor dat de profielen alle systeemaanroepen bevatten die de toepassing tijdens uitvoering nodig heeft.
Tip #7: Let goed op de details en controleer of alle benodigde systeemaanroepen op de whitelist staan.
Dit is het einde van het eerste deel van de 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 we het proces kunnen automatiseren.
P.S. van de vertaler
Lees ook op onze blog:
- «»;
- «»;
- «»;
- «».
Bron: habr.com
