user_namespaces(7) Miscellaneous Information Manual user_namespaces(7)

user_namespaces — översikt över Linux användarnamnrymder

För en översikt över namnrymder, se namespaces(7).

Användarnamnrymder isolerar säkerhetsrelaterade identifierare och attribut, mer exakt, användar-ID:n och grupp-ID:n (se credentials(7)), rotkatalogen, nycklar (se keyring(7)) och förmågor (se capabilities(7)). En process användar- och grupp-ID:n kan vara olika inuti och utanför en användarnamnrymd. I synnerhet kan en process ha ett normalt oprivilegierat användar-ID utanför en användarnamnrymd medan den på samma gång har användar-ID:t 0 inuti namnrymden; med andra ord, processen har fullständiga rättigheter vid åtgärder inuti användarnamnrymden, men är oprivilegierad vid åtgärder utanför namnrymden.

Användarnamnrymder kan nästas; det vill säga, varje användarnamnrymd — utom den initiala (”rot-”)namnrymden — har en föräldraanvändarnamnrymd, och kan ha noll eller flera barnanvändarnamnrymder. Föräldraanvändarnamnrymden är användarnamnrymden för processen som skapar användarnamnrymden via ett anrop av unshare(2) eller clone(2) med flaggan CLONE_NEWUSER.

Kärnan lägger (från Linux 3.11) en gräns på 32 nästade nivåer med användarnamnrymder. Anrop av unshare(2) eller clone(2) som annars skulle orsaka att denna gräns överskrids misslyckas med felet EUSERS.

Varje process är medlem i exakt en användarnamnrymd. En process som skapas via fork(2) eller clone(2) utan flaggan CLONE_NEWUSER är medlem av samma användarnamnrymd som sin förälder. En enkeltrådad process kan gå in i en annan användarnamnrymd med setns(2) om den har CAP_SYS_ADMIN i den namnrymden; när den gör det får den en fullständig uppsättning av förmågor i den namnrymden.

Ett anrop av clone(2) eller unshare(2) med flaggan CLONE_NEWUSER gör den nya barnprocessen (för clone(2)) eller anroparen (för unshare(2)) medlem av den nya användarnamnrymden som skapas av anropet.

Åtgärden NS_GET_PARENT till ioctl(2) kan användas för att upptäcka föräldrarelationen mellan användarnamnrymder; se ioctl_ns(2).

En uppgift som ändrar ett av sina effektiva ID:n kommer ha sin dumpbarhet återställd till värdet i /proc/sys/fs/suid_dumpable. Detta kan påverka ägandet av proc-filer för barnprocesser och kan alltså orsaka att föräldern kommer att sakna rättigheter att skriva till avbildningsfiler för barnprocesser som kör i en ny användarnamnrymd. I dessa fall kan man genom att göra processen dumpbar med PR_SET_DUMPABLE i ett anrop av prctl(2) före en barnprocess skapas i en ny användarnamnrymd åtgärda detta problem. Se prctl82) och proc(5) för detaljer om hur ägarskap påverkas.

Barnprocesserna som skapas med clone(2) med flaggan CLONE_NEWUSER börjar med en fullständig uppsättning av förmågor i den nya användarnamnrymden. Likadant får en process som skapar en ny användarnamnrymd med unshare(2) eller går in i en befintlig användarnamnrymd med setns(2) en fullständig uppsättning rättigheter i den namnrymden. Å andra sidan har den processen inga förmågor i förälderns (i fallet clone(2)) eller tidigare (i fallet unshare(2) och setns(2)) användarnamnrymd, även om root-anvädaren skapade eller gick in i den nya namnrymden (d.v.s., en process med användar-ID 0 i rotnamnrymden).

Observera att ett anrop av execve(2) kommer göra att processens förmågor räknas om på det vanliga sättet (se capabilities(7)). Alltså, om inte processen har användar-ID 0 inom namnrymden, eller den körbara filen har en ärvbar förmågemask som inte är tom, kommer processen förlora alla förmågor. Se diskussionen om användar- och grupp-ID-avbildningar nedan.

Ett anrop av clone(2) eller unshare(2) med flaggan CLONE_NEWUSER eller ett anrop av setns(2) som flyttar anroparen in i en annan användarnamnrymd sätter flaggorna ”securebits” (se capabilities(7)) till sitt standardvärde (alla flaggor avslagna) i barnet (för clone(2)) eller anroparen (för unshare(2) eller setns(2)). Observera att eftersom anroparen inte längre har förmågor i sin originalanvändarnamnrymd efter anropet av setns(2) är det inte möjligt för en process att återställa sina flaggor ”securebits” medan den behåller sitt medlemskap i användarnamnrymden genom att använda ett par av anrop av setns(2) för att flytta till en annan användarnamnrymd och sedan återvända till sin originalanvändarnamnrymd.

Reglerna för att avgöra huruvida en process har en förmåga eller inte i en viss användarnamnrymd är som följer:

En process har en förmåga inuti en användarnamnrymd om den är medlem av den namnrymden och den har förmågan i sin effektiva förmågemängd. En process kan få förmågor i sin effektiva förmågemängd på olika sätt. Till exempel kan den köra ett sätt-användar-ID-program eller en körbar fil associerad med filförmågor. Dessutom kan en process få förmågor via effekten av clone(2), unshare(2) eller setns(2), som redan beskrivits.
Om en process har en förmåga i en användarnamnrymd, då har den den förmågan i alla barn- (och mer avlägsna avkomme-)namnrymder också.
När en användarnamnrymd skapas sparar kärnan det effektiva användar-ID:t för processen som skapar den som varandes ”ägaren” till namnrymden. En process som bor i föräldern till användarnamnrymden och vars effektiva användar-ID stämmer med ägaren till namnrymden har alla förmågor i namnrymden. Tack vare föregående regel har processen alla förmågor i alla mer avlägsna avkommeanvändarnamnrymder också. Åtgärden NS_GET_OWNER_UID till ioctl(2) kan användas för att upptäcka användar-ID för namnrymdens ägare; se ioctl_ns(2).

Att ha en förmåga inuti en användarnamnrymd tillåter en process att utföra åtgärder (som kräver privilegier) endast på resurser som styrs av den namnrymden. Med andra ord, att ha en förmåga i en användarnamnrymd tillåter en process att utföra privilegierade åtgärder på resurser som styrs av (icke användar-)namnrymder som ägs av (är associerade med) användarnamnrymden (se nästa underavdelning).

Däremot finns det många privilegierade åtgärder som påverkar resurser som inte är associerade med någon namnrymdstyp, till exempel, att ändra systemtiden (d.v.s., kalendertiden) (som styrs av CAP_SYS_TIME), att ladda kärnmoduler (som styrs av CAP_SYS_MODULE) och att skapa en enhet (som styrs av CAP_MKNOD). Endast en process med privilegier i den initiala användarnamnrymden kan utföra sådana åtgärder.

Att hålla CAP_SYS_ADMIN inom användarnamnrymden som äger en process monteringsnamnrymd gör att den processen kan skapa bindmonteringar och montera följande typer av filsystem:

/proc (från Linux 3.8)
/sys (från Linux 3.8)
devpts (från Linux 3.9)
tmpfs(5) (från Linux 3.9)
ramfs (från Linux 3.9)
mqueue (från Linux 3.9)
bpf (från Linux 4.4)
overlayfs (från Linux 5.11)

Att hålla CAP_SYS_ADMIN inom användarnamnrymden som äger en process cgroup-namnrymd gör (från Linux 4.6) att den processen kan montera filsystemet cgroup version 2 och namngivna hierarkier av filsystemet cgroup verson 1 (d.v.s., cgroup-filsystem monterade med flaggan "none,name=").

Att hålla CAP_SYS_ADMIN inom användarnamnrymden som äger en process PID-namnrymd gör (från Linux 3.8) att den processen kan montera filsystemet /proc.

Observera dock att montering av blockbaserade filsystem endast kan göras av en process som håller CAP_SYS_ADMIN i den initiala användarnamnrymden.

Med början från Linux 3.8 kan oprivilegierade processer skapa användarnamnrymder, och de andra typerna av namnrymder kan skapas med bara förmågan CAP_SYS_ADMIN i anroparens användarnamnrymd.

När en annan namnrymd än en användarnamnrymd skapas ägs den av användarnamnrymden processen som skapade den var en medlem i vid tidpunkten då namnrymden skapades. Privilegierade åtgärder på resurser som styrs av en icke-användarnamnrymd kräver att processen har de nödvändiga förmågorna i användarnamnrymden som äger den icke-användarnamnrymden.

Om CLONE_NEWUSER anges tillsammans med andra flaggor CLONE_NEW* i ett enskilt anrop av clone(2) eller unshare(2) garanteras det att användarnamnrymden skapas först vilket ger barnet (clone(2)) eller anroparen (unshare(2)) privilegier över de andra namnrymderna som skapas av anropet. Det är alltså möjligt för en oprivilegierad anropare att ange denna kombination av flaggor.

När en ny namnrymd (annan än en användarnamnrymd) skapas via clone(2) eller unshare(2) registrerar kärnan användarnamnrymden för processen som skapar den som ägare till den nya namnrymden. (Denna koppling kan inte ändras.) När en process i den nya namnrymden senare utför privilegierade åtgärder som verkar på globala resurser isolerade av namnrymden utförs rättighetskontrollerna enligt processens förmågor i användarnamnrymden som kärnan associerade med den nya namnrymden. Till exempel, antag att en process försöker ändra värdnamnet (sethostname(2)), en resurs som styrs av UTS-namnrymden. I detta fall kommer kärnan avgöra vilken användarnamnrymd som äger processens UTS-namnrymd, och kontrollera huruvida processen har den nödvändiga förmågan (CAP_SYS_ADMIN) i den användarnamnrymden.

ioctl(2)-åtgärden NS_GET_USERNS kan användas för att hitta användarnamnrymden som äger en icke-användarnamnrymd; se ioctl_ns(2).

När en användarnamnrymd skapas börjar den utan någon avbildning av användar-ID:n (grupp-ID:n) till förälderns användarnamnrymd. Filerna /proc/pid/uid_map och /proc/pid/gid_map (tillgängliga från Linux 3.5) visar avbildningarna för användar- och grupp-ID:n inuti användarnamnrymden för processen pid. Dessa filer kan läsas för att visa avbildningarna i en användarnamnrymd och skrivas till (en gång) för att definiera avbildningarna.

Beskrivningen i följande stycken förklarar detaljerna för uid_map; gid_map är exakt likadan, med varje förekomst av ”användar-ID” ersatt med ”grupp-ID”.

Filen uid_map visar avbildningen av användar-ID:n från användarnamnrymden för processen som öppnade uid_map (men se en avvikelse från denna punkt nedan). Med andra ord kan potentiellt processer som finns i olika användarnamnrymder se olika värden när de läser från en viss uid_map-fil, beroende på användar-ID-avbildningarna för användarnamnrymderna för de läsande processerna.

Varje rad i filen uid_map anger en 1-till-1-avbildning av ett intervall av sammanhängande användar-ID:n mellan två användarnamnrymder. (När en användarnamnrymd först skapas är denna fil tom.) Specifikationen på varje rad har formen tre tal avgränsade av mellanslag. De första två talen anger det första användar-ID:t i vardera av de två användarnamnrymderna. Det tredje talet anger längden på det avbildade intervallet. Mer exakt tolkas fälten som följer:

(1)
Början av intervallet av användar-ID:n i processen pids användarnamnrymd.
(2)
Början på intervallet av användar-ID:n till vilka användar-ID:n som anges av fält ett avbildas. Hur fält två tolkas beror på huruvida processen som öppnade uid_map och processen pid finns i samma användarnamnrymd, som följer:
(a)
Om de två processerna finns i olika användarnamnrymder: fält två är början på ett intervall av användar-ID:n i användarnamnrymden för processen som öppnade uid_map.
(b)
Om de två processerna finns i samma användarnamnrymd: fält två är början på intervallet av användar-ID:n i process pids föräldraanvändarnamnrymd. Detta gör att den som öppnar uid_map (det vanliga fallet här är att öppna /proc/self/uid_map) kan se avbildningen av användar-ID:n in i användarnamnrymden för processen som skapade denna användarnamnrymd.
(3)
Längden på intervallet av användar-ID:n som avbildas mellan de två användarnamnrymderna.

Systemanrop som returnerar användar-ID:n (grupp-ID:n) — till exempel getuid(2), getgid(2), och kreditivfälten i posten som returneras av stat(2) — returnerar användar-ID:t (grupp-ID:t) avbildat in i anroparens användarnamnrymd.

När en process begär tillgång till en fil avbildas dess användar- och grupp-ID:n in i den initiala användarnamnrymden i syfte att kontrollera rättigheterna och tilldela ID:n när en fil skapas. När en process hämtar filens användar- och grupp-ID:n via stat(2) avbildas ID:erna i den andra riktningen, för att skapa värden relativt processens användar- och grupp-ID-avbildningar.

Den initiala användarnamnrymden har ingen föräldranamnrymd, men för konsistensens skull tillhandahåller kärnan tomfiler för användar- och grupp-ID-avbildningar för den namnrymden. Tittar man på filen uid_map (gid_map är likadan) från ett skal i den initiala namnrymden ser man:


$ cat /proc/$$/uid_map

0 0 4294967295

Denna avbildning säger oss att intervallet som startar med användar-ID 0 i denna namnrymd översätts till ett intervall som startar med 0 i den (obefintliga) föräldranamnrymden, och att längden på intervallet är det största 32-bitars teckenlösa heltalet. Detta lämnar 4294967295 (32-bitsvärdet -1) oavbildat. Detta är avsiktligt: (uid_t) -1 används i flera gränssnitt (t.ex., setreuid(2)) som ett sätt att ange ”inget användar-ID”. Genom att låta (uid_t) -1 vara oavbildat och oanvändbart garanteras att det inte kommer uppstå någon förvirring vid användning av dessa gränssnitt.

Efter att en ny användarnamnrymd skapats kan filen uid_map för en av processerna i namnrymden skrivas till en gång för att definiera avbildningen av användar-ID:n i den nya användarnamnrymden. Ett försök att skriva mer än en gång till en uid_map-fil i en användarnamnrymd misslyckas med felet EPERM. Motsvarande regler gäller för gid_map-filer.

Raderna som skrivs till uid_map (gid_map) måste lyda följande giltighetsregler:

De tre fälten måste vara giltiga tal, och det sista fältet måste vara större än 0.
Rader avslutas av nyradstecken.
Det finns en gräns på antalet rader i filen. I Linux 4.14 och tidigare sattes denna gräns (godtyckligt) till 5 rader. Från Linux 4.15 är gränsen 340 rader. Dessutom måste antalet byte som skrivs till filen vara mindre än systemets sidstorlek, och skrivningen måste göras från början av filen (d.v.s., lseek(2) och pwrite(2) kan inte användas för att skriva till nollskilda avstånd in i filen).
Intervallet av användar-ID:n (grupp-ID:n) som anges på varje rad får inte överlappa med intervallerna på några andra rader. I den ursprungliga implementationen (Linux 3.8) uppfylldes detta krav av en enkel implementation som lade till det ytterligare kravet att värdena i både fält 1 och fält 2 av följande rader måste vara i stigande numerisk ordning, vilket förhindrade några eljest giltiga avbildningar från att skapas. Linux 3.9 och senare fixar denna begränsning, och tillåter alla giltiga uppsättningar av avbildningar som inte överlappar.
Åtminstone en rad måste skrivas till filen.

Skrivningar som bryter mot ovanstående regler misslyckas med felet EINVAL.

För att en process skall kunna skriva till filen /procpid/uid_map (/procpid/gid_map) måste alla följande rättighetskrav vara uppfyllda:

Den skrivande processen måste ha förmågan CAP_SETUID (CAP_SETGID) i processen pids användarnamnrymd.
Den skrivande processen måste antingen finnas i namnrymden för process pid eller finnas i föräldraanvändarnamnrymden till process pid.
De avbildade användar-ID:na (grupp-ID:na) måste i sin tur ha en avbildning i föräldraanvändarnamnrymden.
Om man uppdaterar /proc/pid/uid_map för att skapa en avbildning som avbildar till UID 0 i föräldranamnrymden måste en av de följande vara sann:
(a)
om den skrivande processen finns i föräldraanvändarnamnrymden, då måste den ha förmågan CAP_SETFCAP i den användarnamnrymden; eller
(b)
om den skrivande processen finns i barnanvändarnamnrymden, då måste processen som skapade användarnamnrymden ha haft förmågan CAP_SETFCAP när namnrymden skapades.
Denna regel har funnits från Linux 5.12. Den eliminerar en tidigare säkerhetsbrist varigenom en UID 0-process som saknar förmågan CAP_SETFCAP vilken behövs för att skapa en binär med namnrymdsfilförmågor (så som det beskrivs i capabilities(7)) ändå kunde skapa en sådan binär genom följande steg:
(1)
Skapa en ny användarnamnrymd med identitetsavbildningen (d.v.s., AID 0 i den nya användarnamnrymden avbildas på AID 0 i föräldranamnrymden), så att AID 0 båda namnrymderna är ekvivalent med samma root-användar-ID.
(2)
Eftersom barnprocessen har förmågan CAP_SETFCAP kunde den skapa en binär med namnrymdsfilförmågor som sedan skulle vara verkningsfulla i föräldranamnrymden (eftersom root-användar-ID:t är desamma i de två namnrymderna).
En av följande två fall gäller:
(a)
Antingen har den skrivande processen förmågan CAP_SETUID (CAP_SETGID) i föräldraanvändarnamnrymden.
Inga ytterligare begränsningar gäller: processen kan göra avbildningar till godtyckliga användar-ID:n (grupp-ID:n) i föräldraanvändarnamnrymden.
(b)
Eller i annat fall gäller alla följande beskrivningar:
Data som skrivs till uid_map (gid_map) måste bestå av en enda rad som avbildar den skrivande processens effektiva användar-ID (grupp-ID) i föräldraanvändarnamnrymden till ett användar-ID (grupp-ID) i användarnamnrymden.
Den skrivande processen måste ha samma effektiva användar-ID som processen som skapade användarnamnrymden.
När det gäller gid_map måste först användning av systemanropet setgroups(2) nekas genom att skriva "deny" till filen /proc/pid/setgroups (se nedan) före någon skrivning till gid_map.

Skrivningar som strider mot ovanstående regler misslyckas med felet EPERM.

Liksom för användar- och grupp-ID-avbildningar är det möjligt att skapa projekt-ID-avbildningar för användarnamnrymder. (Projekt-ID:n används för diskkvoter; se setquota(8) och quotactl(2).)

Projekt-ID-avbildningar definieras genom att skriva till filen /proc/pid/projid_map (som finns från Linux 3.7).

Giltighetsreglerna för att skriva till filen /proc/pid/projid_map är som för att skriva till filen uid_map; brott mot dessa regler får write(2) att misslyckas med felet EINVAL.

Rättighetsreglerna för att skriva till filen /proc/pid/projid_map är som följer:

Den skrivande processen måste antingen finnas i namnrymden för process pid eller finnas i föräldraanvändarnamnrymden till process pid.
De avbildade projekt-ID:na måste i sin tur ha en avbildning i föräldraanvändarnamnrymden.

Brott mot dessa regler får write(2) att misslyckas med felet EPERM.

I en användarnamnrymd där filen uid_map inte har skrivits kommer systemanrop som ändrar användar-ID:n att misslyckas. På liknande sätt, om filen gid_map inte har skrivits kommer systemanrop som ändrar grupp-ID:n att misslyckas. Efter att filerna uid_map och gid_map har skrivits kan endast de avbildade värdena användas i systemanrop som ändrar användar- och grupp-ID:n.

För användar-ID:n inkluderar de relevanta systemanropen setuid(2), setfsuid(2), setreuid(2) och setresuid(2). För grupp-ID:n inkluderar de relevanta systemanropen setgid(2), setfsgid(2), setregid(2), setresgid(2) och setgroups(2).

Att skriva "deny" till filen /proc/pid/setgroups före man skriver till /proc/pid/gid_map kommer permanent avaktivera setgroups(2) i en användarnamnrymd och tillåta att man skriver till /proc/pid/gid_map utan att ha förmågan CAP_SETGID i föräldraanvändarnamnrymden.

Filen /proc/pid/setgroups visar strängen "allow" om processer i användarnamnrymden som innehåller process pid får lov att använda systemanropet setgroups(2); den visar "deny" om setgroups(2) inte tillåts i den användarnamnrymden. Observera att oavsett värdet i filen /proc/pid/setgroups (och oavsett processens förmågor) är inte heller setgroups(2) tillåtet om inte /proc/pid/gid_map har satts ännu.

En privilegierad process (en med förmågan CAP_SYS_ADMIN i namnrymden) kan skriva endera av strängarna "allow" eller "deny" till denna fil före den skriver en grupp-ID-avbildning för denna användarnamnrymd till filen /proc/pid/gid_map. Att skriva strängen "deny" förhindrar alla processer i användarnamnrymden från att använda setgroups(2).

Essensen av begränsningarna som beskrivs i föregående stycke är att det är tillåtet att skriva till /proc/pid/setgroups endast så länge som anrop av setgroups(2) är otillåtet för att /proc/pid/gid_map inte har satts. Detta säkerställer att en process inte kan gå över från ett tillstånd där setgroups(2) är tillåtet till ett tillstånd där setgroups(2) nekas; en process kan gå över endast från att setgroups(2) är otillåtet till att setgroups(2) är tillåtet.

Standardvärdet i denna fil i den initiala användarnamnrymden är "allow".

När /proc/pid/gid_map har skrivits till (vilket har effekten av att aktivera setgroups(2) i användarnamnrymden), är det inte längre möjligt att göra setgroups(2) otillåtet genom att skriva "deny" till /proc/pid/setgroups (skrivningen misslyckas med felet EPERM).

En barnnamnrymd ärver inställningen av /proc/pid/setgroups från sin förälder.

Om filen setgroups har värdet "deny", då kan inte senare systemanropet setgroups(2) återaktiveras (genom att skriva "allow" till filen) i den användarnamnrymden. (Försök att göra så misslyckas med felet EPERM.) Denna begränsning propagerar ner till alla barnanvändarnamnrymder till denna användarnamnrymd.

Filen /proc/pid/setgroups lades till i Linux 3.19, men bakåtporterades till många tidigare stabila kärnserier, eftersom den åtgärdar ett säkerhetsproblem. Problemet rörde filer med rättigheter såsom ”rwx---rwx”. Sådana filer ger färre rättigheter till ”gruppen” än de gör till ”övriga”. Detta betyder att genom att släppa grupper med setgroups(2) kan en process tillåtas filåtkomst som den inte tidigare hade. Före det fanns användarnamnrymder var detta inte ett bekymmer, eftersom endast en privilegierad process (en med förmågan CAP_SETGID) kunde anropa setgroups(2). Dock, med introduktionen av användarnamnrymder blev det möjligt för en oprivilegierad process att skapa en ny namnrymd i vilken användaren har alla privilegier. Detta tillät sedan tidigare oprivilegierade användare att släppa grupprivilegier och därmed få filåtkomst som de inte tidigare hade. Filen /proc/pid/setgroups lades till för att åtgärda detta säkerhetsproblem, genom att neka alla vägar för en oprivilegierad process att släppa grupper med setgroups(2).

Det finns olika platser där ett oavbildat användar-ID (grupp-ID) kan exponeras för användarrymden. Till exempel kan den första processen i en ny användarnamnrymd anropa getuid(2) innan en användar-ID-avbildning har definierats för namnrymden. I de flesta sådana fall konverteras ett oavbildat användar-ID till spillanvändar-ID:t (grupp-ID:t); standardvärdet på spillanvändar-ID:t (grupp-ID:t) 65534). Se beskrivningarna av /proc/sys/kernel/overflowuid och /proc/sys/kernel/overflowgid i proc(5).

Fallen där oavbildade ID:n avbildas på detta sätt inkluderar systemanrop som returnerar användar-ID:n (getuid(2), getgid(2) och liknande), kreditiv som skickas över ett UNIX-domänsuttag, kreditiv som returneras av stat(2), waitid(2) och System V IPC ”ctl”-åtgärder IPC_STAT, kreditiv som exponeras av /proc/pid/status och filerna i /proc/sysvipc/*, kreditiv som returneras via fältet si_uid i den siginfo_t som tas emot med en signal (se sigaction(2)), kreditiv som skrivs till processbokföringsfilen (se acct(5)) och kreditiv som returneras via POSIX notifieringar i meddelandeköer (se mq_notify(3)).

Det finns ett nämnvärt fall där oavbildade användar- och grupp-ID:n inte konverteras till motsvarande spill-ID-värde. När man betraktar en fil uid_map eller gid_map i vilken det inte finns någon avbildning för det andra fältet visas det fältet som 4294967295 (-1 som ett teckenlöst heltal).

Åtkomst av filer

För att avgöra rättigheterna när en oprivilegierad process söker tillgång till en fil avbildas processkreditiven (AID, GID) och de aktiva filkreditiven tillbaka till vad de skulle vara i den initiala användarnamnrymden och jämförs sedan för att avgöra rättigheterna som processen har till filen. Detsamma är också sant för andra objekt som använder åtkomstmodellen med kreditiv plus rättighetsmasker, såsom System V IPC-objekt.

Åtgärder med filrelaterade förmågor

Vissa förmågor tillåter en process att kringgå olika begränsningar som kärnan upprätthåller när den utför åtgärder på filer som ägs av andra användare eller grupper. Dessa förmågor är: CAP_CHOWN, CAP_DAC_OVERRIDE, CAP_DAC_READ_SEARCH, CAP_FOWNER och CAP_FSETID.

Inom en användarnamnrymd tillåter dessa förmågor en process att kringgå reglerna om processen har den relevanta förmågan över filen, vilket betyder:

processen har den relevanta effektiva förmågan i sin användarnamnrymd; och
filens användar-ID och grupp-ID båda har giltiga avbildningar i användarnamnrymden.

Förmågan CAP_FOWNER behandlas något speciellt: den tillåter en process att kringgå motsvarande regler så länge som åtminstone filens användar-ID har en avbildning i användarnamnrymden (d.v.s., filens grupp-ID behöver inte ha en giltig avbildning).

När en process inuti en användarnamnrymd kör ett program som är sätt-användar-ID (sätt-grupp-ID) ändras processens effektiva användar- (grupp-)ID inuti namnrymden till det värde som är avbildat för filens användar- (grupp-)ID. Dock, om antingen filens användar- eller grupp-ID inte har någon avbildning inuti namnrymden ignoreras tyst biten sätt-användar-ID (sätt-grupp-ID): det nya programmet körs, men processens effektiva användar- (grupp-)ID lämnas orört. (Detta avspeglar semantiken hos att köra ett program med sätt-användar-ID eller sätt-grupp-ID som bor på ett filsystem som monterades med flaggan MS_NOSUID, såsom beskrivs i mount(2).)

När en process användar- och grupp-ID:n skickas över ett UNIX-domänsuttag till en process i en annan användarnamnrymd (se beskrivningen av SCM_CREDENTIALS i unix(7)) översätts de till de motsvarande värdena i enlighet med den mottagande processens användar- och grupp-ID-avbildningar.

Linux.

Under åren har det funnits många funktioner som har lagts till till Linuxkärnan som har gjorts tillgängliga endast till privilegierade användare på grund av deras potential att förvirra sätt-användar-ID-root-program. I allmänhet blir det säkert att tillåta root-användaren i en användarnamnrymd att använda dessa funktioner eftersom det är omöjligt, så länge man finns i en användarnamnrymd, att få fler privilegier än de som en användarnamnrymds root-användare har.

Termen ”global root” används ibland som en kortform för användar-ID 0 i den initiala användarnamnrymden.

Användning av användarnamnrymder kräver en kärna som är konfigurerad med alternativet CONFIG_USER_NS. Användarnamnrymder kräver stöd i ett antal undersystem i hela kärnan. När ett undersystem som inte stödjs konfigureras i kärnan är det inte möjligt att konfigurera stöd för användarnamnrymder.

Fram till Linux 3.8 stödde de mest relevanta subsystemen användarnamnrymder, men ett antal filsystem hade inte den nödvändiga infrastrukturen för att avbilda användar- och grupp-ID:n mellan användarnamnrymder. Linux 3.9 lade till det nödvändiga infrastrukturstödet till många av de återstående ej stödda filsystemen (Plan 9 (9P), Andrew File System (AFS), Ceph, CIFS, CODA, NFS och OCFS2). Linux 3.12 lade till stöd till det sista av de ej stödda större filsystemen, XFS.

Nedanstående program är skapat för att möjliggöra experimenterande med användarnamnrymder, liksom även andra typer av namnrymder. Det skapar namnrymder enligt specifikationen från kommandoradsflaggor och kör sedan ett kommando inuti dessa namnrymder. Kommentarerna och funktionen usage() inuti programmet ger den fullständiga förklaringen av programmet. Följande skalsession demonstrerar dess användning.

Först betraktar vi körtidsmiljön:


$ uname -rs     # Förutsätter Linux 3.8 eller senare
Linux 3.8.0
$ id -u         # Kör som en oprivilegierad användare
1000
$ id -g
1000

Starta nu ett skal i en ny användar- (-U), monterings- (-m) och PID- (-p) namnrymd, med användar-ID (-M) och grupp-ID (-G) 1000 avbildade på 0 inuti användarnamnrymden:


$ ./userns_child_exec -p -m -U -M '0 1000 1' -G '0 1000 1' bash

Skalet har PID 1, för att det är den första processen i den nya PID-namnrymden.


bash$ echo $$
1

Att montera ett nytt /proc-filsystem och lista alla processerna som är synliga i den nya PID-namnrymden visar att skalet inte kan se några processer utanför PID-namnrymden:


bash$ mount -t proc proc /proc
bash$ ps ax

PID TTY STAT TIME COMMAND
1 pts/3 S 0:00 bash
22 pts/3 R+ 0:00 ps ax

Inuti användarnamnrymden har skalet användar- och grupp-ID 0, och den kompletta uppsättningen av tillåtna och effektiva förmågor:


bash$ cat /proc/$$/status | egrep '^[UG]id'
Uid:	0	0	0	0
Gid:	0	0	0	0
bash$ cat /proc/$$/status | egrep '^Cap(Prm|Inh|Eff)'
CapInh:	0000000000000000
CapPrm:	0000001fffffffff
CapEff:	0000001fffffffff

/* userns_child_exec.c

Licensed under GNU General Public License v2 or later
Skapa en barnprocess som kör ett skalkommando i nya namnrymder;
låt UID- och GID-avbildningar anges när en användarnamnrymd
skapas. */ #define _GNU_SOURCE #include <err.h> #include <sched.h> #include <unistd.h> #include <stdint.h> #include <stdlib.h> #include <sys/wait.h> #include <signal.h> #include <fcntl.h> #include <stdio.h> #include <string.h> #include <limits.h> #include <errno.h> struct child_args {
char **argv; /* Kommando att köras av barnet, med argument */
int pipe_fd[2]; /* Rör använt för att synkronisera förälder och barn */ }; static int verbose; static void usage(char *pname) {
fprintf(stderr, "Användning: %s [flaggor] kmd [arg…]\n\n", pname);
fprintf(stderr, "Skapa en barnprocess som kör ett skalkommando i "
"en ny användarnamnrymd,\n"
"och eventuellt även andra nya namnrymder.\n\n");
fprintf(stderr, "Flaggor kan vara:\n\n"); #define fpe(str) fprintf(stderr, " %s", str);
fpe("-i Ny IPC-namnrymd\n");
fpe("-m Ny monteringsnamnrymd\n");
fpe("-n Ny nätverksnamnrymd\n");
fpe("-p Ny PID-namnrymd\n");
fpe("-u Ny UTS-namnrymd\n");
fpe("-U Ny användarnamnrymd\n");
fpe("-M uid_map Ange AID-avbildning för användarnamnrymden\n");
fpe("-G gid_map Ange GID-avbildning för användarnamnrymden\n");
fpe("-z Avbilda användar-AID och -GID till 0 i användarnamnrymden\n");
fpe(" (ekvivalent med: -M '0 <aid> 1' -G '0 <gid> 1')\n");
fpe("-v Visa utförliga meddelanden\n");
fpe("\n");
fpe("Om -z, -M eller -G anges är -U nödvändigt.\n");
fpe("Det är inte tillåtet att ange både -z och någon av -M eller -G.\n");
fpe("\n");
fpe("Avbildningssträngar för -M och -G består av poster på formen:\n");
fpe("\n");
fpe(" ID-inuti-nr ID-utanför-nr längd\n");
fpe("\n");
fpe("En avbildningssträng kan innehålla flera poster, separerade"
" av komman;\n");
fpe("kommatecknen ersätts med nyrader före skrivning"
" till avbildningsfiler.\n");
exit(EXIT_FAILURE); } /* Uppdatera avbildningsfilen 'map_fil', med värdena som ges i
'avbildning', en sträng som definierar en AID- eller GID-avbildning.
En AID- eller GID-avbildning består av en eller flera nyradsavgränsade poster
på formen:
ID_inuti-nr ID-utanför-nr längd
Att begära att användaren skall ge en sträng som innehåller nyrader är
naturligtvis opraktiskt för kommandoradsanvändning. Därför tillåter vi
användningen av komman för att avgränsa poster i denna sträng, och
ersätter dem med nyrader före vi skriver strängen till filen. */" static void update_map(char *mapping, char *map_file) {
int fd;
size_t map_len; /* Längden på \[aq]avbildning\[aq] */
/* Ersätt kommatecken i avbildningssträngar med nyrader. */
map_len = strlen(mapping);
for (size_t j = 0; j < map_len; j++)
if (mapping[j] == ',')
mapping[j] = '\n';
fd = open(map_file, O_RDWR);
if (fd == -1) {
fprintf(stderr, "FEL: open %s: %s\n", map_file,
strerror(errno));
exit(EXIT_FAILURE);
}
if (write(fd, mapping, map_len) != map_len) {
fprintf(stderr, "FEL: write %s: %s\n", map_file,
strerror(errno));
exit(EXIT_FAILURE);
}
close(fd); } /* Linux 3.19 gjorde en ändring av hanteringen av setgroups(2) och filen
'gid_map' för att åtgärda ett säkerhetsproblem. Problemet tillät
*oprivilegierade* användare att nyttja användarnamnrymder för att släppa
grupper. Följden av ändringarna i 3.19 är att för att uppdatera filen
'gid_maps' måste först användning av systemanropet setgroups()
i denna användarnamnrymd avaktiveras genom att skriva "deny" till en
av filerna /proc/PID/setgroups för denna namnrymd. Det är syftet med
följande funktion. */ static void proc_setgroups_write(pid_t child_pid, char *str) {
char setgroups_path[PATH_MAX];
int fd;
snprintf(setgroups_path, PATH_MAX, "/proc/%jd/setgroups",
(intmax_t) child_pid);
fd = open(setgroups_path, O_RDWR);
if (fd == -1) {
/* Vi kan vara på ett system som inte stödjer /proc/PID/setgroups.
I det fallet kommer filen inte att finnas, och systemet kommer
inte att påtvinga begränsningarna som Linux 3.19 lade till. Det
är bra så: vi behöver inte göra något för att tillåta att
'gid_map' uppdateras.
Dock, om felet från open() var något annat än felet ENOENT
som förväntas i det fallet, tala om det för användaren. */
if (errno != ENOENT)
fprintf(stderr, "FEL: open %s: %s\n", setgroups_path,
strerror(errno));
return;
}
if (write(fd, str, strlen(str)) == -1)
fprintf(stderr, "FEL: write %s: %s\n", setgroups_path,
strerror(errno));
close(fd); } static int /* Startfunktion för klonat barn */ childFunc(void *arg) {
struct child_args *args = arg;
char ch;
/* Vänta tills föräldern har uppdaterat AID- och GID-avbildningarna.
Se kommentaren i main(). Vi väntar på filslut från ett rör som
kommer stängas av föräldraprocessen när den har uppdaterat
avbildningarna. */
close(args->pipe_fd[1]); /* Stäng vår beskrivare för skrivänden
av röret så att vi ser EOF när föräldern
stänger sin beskrivare. */
if (read(args->pipe_fd[0], &ch, 1) != 0) {
fprintf(stderr,
"Fel i barnet: läsning från röret returnerade ≠ 0\n");
exit(EXIT_FAILURE);
}
close(args->pipe_fd[0]);
/* Kör ett skalkommando. */
printf("I begrepp att göra exec %s\n", args->argv[0]);
execvp(args->argv[0], args->argv);
err(EXIT_FAILURE, "execvp"); } #define STACK_SIZE (1024 * 1024) static char child_stack[STACK_SIZE]; /* Utrymme för barnets stack */ int main(int argc, char *argv[]) {
int flags, opt, map_zero;
pid_t child_pid;
struct child_args args;
char *uid_map, *gid_map;
const int MAP_BUF_SIZE = 100;
char map_buf[MAP_BUF_SIZE];
char map_path[PATH_MAX];
/* Tolka kommandoradsflaggor. Det inledande tecknet '+' i det
sista argumentet till getopt() förhindrar permutationer av
kommandoradsflaggor enligt GNU-stil. Det är användbart, eftersom
ibland 'kommandot' som skall köras av detta program självt
har kommandoradsflaggor. Vi vill inte att getopt() skall hantera
dessa som flaggor till detta program. */
flags = 0;
verbose = 0;
gid_map = NULL;
uid_map = NULL;
map_zero = 0;
while ((opt = getopt(argc, argv, "+imnpuUM:G:zv")) != -1) {
switch (opt) {
case 'i': flags |= CLONE_NEWIPC; break;
case 'm': flags |= CLONE_NEWNS; break;
case 'n': flags |= CLONE_NEWNET; break;
case 'p': flags |= CLONE_NEWPID; break;
case 'u': flags |= CLONE_NEWUTS; break;
case 'v': verbose = 1; break;
case 'z': map_zero = 1; break;
case 'M': uid_map = optarg; break;
case 'G': gid_map = optarg; break;
case 'U': flags |= CLONE_NEWUSER; break;
default: usage(argv[0]);
}
}
/* -M eller -G utan -U är meningslöst */
if (((uid_map != NULL || gid_map != NULL || map_zero) &&
!(flags & CLONE_NEWUSER)) ||
(map_zero && (uid_map != NULL || gid_map != NULL)))
usage(argv[0]);
args.argv = &argv[optind];
/* Vi använder ett rör för att synkronisera förälder och barn, för
att säkerställa att föräldern ställer in UID- och GID-avbildningarna
före barnet anropar execve(). Detta ser till att barnet behåller
sina förmågor under execve() i det vanliga fallet då vi vill avbilda
barnets effektiva användar-ID på 0 i den nya användarnamnrymden.
Utan denna synkronisering skulle barnet tappa sina förmågor om det
utförde en execve() med användar-ID:n skilda från noll (se
manualsidan capabilities(7) för detaljer om transformationen av en
process förmågor under execve()). */
if (pipe(args.pipe_fd) == -1)
err(EXIT_FAILURE, "pipe");
/* Skapa barnet i nya namnrymder. */
child_pid = clone(childFunc, child_stack + STACK_SIZE,
flags | SIGCHLD, &args);
if (child_pid == -1)
err(EXIT_FAILURE, "clone");
/* Föräldern faller igenom hit. */
if (verbose)
printf("%s: PID för barnet som skapades av clone() är %jd\n",
argv[0], (intmax_t) child_pid);
/* Uppdatera UID- och GID-avbildningarna i barnet. */
if (uid_map != NULL || map_zero) {
snprintf(map_path, PATH_MAX, "/proc/%jd/uid_map",
(intmax_t) child_pid);
if (map_zero) {
snprintf(map_buf, MAP_BUF_SIZE, "0 %jd 1",
(intmax_t) getuid());
uid_map = map_buf;
}
update_map(uid_map, map_path);
}
if (gid_map != NULL || map_zero) {
proc_setgroups_write(child_pid, "deny");
snprintf(map_path, PATH_MAX, "/proc/%jd/gid_map",
(intmax_t) child_pid);
if (map_zero) {
snprintf(map_buf, MAP_BUF_SIZE, "0 %ld 1",
(intmax_t) getgid());
gid_map = map_buf;
}
update_map(gid_map, map_path);
}
/* Stäng skrivänden av röret för att signalera till barnet att vi
har uppdaterat UID- och GID-avbildningarna. */
close(args.pipe_fd[1]);
if (waitpid(child_pid, NULL, 0) == -1) /* Wait for child */
err(EXIT_FAILURE, "waitpid");
if (verbose)
printf("%s: avslutar\n", argv[0]);
exit(EXIT_SUCCESS); }

newgidmap(1), newuidmap(1), clone(2), ptrace(2), setns(2), unshare(2), proc(5), subgid(5), subuid(5), capabilities(7), cgroup_namespaces(7), credentials(7), namespaces(7), pid_namespaces(7)

Kärnans källfil Documentation/admin-guide/namespaces/resource-control.rst.

ÖVERSÄTTNING

Den svenska översättningen av denna manualsida skapades av Göran Uddeborg <goeran@uddeborg.se>

Denna översättning är fri dokumentation; läs GNU General Public License Version 3 eller senare för upphovsrättsvillkor. Vi tar INGET ANSVAR.

Om du hittar fel i översättningen av denna manualsida, skicka ett mail till Tp-sv@listor.tp-sv.se.

3 maj 2023 Linux man-pages 6.05.01