[{"data":1,"prerenderedAt":762},["ShallowReactive",2],{"mdc--au5f0s-key":3,"mdc--w2qcdm-key":29,"mdc-nyivwe-key":262,"mdc-cc3tb8-key":285,"mdc--shur7c-key":336,"mdc-pdzmlr-key":348,"mdc--qdp4ai-key":521,"mdc--p1dici-key":531,"mdc-47x558-key":606,"mdc--lcfrml-key":665,"mdc--fugku7-key":729},{"data":4,"body":5},{},{"type":6,"children":7},"root",[8,24],{"type":9,"tag":10,"props":11,"children":12},"element","p",{},[13,16,22],{"type":14,"value":15},"text","A ",{"type":9,"tag":17,"props":18,"children":19},"strong",{},[20],{"type":14,"value":21},"namespace",{"type":14,"value":23}," wraps a global system resource so that the processes inside it see their own copy of it. That is the entire idea. The kernel has one process table, but a process in a PID namespace sees only the entries belonging to that namespace — and numbered from 1.",{"type":9,"tag":10,"props":25,"children":26},{},[27],{"type":14,"value":28},"Namespaces are what makes a container's view of the machine false. Everything else in this course is either a resource limit or a filesystem trick.",{"data":30,"body":31},{},{"type":6,"children":32},[33,40,249],{"type":9,"tag":34,"props":35,"children":37},"h2",{"id":36},"the-seven-kinds",[38],{"type":14,"value":39},"The seven kinds",{"type":9,"tag":41,"props":42,"children":43},"table",{},[44,68],{"type":9,"tag":45,"props":46,"children":47},"thead",{},[48],{"type":9,"tag":49,"props":50,"children":51},"tr",{},[52,58,63],{"type":9,"tag":53,"props":54,"children":55},"th",{},[56],{"type":14,"value":57},"Namespace",{"type":9,"tag":53,"props":59,"children":60},{},[61],{"type":14,"value":62},"Isolates",{"type":9,"tag":53,"props":64,"children":65},{},[66],{"type":14,"value":67},"Flag",{"type":9,"tag":69,"props":70,"children":71},"tbody",{},[72,99,124,149,174,199,224],{"type":9,"tag":49,"props":73,"children":74},{},[75,84,89],{"type":9,"tag":76,"props":77,"children":78},"td",{},[79],{"type":9,"tag":17,"props":80,"children":81},{},[82],{"type":14,"value":83},"PID",{"type":9,"tag":76,"props":85,"children":86},{},[87],{"type":14,"value":88},"process IDs — the container's first process is PID 1",{"type":9,"tag":76,"props":90,"children":91},{},[92],{"type":9,"tag":93,"props":94,"children":96},"code",{"className":95},[],[97],{"type":14,"value":98},"--pid",{"type":9,"tag":49,"props":100,"children":101},{},[102,110,115],{"type":9,"tag":76,"props":103,"children":104},{},[105],{"type":9,"tag":17,"props":106,"children":107},{},[108],{"type":14,"value":109},"Mount",{"type":9,"tag":76,"props":111,"children":112},{},[113],{"type":14,"value":114},"the mount table — its own view of what is mounted where",{"type":9,"tag":76,"props":116,"children":117},{},[118],{"type":9,"tag":93,"props":119,"children":121},{"className":120},[],[122],{"type":14,"value":123},"--mount",{"type":9,"tag":49,"props":125,"children":126},{},[127,135,140],{"type":9,"tag":76,"props":128,"children":129},{},[130],{"type":9,"tag":17,"props":131,"children":132},{},[133],{"type":14,"value":134},"Network",{"type":9,"tag":76,"props":136,"children":137},{},[138],{"type":14,"value":139},"interfaces, routing tables, firewall rules, ports",{"type":9,"tag":76,"props":141,"children":142},{},[143],{"type":9,"tag":93,"props":144,"children":146},{"className":145},[],[147],{"type":14,"value":148},"--net",{"type":9,"tag":49,"props":150,"children":151},{},[152,160,165],{"type":9,"tag":76,"props":153,"children":154},{},[155],{"type":9,"tag":17,"props":156,"children":157},{},[158],{"type":14,"value":159},"UTS",{"type":9,"tag":76,"props":161,"children":162},{},[163],{"type":14,"value":164},"hostname and domain name",{"type":9,"tag":76,"props":166,"children":167},{},[168],{"type":9,"tag":93,"props":169,"children":171},{"className":170},[],[172],{"type":14,"value":173},"--uts",{"type":9,"tag":49,"props":175,"children":176},{},[177,185,190],{"type":9,"tag":76,"props":178,"children":179},{},[180],{"type":9,"tag":17,"props":181,"children":182},{},[183],{"type":14,"value":184},"IPC",{"type":9,"tag":76,"props":186,"children":187},{},[188],{"type":14,"value":189},"shared memory, semaphores, message queues",{"type":9,"tag":76,"props":191,"children":192},{},[193],{"type":9,"tag":93,"props":194,"children":196},{"className":195},[],[197],{"type":14,"value":198},"--ipc",{"type":9,"tag":49,"props":200,"children":201},{},[202,210,215],{"type":9,"tag":76,"props":203,"children":204},{},[205],{"type":9,"tag":17,"props":206,"children":207},{},[208],{"type":14,"value":209},"User",{"type":9,"tag":76,"props":211,"children":212},{},[213],{"type":14,"value":214},"UID and GID mapping — root inside, unprivileged outside",{"type":9,"tag":76,"props":216,"children":217},{},[218],{"type":9,"tag":93,"props":219,"children":221},{"className":220},[],[222],{"type":14,"value":223},"--user",{"type":9,"tag":49,"props":225,"children":226},{},[227,235,240],{"type":9,"tag":76,"props":228,"children":229},{},[230],{"type":9,"tag":17,"props":231,"children":232},{},[233],{"type":14,"value":234},"Cgroup",{"type":9,"tag":76,"props":236,"children":237},{},[238],{"type":14,"value":239},"the cgroup hierarchy the process can see",{"type":9,"tag":76,"props":241,"children":242},{},[243],{"type":9,"tag":93,"props":244,"children":246},{"className":245},[],[247],{"type":14,"value":248},"--cgroup",{"type":9,"tag":10,"props":250,"children":251},{},[252,254,260],{"type":14,"value":253},"They are independent. A process can be in a new PID namespace while sharing the host's network — which is exactly what ",{"type":9,"tag":93,"props":255,"children":257},{"className":256},[],[258],{"type":14,"value":259},"docker run --network host",{"type":14,"value":261}," does, and what every pod in Kubernetes does with the containers that share it.",{"data":263,"body":264},{},{"type":6,"children":265},[266,272],{"type":9,"tag":34,"props":267,"children":269},{"id":268},"seeing-them",[270],{"type":14,"value":271},"Seeing them",{"type":9,"tag":10,"props":273,"children":274},{},[275,277,283],{"type":14,"value":276},"Every process exposes its namespaces as symlinks in ",{"type":9,"tag":93,"props":278,"children":280},{"className":279},[],[281],{"type":14,"value":282},"/proc",{"type":14,"value":284},":",{"data":286,"body":287},{},{"type":6,"children":288},[289,294,315],{"type":9,"tag":290,"props":291,"children":293},"terminal-teaser",{":lines":292},"[{\"cmd\":\"ls -l /proc/self/ns/\",\"out\":\"lrwxrwxrwx 1 you you 0 ipc -> 'ipc:[4026531839]'\\nlrwxrwxrwx 1 you you 0 mnt -> 'mnt:[4026531841]'\\nlrwxrwxrwx 1 you you 0 net -> 'net:[4026531992]'\\nlrwxrwxrwx 1 you you 0 pid -> 'pid:[4026531836]'\\nlrwxrwxrwx 1 you you 0 uts -> 'uts:[4026531838]'\"},{\"cmd\":\"lsns -t pid\",\"out\":\"NS         TYPE NPROCS PID USER COMMAND\\n4026531836 pid     241   1 root /sbin/init\"}]",[],{"type":9,"tag":10,"props":295,"children":296},{},[297,299,305,307,313],{"type":14,"value":298},"Those numbers are inode numbers, and they are how you answer \"are these two processes in the same namespace?\" — compare the inodes. Two processes with the same ",{"type":9,"tag":93,"props":300,"children":302},{"className":301},[],[303],{"type":14,"value":304},"net:[...]",{"type":14,"value":306}," number share a network stack; two with different ",{"type":9,"tag":93,"props":308,"children":310},{"className":309},[],[311],{"type":14,"value":312},"pid:[...]",{"type":14,"value":314}," numbers cannot see each other's processes.",{"type":9,"tag":10,"props":316,"children":317},{},[318,320,326,328,334],{"type":14,"value":319},"This is also the diagnostic that settles arguments. Comparing ",{"type":9,"tag":93,"props":321,"children":323},{"className":322},[],[324],{"type":14,"value":325},"/proc/PID/ns/net",{"type":14,"value":327}," between a container process and the host tells you definitively whether ",{"type":9,"tag":93,"props":329,"children":331},{"className":330},[],[332],{"type":14,"value":333},"--network host",{"type":14,"value":335}," is in effect, regardless of what the deployment manifest claims.",{"data":337,"body":338},{},{"type":6,"children":339},[340],{"type":9,"tag":341,"props":342,"children":347},"quiz",{":answer":343,":options":344,"explanation":345,"question":346},"0","[\"They share a network stack but cannot see each other in `ps`\",\"They are the same process seen twice\",\"They are in the same container\"]","Namespaces are per-type and independent. Same net namespace means the same interfaces, routes, and port space — one can reach the other on localhost. Different PID namespaces means neither appears in the other's process table. This combination is exactly a Kubernetes pod with two containers.","Two processes have identical inode numbers for `/proc/PID/ns/net` but different ones for `/proc/PID/ns/pid`. What is true of them?",[],{"data":349,"body":350},{},{"type":6,"children":351},[352,364,374,386,391,400,405,410],{"type":9,"tag":34,"props":353,"children":355},{"id":354},"unshare-creates-them",[356,362],{"type":9,"tag":93,"props":357,"children":359},{"className":358},[],[360],{"type":14,"value":361},"unshare",{"type":14,"value":363}," creates them",{"type":9,"tag":10,"props":365,"children":366},{},[367,372],{"type":9,"tag":93,"props":368,"children":370},{"className":369},[],[371],{"type":14,"value":361},{"type":14,"value":373}," runs a program with new namespaces of the kinds you name. The whole mechanism, in one command:",{"type":9,"tag":375,"props":376,"children":380},"pre",{"className":377,"code":379,"language":14},[378],"language-text","sudo unshare --fork --pid --mount-proc /bin/sh\n",[381],{"type":9,"tag":93,"props":382,"children":384},{"__ignoreMap":383},"",[385],{"type":14,"value":379},{"type":9,"tag":10,"props":387,"children":388},{},[389],{"type":14,"value":390},"Inside that shell:",{"type":9,"tag":375,"props":392,"children":395},{"className":393,"code":394,"language":14},[378],"# ps -e\n  PID TTY          TIME CMD\n    1 pts/0    00:00:00 sh\n    5 pts/0    00:00:00 ps\n",[396],{"type":9,"tag":93,"props":397,"children":398},{"__ignoreMap":383},[399],{"type":14,"value":394},{"type":9,"tag":10,"props":401,"children":402},{},[403],{"type":14,"value":404},"Two processes on a machine running hundreds. The shell is PID 1.",{"type":9,"tag":10,"props":406,"children":407},{},[408],{"type":14,"value":409},"Three parts of that command are load-bearing:",{"type":9,"tag":411,"props":412,"children":413},"ul",{},[414,428,471],{"type":9,"tag":415,"props":416,"children":417},"li",{},[418,426],{"type":9,"tag":17,"props":419,"children":420},{},[421],{"type":9,"tag":93,"props":422,"children":424},{"className":423},[],[425],{"type":14,"value":98},{"type":14,"value":427}," asks for a new PID namespace.",{"type":9,"tag":415,"props":429,"children":430},{},[431,440,442,447,449,455,457,462,464,469],{"type":9,"tag":17,"props":432,"children":433},{},[434],{"type":9,"tag":93,"props":435,"children":437},{"className":436},[],[438],{"type":14,"value":439},"--fork",{"type":14,"value":441}," is required with it. The process that calls ",{"type":9,"tag":93,"props":443,"children":445},{"className":444},[],[446],{"type":14,"value":361},{"type":14,"value":448}," does ",{"type":9,"tag":450,"props":451,"children":452},"em",{},[453],{"type":14,"value":454},"not",{"type":14,"value":456}," move into the new PID namespace — only its children do, because a process's PID cannot change while it is running. ",{"type":9,"tag":93,"props":458,"children":460},{"className":459},[],[461],{"type":14,"value":439},{"type":14,"value":463}," makes ",{"type":9,"tag":93,"props":465,"children":467},{"className":466},[],[468],{"type":14,"value":361},{"type":14,"value":470}," fork, and the child becomes PID 1.",{"type":9,"tag":415,"props":472,"children":473},{},[474,483,485,490,492,498,500,505,507,512,514,519],{"type":9,"tag":17,"props":475,"children":476},{},[477],{"type":9,"tag":93,"props":478,"children":480},{"className":479},[],[481],{"type":14,"value":482},"--mount-proc",{"type":14,"value":484}," remounts ",{"type":9,"tag":93,"props":486,"children":488},{"className":487},[],[489],{"type":14,"value":282},{"type":14,"value":491}," inside a new mount namespace. Without it, ",{"type":9,"tag":93,"props":493,"children":495},{"className":494},[],[496],{"type":14,"value":497},"ps",{"type":14,"value":499}," reads the host's ",{"type":9,"tag":93,"props":501,"children":503},{"className":502},[],[504],{"type":14,"value":282},{"type":14,"value":506}," and lists every process on the machine, even though the isolation is genuinely in place. The isolation is real; the ",{"type":9,"tag":450,"props":508,"children":509},{},[510],{"type":14,"value":511},"view",{"type":14,"value":513}," comes from ",{"type":9,"tag":93,"props":515,"children":517},{"className":516},[],[518],{"type":14,"value":282},{"type":14,"value":520},", which is a filesystem, which is why a PID namespace is nearly useless without a mount namespace to go with it.",{"data":522,"body":523},{},{"type":6,"children":524},[525],{"type":9,"tag":341,"props":526,"children":530},{":answer":343,":options":527,"explanation":528,"question":529},"[\"Both problems at once — the shell never entered the new namespace, and `/proc` was never remounted\",\"Nothing — PID namespaces only take effect after a reboot\",\"`--pid` requires root, and the command silently ignored it\"]","Without `--fork`, `unshare` execs the shell in the *old* PID namespace; the new one is created and immediately empty. And even with `--fork`, `ps` reads `/proc`, which without `--mount-proc` is still the host's. Both flags are needed for the demonstration to show anything.","You run `unshare --pid /bin/sh` without `--fork`, and `ps` still shows every process. What went wrong?",[],{"data":532,"body":533},{},{"type":6,"children":534},[535,541,546,564],{"type":9,"tag":34,"props":536,"children":538},{"id":537},"pid-1-is-a-real-job",[539],{"type":14,"value":540},"PID 1 is a real job",{"type":9,"tag":10,"props":542,"children":543},{},[544],{"type":14,"value":545},"Being PID 1 is not just a small number. The kernel gives it two special duties, and containers inherit both problems.",{"type":9,"tag":10,"props":547,"children":548},{},[549,554,556,562],{"type":9,"tag":17,"props":550,"children":551},{},[552],{"type":14,"value":553},"It reaps orphans.",{"type":14,"value":555}," When a process's parent dies, the orphan is re-parented to PID 1, which is expected to ",{"type":9,"tag":93,"props":557,"children":559},{"className":558},[],[560],{"type":14,"value":561},"wait()",{"type":14,"value":563}," on it. A PID 1 that doesn't becomes a zombie factory — which is why long-running containers whose entrypoint is a plain application sometimes accumulate defunct processes.",{"type":9,"tag":10,"props":565,"children":566},{},[567,572,574,580,582,588,590,596,598,604],{"type":9,"tag":17,"props":568,"children":569},{},[570],{"type":14,"value":571},"It ignores signals it has no handler for.",{"type":14,"value":573}," The default action for ",{"type":9,"tag":93,"props":575,"children":577},{"className":576},[],[578],{"type":14,"value":579},"SIGTERM",{"type":14,"value":581}," is \"terminate\", but the kernel suppresses that for PID 1. A shell script as PID 1 that installs no ",{"type":9,"tag":93,"props":583,"children":585},{"className":584},[],[586],{"type":14,"value":587},"trap",{"type":14,"value":589}," will not die on ",{"type":9,"tag":93,"props":591,"children":593},{"className":592},[],[594],{"type":14,"value":595},"docker stop",{"type":14,"value":597}," — it waits out the grace period and gets ",{"type":9,"tag":93,"props":599,"children":601},{"className":600},[],[602],{"type":14,"value":603},"SIGKILL",{"type":14,"value":605},"ed instead. That is the usual explanation for a container that always takes ten seconds to stop.",{"data":607,"body":608},{},{"type":6,"children":609},[610],{"type":9,"tag":611,"props":612,"children":614},"deep-dive",{"title":613},"User namespaces, and rootless containers",[615,626,639,648,653],{"type":9,"tag":10,"props":616,"children":617},{},[618,620,624],{"type":14,"value":619},"The user namespace is the one that changes the security story, and the one we do ",{"type":9,"tag":450,"props":621,"children":622},{},[623],{"type":14,"value":454},{"type":14,"value":625}," use in this course.",{"type":9,"tag":10,"props":627,"children":628},{},[629,631,637],{"type":14,"value":630},"It maps UIDs across the boundary: UID 0 inside can be UID 100000 outside. The process believes it is root — it can ",{"type":9,"tag":93,"props":632,"children":634},{"className":633},[],[635],{"type":14,"value":636},"chown",{"type":14,"value":638},", install packages, bind port 80 — while the kernel treats every one of its actions as coming from an unprivileged user.",{"type":9,"tag":375,"props":640,"children":643},{"className":641,"code":642,"language":14},[378],"unshare --user --map-root-user /bin/sh\n# id\nuid=0(root) gid=0(root)\n# touch /etc/anything\ntouch: cannot touch '/etc/anything': Permission denied\n",[644],{"type":9,"tag":93,"props":645,"children":646},{"__ignoreMap":383},[647],{"type":14,"value":642},{"type":9,"tag":10,"props":649,"children":650},{},[651],{"type":14,"value":652},"Root, and powerless. This is the foundation of rootless Podman and of Docker's userns-remap mode, and it is the single biggest mitigation for container escape: a container breakout without a user namespace lands you as real root on the host, and with one it lands you as nobody.",{"type":9,"tag":10,"props":654,"children":655},{},[656,658,663],{"type":14,"value":657},"We build without it because it complicates every subsequent step — the filesystem needs UID mapping, the network setup needs privileges the namespace has just taken away — and the goal here is to see the mechanisms clearly. ",{"type":9,"tag":17,"props":659,"children":660},{},[661],{"type":14,"value":662},"A container built the way this course builds it is an isolation boundary, not a security boundary.",{"type":14,"value":664}," Worth being precise about that, because it is exactly the distinction that gets lost in production.",{"data":666,"body":667},{},{"type":6,"children":668},[669,681,693,702],{"type":9,"tag":34,"props":670,"children":672},{"id":671},"nsenter-joins-an-existing-one",[673,679],{"type":9,"tag":93,"props":674,"children":676},{"className":675},[],[677],{"type":14,"value":678},"nsenter",{"type":14,"value":680}," joins an existing one",{"type":9,"tag":10,"props":682,"children":683},{},[684,686,691],{"type":14,"value":685},"The counterpart to ",{"type":9,"tag":93,"props":687,"children":689},{"className":688},[],[690],{"type":14,"value":361},{"type":14,"value":692},": enter namespaces that already exist, given a PID that is in them.",{"type":9,"tag":375,"props":694,"children":697},{"className":695,"code":696,"language":14},[378],"sudo nsenter -t 30412 -p -m -u -n /bin/sh\n",[698],{"type":9,"tag":93,"props":699,"children":700},{"__ignoreMap":383},[701],{"type":14,"value":696},{"type":9,"tag":10,"props":703,"children":704},{},[705,707,713,715,720,722,727],{"type":14,"value":706},"That is essentially what ",{"type":9,"tag":93,"props":708,"children":710},{"className":709},[],[711],{"type":14,"value":712},"docker exec",{"type":14,"value":714}," is. It finds the container's PID 1 on the host, joins the same namespaces, and runs your command there. It is also the tool that gets you into a container whose image has no shell — ",{"type":9,"tag":93,"props":716,"children":718},{"className":717},[],[719],{"type":14,"value":678},{"type":14,"value":721}," runs a binary from the ",{"type":9,"tag":450,"props":723,"children":724},{},[725],{"type":14,"value":726},"host's",{"type":14,"value":728}," filesystem inside the container's other namespaces, so a distroless container with a broken network is still debuggable.",{"data":730,"body":731},{},{"type":6,"children":732},[733,741],{"type":9,"tag":734,"props":735,"children":740},"fill-blank",{":answer":736,"hint":737,"placeholder":738,"prompt":739},"[\"unshare --fork --pid --mount-proc /bin/sh\",\"unshare --pid --fork --mount-proc /bin/sh\",\"unshare --mount-proc --fork --pid /bin/sh\"]","Three flags — one for the namespace, one so a child actually enters it, one for the process view.","unshare ...","Start `/bin/sh` in a new PID namespace with `/proc` remounted, so `ps` shows only the namespace's processes. (You are already root.)",[],{"type":9,"tag":10,"props":742,"children":743},{},[744,746,752,754,760],{"type":14,"value":745},"Next up: the root filesystem — ",{"type":9,"tag":93,"props":747,"children":749},{"className":748},[],[750],{"type":14,"value":751},"chroot",{"type":14,"value":753},", a real Alpine userland, and what \"its own ",{"type":9,"tag":93,"props":755,"children":757},{"className":756},[],[758],{"type":14,"value":759},"/",{"type":14,"value":761},"\" actually means.",1787908868353]