 Goedemorgen en welkom op deze sessie, containers, een look under de hoed. Mijn naam is Gelof Langeveld. Ik ben employeerd door de Duits-companie 80 Computing, waar ik een trainer ben in mijn dagelijks werk. Geen coursen over Linux, geen coursen over Docker, Kubernetes en dat soort coursen en trainings. Voor deze sessie hebben we een interactieve sessie op een bepaalde moment. We gaan een container maken, meteen een container-implementatie, een van de bepaalde implementaties. En als je wilt joinen, kun je joinen door de download van dat tarffile. Dus als je het niet doet, kun je het doen. Er is een kleine environment waar we een container maken gaan maken. En er zijn ook een paar toolen in erin die kunnen bepaald worden tijdens deze sessie, maar ook misschien later op. Als we kijken naar de konventionale Linux-approach, als we kijken naar de procesen daar, dan kunnen we zien dat alle procesen die in één systeem, in één hosten, in één ecosysteem ook. Ze hebben dezelfde environment. Ze hebben een onbedrijfde notie van wat de hostname van dit systeem is. Ze hebben één pitnummering scheme dat ze allemaal gebruiken, alle procesen op het systeem. Alle procesen op het systeem hebben dezelfde gevoel van het file systeem en alle hoge physical file systeem in de logische file systeem 3. Ze hebben dezelfde notie over het netwerkstak, wat de open porten zijn, wat de interfaces zijn gebruikt. Ze hebben dezelfde notie over interproces communicatie, objecten zoals shared memory, message queues en semaphores. En ze hebben ook dezelfde notie over usernames en user id's, de mapping van usernames op user id's. Dat is de klassieke approach, de konventionale approach. Wat was al daar, sinds een paar decades, was de fact dat alle procesen hun eigen idee van wat mijn root directie is. Dat was niet necessarily de root directie van het hele file systeem, maar alle procesen kunnen hun eigen root directie ergens in het file systeem en alleen kunnen gebruiken van dat de file systeem als dat proces, het file systeem. In de konventionale approach zien we ook dat de root identiteit betekent dat je alle privilijst acties kunt doen, de file systeem, de systeem bezoeken, de hardwerkklokken, dat soort dingen, en procesen met een non-root identiteit hebben geen privilijst, een mogelijkheid voor geen privilijst acties. En in de konventionale approach zien we dat het hartelijk mogelijk is om de reisorgse consumptie, er was hartelijk geen limiter om de maxime op de CPU utilisatie dat je hebt, of je kan geen minimale CPU-utilisatie kunnen garantieren of memorie-utilisatie. Dat was allemaal niet mogelijk in de konventionale approach. Als we kijken naar de containeriseerde approach, kunnen we zien dat procesen in de container worden isolaties van de andere procesen op de host, dat betekent dat ze een eigen middenfile systeem in de container zijn. Dat is wat je ziet in deze foto. Dit is een host systeem met de host file systeem en op dat kunnen we een container doen. In dat container hebben we onze eigen middenfile systeem, de container heeft zijn eigen idee van wat mijn hostname is binnen de container, apart van de host op die het runt. Het heeft zijn eigen private PID nummering, proces ID's, het heeft zijn eigen private network stack met zijn eigen open port en zijn eigen interfaces. Het heeft, het zou limitere privilijst zijn, zelfs als een proces in de container is runnen als onder de route identiteit, het betekent dat het niets kan doen of alles, ik moet zeggen. En je kunt, als je met containers werkt, maar dat is ook mogelijk, buiten containers, je kunt ook limit of garantieën een certaine resource consumptie, CPU consumptie, memory consumptie, bijvoorbeeld. Hoe werkt dat? Dat proces in de container heeft zijn eigen uitzicht van wat mijn environment is, wat mijn hostname is, wat mijn file systeem is en dat soort dingen. Als ik vaak classes gegeven heb over containers, zie ik dat mensen een idee hebben over de container, dat wanneer je de container begint, dat er wat magische bochten is gecreëerd en dat bochten is beperkt met een minifile systeem en een private netwerk en een pithnummergenerator, specifiek voor deze bochten en in de eind, de proces zal in de bochten loaden en de proces zal bekeken en het kan het werk doen. En zoals het op de slide staat, dit is de onrealistische uitzicht van wat een container is, dat is niet hoe het werkt. Dus wat is een meer realistische uitzicht? Als we kijken naar een existente proces, een proces is connecteerd, is gebouwd, naar naamspaces en de naamspaces bevatten wat het environment van het proces is, wat het systeem is op het file systeem, hoe het pithnummerering is en dat soort dingen. Dat is bevatten door naamspaces en we kunnen ook zien dat een proces is connecteerd met c-groepen, controlgroepen dat kan gebruikt zijn om de cpu-utilisatie te maximiseren of te garantieven een certaine minimie van cpu-utilisatie en een proces heeft ook een idee over wat mijn route directie is en wat je ziet in een normaal host zonder gebruik van containers is dat als deze proces een nieuw proces creëert, waarin we een cijlproces hebben, dat de cijlproces verhalen alle verbindingen tot de naamspaces en de controlgroepen, het ook de idee van wat mijn route directie in het file systeem is, is ook geïnherenteerd. Maar als we de containeriseerde proces gaan starten, dan kunnen we zien, we hebben nog een existente proces, het is connecteerd met alle naamspaces en het heeft een idee over een route directie en controlgroepen, maar de cijlproces, die is geïnherenteerd, dat heeft zijn eigen naamspaces en via deze naamspaces, de cijlp heeft zijn eigen idee over wat mijn host naam is en wat mijn file systeem is en wat mijn pitnumbers zijn en dat soort dingen. Ja, dus het heeft ook zijn eigen idee over wat mijn route directie is en zijn eigen connectie met de controlgroepen en in deze pictorie je kunt zien dat alle dingen zijn geïnherenteerd, dat is niet necessaire wat de cijlproces is. Het zou zijn dat de cijlproces nog steeds een aantal naamspaces met de parenten en andere naamspaces niet te zijn geïnherenteerd. Dus als we kijken naar deze naamspaces waarin de proces is connecteerd, dan kunnen we een naamspace ontdekken dat de host naam is voor het proces en dat is wat we de UTS konden, naamspaces. Er is een naamspace die bevindt wat de in de proces communicatie objecten zijn voor dit proces MSSQ's en dat soort dingen, IPC naamspaces, er is een pitnumbers naamspaces, een network naamspaces, een mount naamspaces dat bevindt hoe de file systeem eruit ziet. Er zijn usernaamspaces en dat bevindt de environment van het proces. Verdermaar, een proces kan een private minifile systeem hebben. Dat is gedaan door technieken zoals veranderd en verwijderd. We zien meer over dat later op. En een proces kan het eigen distincte privilege hebben, die is bevindt door de kernalmeganisme capabilities. Verdermaar, zoals we hebben gezien in de foto, procesen zijn connecteerd met c-groepen, controlgroepen, en er zijn specifieke c-groepen die kunnen worden gebruikt om een certaine resource consumptie te garanteren of een certaine resource consumptie te maximiseren. Zoals je ziet, zijn er certaine dingen uitgegraven hier. De dingen die uitgegraven zijn, ik ga er niet over. Ik hoop dat we genoeg tijd hebben om de andere dingen te handelen die hier niet uitgegraven zijn. Ja, dus zoals ik zei in het begin, de goede van deze presentatie is dat we gaan creëren een containerise proces zonder het gebruiken van docker of potman of wat meer, implementatie van dat. Just by using these namespaces and private file systems ourselves. Bevor we gaan kijken naar de namespaces in detail, een paar woorden over het Proc file system dat we dit morning ook nodig hebben. Proberlijk kun je al weten dat er een soort file system is called Proc, die is genoeg op de slagproc directie. Aangezien, vinden je een paar pseudo files, die niet echt file zijn, maar via deze files kun je de kernel administratie kijken, kernel data. En er zijn systemlevel kernel data, zoals de pseudo file Proc dat je kunt gebruiken. Maar er is ook data op proces niveau die je kunt vinden onder onder de slagproc directie. En voor elke runnig proces op dit moment vind je een subdirectie onder de slagproc en dat subdirectie heeft de PID van het runnig proces als naam. Dus wat je ziet in deze example is, als ik mijn proces hier heb, mijn schel is runnen met deze PID en dan kan ik een loop onder de slagproc op die PID als subdirectie, waar ik de condens van alle informatie van dit proces kan vinden. Ja, je kan het zien daar en ik kan het ook zien op het systeem. Ik kan natuurlijk ook gebruiken als de PID van mijn schel dollar dollar. Als ik naar die directie ga, kan je zien dat er veel files en subdirecties zijn, zelfs, en dat ze allemaal informatie hebben over deze proces. Een van de meest famose files dat houdt veel informatie is de file stop. Maar voor deze moet je natuurlijk een beschrijving. Wat betekent dat? Alle deze figuren. Je kunt een beetje over de begin guessen dat je de naam en de state van het proces kan vinden en dat soort dingen. Maar voor de rest moet je de manpage van Proc zien wat alle andere dingen in daar zijn. Er is een pseudo file hier, wat meer menselijke kan zien. En dat is de status file. En ik gebruik dat later in mijn sessie ook. En daar kun je meer menselijke data zien over wat de naam van het proces is en de Umask en de PID en dat soort dingen. Ik kom terug naar dat later op. Dus, laten we eens kijken naar de naamspaces. Hoe kunnen we ze gebruiken en hoe zijn ze geëverd? Dus, naamspaces allow processes to a shared dedicated collection of resources like host names and process IDs, I mentioned them already. En every type of naam space has its own behavior. You can see that PID namespaces behave different than UTS namespaces and so on. And we will have a look at them one by one and see how they work. In general, if you want to create new namespaces as a process, then you need the Capsis admin capability. Well, for now in other words, you have to be root to be able to create more namespaces, except for a user namespace. There you don't need to be a root. And this really allows separation of groups of processes without allocating VMs. Yeah, it's the basis of making containers. So if we have a look, then we can see that the first process, process one, which is usually system D, that the first process connects to namespaces, like the mount namespace and the PID namespace and the network namespace and the IPC namespace and the UTS namespace for the hostname. And it will also be filled and initialized by system D, these namespaces. So at the moment that system D starts creating new processes, child processes, then these child processes will inherit the connection to those namespaces. And by that such a child process has the same view on the file system and the same is in the same PID nummering scheme and has the same hostname as system D. And if that child process creates by its own grandchild processes and even more grandchild processes, they all inherit the binding with the same namespaces, having the same view on the system as system D has. But what you can do as a process is that you at a certain moment say, I don't want my connection anymore with the UTS namespace, for instance. I want to have my own UTS namespace and there I will fill my own hostname. And that is my hostname from this moment on. While the other processes are still working with the initial UTS namespace and have their own opinion about what is the hostname. And similar things can be done for the mount namespace and the IPC namespace and so on. So how do I know if certain processes in my systems are connected to the name namespace and have the same ID about what is the file system and my hostname and so on. Well, if you look underneath the slashprocfile system in the proc directory of a certain process, there you can see a sub directory which is called NS namespaces. And in that sub directory, you will see simlinks, at least, this is how they are represented as simlinks in this pseudo file system. And there you can see the names of those namespaces with a reference to a specific magic inode number. And this inode number doesn't have any meaning as such and you can't use it for special purposes or so. But they just identify a certain namespace to which this process is connected. And if you see that other processes in their proc, their PID, NS, have the same numbers here, you know that they are connected to the same namespace. And they have the same view about IPC objects and mount namespace and so on. So if I have a look specifically at my own shell here and at the UTS namespace, I can see this number. And if I have a look at process one, the namespace UTS, then I can see it's the same number. So they have the same ID about what is in this case the hostname. But it might be different. So shell possesses, as I said before, inherit the association with a namespace from their parent. And a namespace vanishes again, disappears again at the moment that all the processes that were bound to it are unbound. Processes are gone, for instance, and exited. So let's have a look at a small tool that I created. Before I show the tool, which is also in the downloaded tar-i-guide, by the way. I also want for demonstration purposes to start a containerized process via Docker. Just a small process. Let's do a sleep here. Yeah, then we can also see it in the output of our tools. Well, there is a program that we can run and we have to run it as root because the NS subdirectory in the process directory is not allowed to be watched by anybody. You can normally only see your own processes running under your identity. But here I can run the process as root and do an NS show minus a to see all the processes. And what I get here, and I'm not going to go through all the output with which you can see here at the end, is that it shows here the UTS namespace of all the processes. You can see the process ID and the process name. And you can see the number of the UTS namespace. And by comparing the numbers, you can say, okay, it looks as if they are all connected to the same namespace, these processes. And you get in front of the list also the other namespaces. But what I can do is that I run the same program without the minus a that shows all the namespaces en all the processes. And then I can only see the processes that deviate from system D. So now I can see the processes that deviate according to their mount namespace and their network namespace and their pit namespace and so on. And I can see that the process that I just started via Docker, the containerized sleep process, is connected to all kinds of different namespaces compared to the namespaces to which system D is connected. Ja, so they have their own opinion about how the system looks like. If I scroll a little bit here, you can also see that this mechanism is also used outside the container processes. So there are also other processes that want just to have their own view on what is my file system mount namespace of what are my IPC objects and so on. I am missing one that I expected here, but never mind. So that's the NSO and a show tool that helps you finding out about these namespaces. So how can we manipulate with these namespaces? Apart from starting containers via Docker or Portman or whatever. There are standard commands installed on your system that manipulate with these namespaces and one of them is the Unshare command. And the Unshare command Unshare is a particular namespace. So you start the Unshare command from your shell. It inherits the namespaces from your shell process. So it works as the same namespaces and then it can decouple of one of the namespaces and get a namespace by its own, the Unshare process. With the Unshare command, you can also give a command that you really want to run in this process. And what the Unshare does is first disconnect from a namespace and get a new namespace. And you can also with the flags here control which namespace, U for UTS, I for IPC and so on. And then it execs the process that you specify, the command that you specify, it executes that in the current process. So this is not a fork. It doesn't create a shell possess by its own Unshare to run the new command. But the new command is run inside the same process as Unshare. Similar to commands like NICE and NOHEP and that kind of command. It works similarly. So after that, this command will really run with another namespace or a couple of other namespaces in one go. Another command that we can use, which is standardly available, is the NSEnter command. And the NSEnter command, there we can say connect to a namespace of an already running process. So you don't create a new namespace but you already connect to a namespace which is already there from another process. And here again, you specify what namespace with the flags and you specify the process to which namespace you want to connect. And then again, you specify a command that should run like that. So here we have the other process that I specify with the PID. And here I have NSEnter that connects, this connects from its own namespace and connect to the namespace of that process. And then executes the command in this process that you specify. Let's have a look at an example of that. So we have seen a couple of processes here which have their own namespaces. Let's take this one, Gronai, Grona ID with PID 751. It seems to have its own ID about what is the mount namespace. How does the file system look like? So if I first have a look at the file system that I work with, and this is the mount namespace that I inherited via a lot of processes from SystemD. What I can do now is sudo NSEnter. I want to connect to the mount namespace of the process with PID 751 and I want to run a shell as a command. And in the shell that is now connected to that other namespace of Grona ID, I can do a DF again and I can see that I indeed have another view of the file system. That's not identical. By the way, if you think about Docker or Potman or that kind of implementations, this is of course what the Docker exec command does. That you say I have a running container and I want to start a new command in there in the same environment. Well, then in fact under the hood NSEnter is done and you connect to all the namespaces of that process running in the container and by that you are part of the container. You are part of the same environment. Let's have a look at the specific behaviors of the different namespaces. First of all, let's have a look at the UTS namespace for hostname isolation. If we have a look at this example, Alice-L slash proc, my current shell NSUTS, then I can see that this process is connected to this namespace with this number. And if I have a look at the hostname, I can see it's called myhost. And let's assume this is just a shell that inherits and forked off Firefire from SystemD. Well, what I can do now is that I can do a sudo, Unshare. Unshare the UTS namespace and then load the best program in the process. And then I can see that this is the prompt of my new best program here. And if I do an Alice-L of this bash, dollar dollar is now another shell, UTS, I can see it has a different UTS namespace number now. And here in this new namespace, I can set the hostname. It inherits the same hostname, but I can overrule it now by the hostname command en I can call it from now on otherhost. And if I would do the hostname command now without parameters, I would see, would see, well, the hostname is otherhost now. I can also start another shell again, which is a child process that inherits my namespaces. And then in that child process, I can again see already in the prompt that now it shows its own hostname in the prompt which is otherhost. And I can exit from that third shell process en go back to the second again that I started here and I can exit from there. And then I'm back at the same situation here and I can still see that process works with my host. It's still my host and not otherhost. So this is a rather simple namespace, just holding a hostname. Well, let's have a look what we can do now to make a first step in building our own container. My plan is here to build a new container step-by-step. And for every step, I also create a new shell script that calls another shell script again. We get a couple of shells in this way that are all the time child processes of each other. But that's just to show how it works. So what we can do is that we can go and if you want to join, you can go to the directory cddkit if you have that tar file and if you have impacted tar file. And in there, you find the sub directory myconf. And here we are going to create all the steps to start a new container. And the first step that we do, let's call it the script step one. And let's also, well, make it a nice script. The first thing that we do is that we are going to do the command unshare minus u. And then we are going to start a new script again. Let's call the command bash to execute script step two. This is all that we want to do in script step one. Unshare the utl. Unshare the utl namespace en then let a new process execute step two. And of course that step two script has to be created as well. So let's also create step two. And for now, we can first do the command bash in here. Let's just start an interactive shell. So what I can do now is that I once make the script executable step one only because step two was created or executed explicitly with the bash command. And then I can do sudo start step one. Remember that we have to be rude to create a new namespace. Well, then you can see the prompt of the new shell. And you can see in the prompt that it still has the same name, which is mykond, which was also... Sorry, which is center was 9s, which was also the name of my former namespace. So it has got a copy of the utl namespace with the same name. But I can change that copy. And I can do it by Ctrl D going out of this again. By changing the script step two. And before we start the bash, I'm going to change the hostname. And let's call it mycontainer. And then execute the interactive bash. So again, I can start step one. And you can already see in the prompt of the new shell that it's called mycontainer now. So this new shell has its new environment according to the hostname. Ok, that's the first step in creating our container. Another namespace that we can use is the IPC namespace. I don't want to say too much about that. I have a small example here. But that's also used in most of the container implementations that the processes running in the container get their own view on interprocess communication objects. Well, you might know that with the command ipcs. You can ask for all the interprocess communication objects. You can see the list of message queues and shared memory segments and semaphore arrays. And also here is valid if you do a sudo and share minus i for IPC namespace and create a new shell in this case. I get the prompt of my new shell. And if I do the command ipcs there, you can see there are no IPC objects at all. You have a complete blank environment. So no copy in this case. It behaves different from UTS. You get a blank environment according to interprocess communication. Of course via standard command like ipcmake. I can make a shared memory segment of 4K in this case. And then I can do an IPC yes again. En I can see that there is now one shared memory segment available for the processes that are in this name series or connected to this namespace. Just as a brief example of what you can do with IPC namespaces. Let's have a look at pit namespaces. A pit namespace determines the pitnummering. For a pitnummering scheme for a set of processes. If you think about containers, every container has its own pit namespace en its own pitnummering scheme. Starting by one again. Pit namespaces are nested. As you can see in this example. Here we see the pit namespace which is connected to systemd. Systemd itself has process ID 1. And the direct children have 2 and 5 and 4 as their PIDs and so on. At a certain moment we will get a process that has the PID 17. In the root pit namespace as we call the outer namespace. That process 17 might still fork off a new process and gets PID 18 in the root namespace. But after that process 17 might do an unshare and connect to a new pit namespace. And that's the red pit namespace. And if it creates a new process, a child process afterwards. That child process gets a unique pitnummer in the root pit namespace. But it also gets a PID in the new namespace. So this is a process in fact running with two PIDs in two different namespaces. And if that process forks there its own children again. You can see that every child gets a pitnummer in the root namespace again. But also a free pitnummer in the new child pit namespace. In this case. So you can even create a new namespace underneath so that every process gets three pitnumbers in three different pit namespaces. I think you can go to 32 levels if you really want. If you see a use of that. So we can also visualize this. Remember that I started the process sleep earlier. That was started inside the container. That's this pitnummer. If I have a look here. Let's slash proc slash. And then that pitnummer slash status. Then I'm looking at that status pseudo file. And what you can find here. Is that this process NSPID has a PID number in the root namespace. Which you can also see its PID number in the pit namespace of the container. So that can be a row of pitnumbers that you get in the order of the hierarchical order of the pit namespaces. If you would look inside the container. So only in that sub namespace. Then you only see this process runs with PID1. Beware that a process that runs with PID1 in a namespace in any PID namespace. That it has the same tasks as system D for the root namespace. That also means that if one of the child processors terminates and is orphaned, well not orphaned, no it terminates. Then a process one in that PID namespace has to get rid of that process not to get zombies. So all that kind of tasks is also valid if you start a process in a container. That first process that you really start has certain tasks not to get zombie processes and that kind of things. And such a process that runs with PID1 in a namespace can also not be killed except via the signal and the signal stop signal. So you cannot get rid of it by control C which sends signal 2 or by kill minus 15. That doesn't work. Such a process really has to be killed only by signal 9. So let's have a look at an example of creating a new PID namespace. What I can do is an unchair minus P to get a PID namespace. But be aware if a process disconnects from the current PID namespace the parent process itself cannot do that. Ja, because then that process would suddenly get another PID in a new PID namespace. And what you see is that if you unchair from the PID namespace then the child processes will be in that new PID namespace but the process itself will not be in the new PID namespace. And that's why you see that here we also use the minus minus fork option. Which means that the command that I specify here which is bash to be run finally. That command will run in a child. And not overrule the unchair executable itself in the unchair process. So also realize that if you have a new PID namespace that the slash proc file system that holds all the PID numbers as subdirectories should suddenly hold other PID numbers of that new namespace. And therefore you also have to give the option minus minus min proc that the proc file system is newly mounted after being connected to the new PID namespace. Otherwise you would still see the PIDs in the outer PID namespace. So finally I started the bash here and I get the prompt of that bash in the new PID namespace. I can do a sleep 300 to wait for five minutes in the background. En if I do a ps command then I can see that bash itself runs with PID 1 in this new PID namespace. I can see that sleep 300 runs with PID 37 in this new PID namespace and ps itself runs with 38. So a total new PID numbering namespace and scheme. If I have a look in slash proc from this shell then I can see that there are subdirectories which are really called 137 and in the meantime 39. This is another command again. On the host system I can still see the same process, the same bash process, but on the host system in the PID numbering space it runs with an entire different PID. And sleep also runs with a different PID in that namespace. So let's adopt our container for that. So far we have had the step one and step two. I leave the step one alone and I'm going to modify this step two script that I created. And instead of running an interactive bash I'm going to run a share minus p minus minus fork minus minus mount dash proc. And I'm going to run a new script by that. Step three that we still have to create of course. And of course it will also inherit the formally created UTS namespace. So we can create a step three script here and start with bash, just bash first interactive. So what I can do again is start from the beginning and execute step one again. And finally the bash will be started, the interactive bash. And you can still see it's running in my container. It still has the inherited UTS namespace. But we can also see it's connected now to the new PID namespace. Because you can see that all the processes are numbered here from one now. Okay, that's the second step that we have made in creating a container. So that's what we find here on this slide, which you can do with your scripts. Let's have a look at the other namespace that we need. And that's the network namespace for network isolation. A network namespace determines what kind of interfaces are in use. It also determines what kind of port numbers are open to which you can connect. It also determines routes and firewall rules and that kind of things that's all related to a network namespace. And we have an initial network namespace that is connected to system D. And we have all kind of interfaces in there, a local Lubek interface and an Ethernet interface and so on. And what we can do now is that from a process we can say, well, I inherit the connection to the initial namespace, network namespace. I want to have a namespace of my own. I want to have my own networking stack created here. And what we can see then is if you get a new namespace is that we get a namespace that only gets a local Lubek interface, which is even in downstate. And if you want to have more interfaces here, we can create them afterwards. You can even assign physical interfaces to such a new namespace, where the physical interface can only be connected or can only be part of one of the namespaces. So if you want to have this physical Ethernet namespace here, then you have to disconnect it from the initial namespace and get it here. But it's possible. You can also interconnect namespaces. We'll see that in the meantime by so-called VET pairs that you can create to get a bridge between the two network namespaces. Well, even some kernel parameters are namespaced. If you think about kernel parameters like proxies, net, IPv4, unprivileged port, start number, which is by default 1024. You know the unprivileged port numbers higher than 1024. You can modify such a parameter and put it on 512 like this. And realize that you do that for the namespace to which your process is connected. And that's a namespaced kernel parameter, which can be different for all the namespaces. So also things like forwarding and that kind of things, IP forward, can be set per network namespace. So suppose that I have my initial situation. I have a shell here that is a direct descendant of system B. And if I do the IP error command, I can see my interfaces. And I'm looking at my initial network namespace at this moment. So what I can do now is that I do sudo and share. I want to have a new network namespace. En I want to run a new bash connected to that namespace. And then we get the prompt of the new bash. And then I can do IP other again connected to that new namespace. And I can only see that there is a local Lubeck interface here, which is in the state down. So what I can do here is that I can do IP linkset device low to the upstate. Make it up. And then I can see it's up now. But if I look at the open ports, are there any open TCP ports, for instance? There are no open ports whatsoever. There are no established ports. This is just an initial network namespace. All the ports that were open so far belong to this initial namespace. So what I can do now is that I can set up a bridge between the two namespaces. And that's what I can do with a so-called VET pair. Again, watch the colors here, the color of the prompt. This is a prompt of a process connected to the initial network namespace. And there I can have a look at that shell that I just started on the previous slide. That shell has this PID. And I should know that. That's a PID of a process connected to this namespace. Let's have that clear. So what I can do now, with the shell connected to the initial namespace, is that I can do the command iplink add name mybridge0 type VET. And that will create the mybridge0 interface here. PName is mybridge1 and connected to the namespace, which is connected to this process. And this is the process ID of my shell that is connected to the other namespace. And there I get my mybridge1 interface. And they will be interconnected. But they still have to be configured. But now the interfaces are there. Excuse me. So if I do an IP other now, being connected to this namespace, I can see the mybridge0 interface. I can initialize it and give it an IP address connected to this namespace. And I can set it to the state up. Now I can go to my shell that is connected to this namespace. And I can do the IP other command there. And I can see, well, I have a new interface here. And I can also give it an IP address. And I can set it to the upstate. And once that has been settled, then I can make an SSH connection or do a ping or whatever. Then from this new namespace, I can reach the other namespace. And if you take care that nothing is configured in this namespace, then you can also reach from this namespace the outside world. And that's how it works with containers as well. The ID. So we can also do this ourselves by getting another step again. Step three, we will modify that into an unshare minus n. And then do a bash of step four. And then in step four, we'll start an interactive bash and see how the world looks like for that process. Unshare minus n. And then run bash with step four. Yeah, that's what I'm modifying step three. That was the former interactive bash. So then we are also going to create a step four script. And there I only start bash for the time being. So I can start step one again to start from the beginning. And then I get my prompt, which is connected also to the new network namespace now. If I do an IPA here, I can see indeed, I didn't initialize anything yet. So I can see indeed, there is only one interface connected, which is the local Lubeck interface, which is in state down still. And for the rest, yeah, if I have a look at the open ports, I can see there are no open ports in this namespace. We are going to do some other steps in step four. And that's why you also downloaded the tar file. There is a skeleton for step four that also does the initialization that we really need to get a real network in our container. Let's have a look here. There is a step four skeleton here. And what I can do is that I simply can copy it over the step four that I have at this moment that only starts the dash process interactively. I should do it here. That's better readable. So let's have a look what happens here in step four now. Of course, we have to set the local Lubeck link up. And after that, I'm going to create that Z pair. But I have to do it from the perspective of the initial network namespace. So that's why I do an NSN-ter minus N. I want to connect to an existing network namespace. And that should be the existing network namespace of process 1 minus T1. But beware, this is not system D because we are already in a new pit namespace. This is in fact the first shell that I started in my pit namespace. But that's still a shell connected to the original initial network namespace. So that's fine. I can connect to that namespace. That's the initial network namespace. And there I can give the command IP link add name the command that we saw on the slide before. And that creates a MyBridge 0 in the initial network namespace. Connected to process 1. And it creates a MyBridge 1 in, well, I give my own PID of my running shell here. Which is in the new connected to the new network namespace. And then the same way I can also initialize the IP address of MyBridge 0 in the initial network namespace and put it up. After that, in my own shell connected to the new namespace, I can also give my half of the vet pair. I can give it an IP address and I can set it to state up. And after that, I start my interactive bash again to see how it looks like. Can you repeat the question, please? Oh, the PID 1 if it is still connected to the initial namespace. Yes, it is. It is still. It's the first process that has been started after creating a new PID namespace. And that creates a new bash again and a new bash again. So if we go back, PID 1 is still connected to the initial network namespace. Not by default, but in this case because I first create a new PID namespace and then a new network namespace. I could have turned the order, of course, that I first created a network namespace. Referent to PID 1 will then be SystemD and then create a new PID namespace. But still in the new PID namespace, the first process is still connected to the initial network namespace. So when I run this again, I can now see in my new container that we have a local Lubeck interface which is up and I can see Mybridge 1 which also has its own IP address. And I can also reach 192, 168.47.11 which is the IP address of Mybridge 0 in the initial network namespace. So a network has been set up now between the two namespaces. Okay, so we have a connected network as well now. Let's have a look at the final namespace that I want to cover and that's the mount namespace. The mount namespace gives you an idea about how does your file system look like, your logical file system structure which is usually built up of various physical file systems that are mounted together. Also here you can say all the processes that are connected to the same namespace and that's also the namespace for instance the mount namespace that system D is connected to. Well, they see a certain view of the file system with all kind of directories and subdirectories spread over different physical file systems that are linked together, that are mounted together. And what we see here is that this was also the conventional approach and you can see this file system structure if you do commands like mount and df and for instance the mount command originally in Unix also always looks at slash etc slash mtap and there from there it can see what are the physical file systems that are in our logical file system tree but you can see nowadays in your system that this is a simlink to slash proc slash self en self is means mypid slash mountinfo and from there I get my information now from df and mount about how my file system looks like and that can be for one process on the host that can be different from another process on the host depending to which mount namespace you are connected can be totally different. If you create a new mount namespace you will inherit the structure of the original mount namespace where you come from but from then onwards you can unmount things file systems and you can mount new file systems in your mount namespace not influencing the original mount namespace of to which system d and a lot of other processes are connected and by that every process can have its own view about what is my file system and how does it look like ja it's even very nice that suppose that you have a lot of processes that have an open file in this file system and it's impossible to unmount it because you get device busy that if you create a new mount namespace that you can unmount it in the new file system namespace even if processes here have open files in that physical file system so that kind of things are possible Let's have a look at an example of that if I look at my current file system with a command like df I can see there are a couple of physical file systems put together at certain mount points if I do an unshare minus m and I start a new shell here I get the prompt of my new shell and what I can simply do is unmount slash data and I can mount another file system from my second disk partition from partition one of my second disk and mount it to slash m and t and if I look at df now for my file system view I can see that it looks like this totally independent of the original mount namespace and if I exit from this environment again and I'm back in the original environment I can see that the file system still looks like these mount points that I originally had that will be unmounted yes yes because I'm the last process that was connected to that mount namespace that also means that things will be unmounted that were mounted there so let's modify the container that we have so far remember that step four was copied and ended with an interactive bash here well we are going to change that line to unshare minus m bash step five and create a step five script step four has to be modified for this and here we can say unshare minus n bash step five and obviously then we have to make a step five script that for this moment we only fill with an interactive bash so again let's start step one en well we almost have a rather mature container now I can do a DF and see that a lot of things have been inherited I'm still wondering about that far-lob-lib docker there at the end but I didn't expect it a lot of things have are inherited like the file system but I can manipulate with it myself now for instance I can do an unmount slash boot or whatever I like without influencing the original mom namespace that kind of things can be done still if I look at the hostname I can still see we have our own hostname we have our own pit numbering here although we have got some a lot of processes in the meantime by all the steps en we have our own network dedicated for this process all right that's one thing that I want to mention last thing about namespaces namespaces vanish at the moment that all the connected processes have stopped yeah then they automatically disappear but you can make persistent namespaces and that is possible if you bind mount filename to a namespace then even if all the connected processes have disappeared then the namespace will still be there and later on you can still connect to it again you can preserve it in a way yeah this is for instance the technique that is also used by the command ip netns maybe you've ever used that or use it that also uses this technique of persistent namespaces let's have a look how that works if I do a sudo onshare minus minus UTS or minus U that's also possible and get me a new bash then I can modify the hostname there but what I can do as well now is that I can create a dummy file just by a touch and bind mount with mount minus minus bind sprok self NS UTS to that dummy file en now I can finish my shell that would normally also let the UTS namespace disappear this UTS namespace but now it is connected to a file and it will be preserved you can also do this instead of doing it yourself with a touch and a mount minus minus bind you can also ask unshare to do this this is built in into unshare on beforehand I create a dummy file and then I do an unshare minus minus UTS is slash temp slash permits en now this will be done by the unshare command itself it will be connected to a file but also to this process and here I can modify the hostname again and exit the process while the file is still there yeah if I want to use it later on again then I can do the command n as enter and there I can specify minus minus UTS is slash temp slash permits remember usually we use the minus t flag with a PID but now we specify a file name and that means that the new bash will be connected instead of to its old current UTS namespace it will be connected to the namespace that we have preserved from earlier session and then in that namespace I can do in that shell I can do hostname and see the hostname of that namespace that was still existing just as a final slide for the namespaces part let's have a look at how it is correlates to for instance docker you see with docker if you do docker run that you can have a specify a lot of parameters and a lot of them also correlate to the namespaces part what you can do with a docker run normally the container gets its own UTS namespace but you can say minus minus UTS is host and that means just keep using the host namespace you can also say minus minus UTS is container and then another container ID which means in fact that it does a kind of nsenter under the hood nsenter to that container and share the UTS namespace with that other container that you specify here by the container ID and similarly you can do that for pit isolation you can say minus minus pit is host and then your processes in the container will still have high pit numbers that are also used for the other processes in the host instead of getting its own pit namespace and you can also share with another running container the pit numbering the pit number scheme by doing pit is container colon container ID similarly you can do minus minus network is host I don't want to give my process in the container its own networking namespace it should just use the namespace networking namespace of the host or should share the networking namespace with another container that's also possible and similarly for IPC isolation with the command docker run you can also specify and that's the same example that I gave you earlier in the slides minus minus ctl is and then net ip version 4 and so on that namespaced kernel parameters can also be influenced when you start a new container yes the question is can multiple containers share the same physical network device and then you mean apart from the root network which is not used by the root network namespace yes that's correct ja and that means also here with network container and then a certain container ID that more containers can have the same networking namespace and by that also see the same port numbers and they can also easily reach each other just by referring to local host because they are in the same network namespace so that's that's possible with this option of docker run yeah they are connected to the same network namespace dat even kan have a physical interface of its own ja okay we have our own network name our own mount namespace now in the container that we are building but that's still the entire file system that we have there and it should not have access to the entire file system my container should have a small file system of its own a mini file system so let's have a look what we can do about that still well what we can see and that's already an old mechanism that that's already used in unix for decades is that you can influence the root directory for a particular process yeah there is not such a thing as the root directory for the entire file system every process has its own ID of what is my root directory in the file system that's also what you see underneath slash proc slash and then a PID then you can find also root as a simlink to the real root directory for this process so you can even see it from the outside that certain processes have their own ID about what is my root directory yeah so most of the processes will see the root directory of the entire file system as their root directory but you can say well let's create in some corner of my file system let's create a directory structure with an etc directory and a bin directory and a home directory and that kind of things underneath and let's fill the bin directory with a couple of executables commands and so on and from now on I start a new process that gets this directory as its root directory yeah and all that process sees is this directory and everything underneath and this process cannot even reach the other part of the host file system and that's a technique which is already very old with the change root mechanism but there are also newer variations of that like pivot root there is a change between the old change root mechanism and pivot root change root activates a new process one process with a prepared directory as its root directory if you use pivot root and we will use that later on in the example that I'm building it changes the root directory for a couple of processes in a certain mount namespace so this is namespace related again all the processes connected to a certain mount namespace will from now on use another root directory in that namespace well of course you have to set up such a corner in your file system that you will use as the file system later on for a certain process or a couple of processes so very briefly I can do a make there of a directory called top there create a bin directory a lib64 directory an etc directory and a root directory underneath as a minimum then we have to fill those directories let's copy the slash bin and slash bash to top there bin to get a couple of commands in that new environment echo create a line that I write to the top director etc password file just create a small password file and I create a small dot bash profile for root you can see that slash root is the root is the home directory of the root user in this case of course I copy here two binaries into top there bin maar die binaries mogen gebruiken shared libraries dus met de ldd command kan ik vertellen wat de shared libraries zijn gebruikt bij slash bin bash en cat dat ik heb copied hier kan ik ze zien en ik copy die shared libraries tot lib64 in dat nieuw environment ja en met dat heb ik dit nieuw dit nieuw root directory wat we zien hier is een voorbeeld van gebruik van gebruik van root command sudo doe de change root command de eerste parameter dat ik specifiek is dat nieuw root directory dat ik wil gebruiken en dan de proces dat ik begrijp dat zal worden aansloten op dat root directory en dan zie ik mijn prompt mijn nieuw prompt en ik kan een ls minus l doen wel dat het niet uitgaat omdat ik de ls command niet kopie 2 slash bin alleen kat en bash heb ik copied in de voorbeeld maar natuurlijk kan ik de file zien dat ik hier heb met echo slash asterix echo is een shell built in dus dat zal succeden en dan kan ik ook zien wat soort dingen zijn onder de slash bin en ik kan mijn slash bin slash kat in ieder geval en ik kan ook een kat van ijzee password ik kan gebruiken kat je hebt een vraag is state-trip veranderd de naamspace of is het gebruikt het gebruikt een ander mechanisme ja je kunt er misschien soms je kunt nog wel de groeproces naamspace zien ja natuurlijk deze proces is ook aansloten op een maand naamspace maar deze root directory is gegeven op dit specifieke proces ja en zodat het heeft zijn eigen root directory in ja in zijn eigen maand naamspace maar het voelt niet de andere proces in dat aansloten dat dat heeft het zelf elke proces heeft een maand naamspace ja zelfs de maand naamspace naar het systeem die is verbeterd dus dat is waar alle procesen zijn verbeterd om als ze niet separate van dat maand naamspace en creëer hun eigen elke proces is verbeterd tot alle die naamspaces ja ja natuurlijk ps is niet mogelijk maar echo dollar dollar ik kan nog wel de pid van mijn shell en zo dus hier zien we een een voorbeeld om root te veranderen worden gebruikt maar in container techniek het meestal pivot root is gebruikt als command en met pivot root kan je de root directory modificeren voor een maand naamspace en alle procesen dat is verbeterd tot dat dus pivot root en hier kun je specifieke de nieuwe root directory en wat was de former root directory zal automatisch veranderd tot wat je specifieke als de oude root directory en alles wat onder de oude root kun je een maand en zo en je moet het niet meer en dan kan je werken met je nieuwe root directory er is een limitatie hier dat deze nieuwe root directory moet een maandpoort zijn ja het moet de maandpoort van een maandpile systeem dan kan je het als een nieuwe root directory dus ik kan niet simpel veranderen in dit example niet veranderen in de maandpoort doen een pivot root want topdoor is niet een maandpoort het is gewoon een directrie soms in de maandpile systeem dus wat we kunnen doen is dat we kunnen veranderen nu de de example weer ik had een emptie step 5 zo ver die alleen begon bash ja dat ziet als dit maar ik heb een skeleton nu dat ik kan gebruiken als een nieuwe step 5 dus laten we het step 5 scale naar step 5 en laten we eens kijken hoe het ziet als step 5 nu dus wat we zien hier is dat ik specifie gewoon een directrie nieuwe root als een shell variable ik bekijk of het er al is en of niet het zal worden gecreëerd ja dus anyhow ik zal de directrie nieuwe root creëren en nu ga ik maand een tempFS file systeem of 15 megabytes naar dat nieuwe root directrie ja want het moet een maandpunt zijn ik ga gebruiken pivot root en dat is waarom ik het zo doe ik connecteer een tempFS file systeem naar het en nu ik haal de content van deze directrie naar dat directrie zodat het de volling van de nieuwe maandpile tempFS file systeem wel, ik zal je laten zien deze califes dat is gewoon gelijk te wat we hebben gecreëerd eerder met een maandpunt onder en een etc directrie onder en dat soort dingen ja en na dat ik een cd doe naar dat nieuwe root directrie naar een nieuwe root en dan ga ik een pivot root en ik specifie de current directrie die is een nieuwe root als een nieuwe root directrie en wat was mijn root directrie zal we move naar oud root en dat moet een existente directrie waarin de oud tree zal behoogd zal behoogd te ik zou zeggen als ik in een nieuwe tree ben moet ik ook de prok directrie weer behoogd en ik kan ook specifiek mijn mijn prompt string weer voor de bash dat ik ga starten van nu aan dus dit is hoe de nieuwe step 5 ziet zoals dus wat ik kan doen is start over met step 1 weer en dan we zullen hier zien dat als ik een df doe in een nieuwe environment dat al de oud content van mijn filesystem dat ik vroeger had is behoogd naar oud root en dat kan allemaal worden behoogd maar dat moet nog worden gedaan en ik zal het niet doen hier maar dat moet nog worden gedaan in de praktijk en je kunt zien dat we een nieuwe root directrie wat is mijn filesystem van 50 megabytes nu ontdekend een kleine tree en dit is in fact ook de idee van ja om je minifilesystem voor je container natuurlijk is dit niet een overlay filesystem dat is een ander historie dat ik ik hier niet meer wil maar dit is de nieuwe filesystem de minifilesystem voor de nieuwe container de vraag is is dit een nieuwe filesystem of is het nog een deel van de oud filesystem je hebt in mijn script sorry dat ik dat ik een amount van een mkfs filesystem deed en dat ik de contents van een directrie opgemaakt heb dus ja dat dat ja de vraag is hoeveel filecommissions kan andere procesken ook nog werk in de root filesystem van dit proces ja, ze kunnen wat je hebt gezien voordat ik de pivot root is dat ik een tempfs filesystem op een nieuwe root en alle andere procesken die een nieuwe root kan reachen kan ook modificeren de dingen die hier modificeren door dit proces in dit container dus het is een shared filesystem alleen de proces in dit container kan niet boven de de urbans directrie deze root directrie wanneer de andere procesken dat we in een andere mkfs space kan nog wel worden everywhere in het filesystem andere vraag het klinkt meer als oké ja, de vrije spas in het filesystem is niet limiterd hier dus dit proces opgemaakt in dit container container kan nog alle vrije filesystem spas en ook opgemaakt voor andere procesken opgemaakt in andere namespaces dus wel, in dit geval het is een tempffs dus maar als je het root gebruikt gebruik je het root dan kan je nog alle vrije spas in het originele filesystem hier heb ik een tempffs filesystem van 50 megabytes dus dat is echt de limit wat ik kan gebruiken van hier er was een andere vraag en de vraag is zonder kopiën alle de commanden en de shared libraries en zo is het mogelijk om een link te creëren niet een simlink of een hardlink maar je kunt dingen doen met bindmountain en dat is ook vaak gebruikt in containertechnieken dat 3e dingen zijn bindmountain van het host filesystem naar het filesystem van een container zodat je het nog steeds kunt gebruiken ja andere vraag ja je kunt hier nog bindmountain doen na een pivot root dus de vraag is als je hier in dit proces in dit environment kan je nog bindmountain doen dat is mogelijk ja dat zou mogelijk zijn maar dan nog steeds shares de dingen met de procesken die op het host andere procesken die op het host zijn ja maar het is nog steeds mogelijk om bindmountain te doen zonder dit oké dus daar waren er vragen oké ik heb 4 slides de laatste 4 slides dat is over een ander ding en dat is capacities wat is ook een laatste technie dat ik wil mention wanneer we met containers als we naar de traditionele Unix privilege scheme dan zien we traditionelly dat als je een proces hebt die met UID0 praktisch de rootuser dan heeft dat proces al de speciaal privileges er zijn ja je kunt starten verbinden de systeem je kunt modificeren de hardware klok je kunt iedere proces dat soort dingen worden gevoerd dan als je een proces hebt niet met UID0 een normale user dan heb je geen speciaal privileges allemaal ja dat is de traditionele scheme wel de linux privilege scheme daar kunnen we zien dat ze een capaciteit mechanisme hebben geïnteresseerd in de kernel en capacities betekent dat elke speciaal privilege dat is normaal betekend voor een rootuser is een certain bit in een bitlist en als dat bit is zet je kunt dat doen speciaal privilege als je een korrespondente bit is niet zet je kunt niet dat privilege doen en alle deze distincte privileges die worden geïnteresseerd in bits hebben naam zoals kan je een verkeerde oon doen kan je een kill doen kan je een reboot systeem kan je een proces meer dan normaal een nice value wel ik moet zeggen een lower nice value wat betekent een hoge prioriteit dus alle deze speciaal privileges dat normaal geïnteresseerd voor een rootuser zijn distinct bits in feite en als je wilt weten meer over het je kunt bekijken de zeven capacities voor dat wat je ziet in de kernel code nowadays is dat er is geen check meer als je een speciaal systeem koal is de current process als uid 0 nee er is een check is dit een certain bits set in de current processes administratie dan de proces is geïnteresseerd om dit te doen en dat betekent dat je je zelf kan geven normaal gebruikers speciaal privileges en je kunt even een rootuser hebben die geen privileges heeft al het al begint welke bits zijn zet in de capaciteit list in de proces administratie dat betekent wat de proces is geïnteresseerd als speciaal privileges dus we kunnen zien deze privileges en er is veel te zeggen over dit en ik ga niet doen dat de enige ding dat ik wil kijken is de effectieve capaciteit zoals de naam zegt dat is echt effectief dat bitlist en je kunt zien dat bitlist weer in dat status file dat pseudo file status onder de slash proc slash pid en zo status een van de laatste lijn in dat file zeggen kept effectief en dat is een hexadecimal waalje van dat bitlist dat betekent welke speciaal privileges zijn geïnteresseerd voor je en die zijn niet geïnteresseerd voor je en je kunt zien dat mijn eigen schijf runnen als een normaal user kept effectief al de bits zijn niet zet en als je bijvoorbeeld heeft een look aan proces 1 system D die ook runnen onder uid 0 maar het is niet in fact relevant daar je kunt zien dat een aantal bits in fact alle bits zijn zet hier als een hexadecimal waalje wel als je de tarffile downloadt vind je een speciaal programma die is geïnteresseerd als een command en daar kan je zien dat het alle deze waaljes uit deze status files en je kunt een overvuur over alle procesen en de effectieve capacities wel je kunt wachten speciaal hier je ziet veel dat heeft alle capacities maar er zijn ook procesen die niet alle capacities hebben ze hebben een aantal capacities laten we een look op 739 wat is de rootkit demon gewoon een arbitrair proces als we een look op dat proces dat is mijn muis deze voorbeeld het heeft een certain capaciteit zet wel, je kunt met capshow ook vragen voor een particular proces en dan kan je nog ook een look op de mensrebel format wat betekent dat het echt het hele bits in de verb dat we zo eerder op een eerder slide dus je kunt precies zien wat is gevoel en je zal zien als ik een look hier op de sleepproces alle procesen probleem sleep je kunt hier zien dat deze proces remember het was begonnen via dockerrun je kunt zien dat het niet alle capacities maar het heeft wat capacities en het heeft wat speciaal privileges ik kom terug naar dat later op ja, dus dit is de wat de proces is gevoelig te doen en dat is ook de scenario's die je kan hebben vandaag dat je een non-route proces kan hebben dat nog heeft capacities of route procesen die geen capacities hebben allemaal ja, dat is independend van de UID er is nog voor compatibility reden de fact dat als je veranderd route dat alle capacities zal worden zet maar van dat moment op wat je kan veranderd certain capacities weer als je wilt ja in de kernel al al lang tijdens ik denk al in 2.6 maar ik ben niet zeker over het ja het is al voor een decade of meer ja, maar je weet niet omdat van de compatibility dat als je veranderd dat alle capacities zal worden zet maar het is onder de hoed het werkt zoals dit ja dus als de final slide hoe does this correlate tot for instance docker docker run command wat je kunt zien is dat ze hebben specifiek een distinct set van capacities specifiek voor dockercontainers dus als je een proces in een dockercontainer runs als route het betekent dat alle capacities zijn zet maar alleen dit set van capacities is zet en dat betekent voor instance de keelcapabiliteit betekent dat je kan keel een proces maar is dat de probleem als het is binnen de container pit namespace dus certain things zijn niet even bed dat ze zijn zet maar natuurlijk er is veel te doen over security in dit in dit geval je kunt ook influence deze capacities door docker run cap app en je kunt zelfs assign meer capacities dan dit wel define set of je kunt doen een minus minus cap drop en drop wat van deze capacities en adapt het naar jezelf bedrijven je moet niet te steken tot de de zet dat is ontdekend hier wat is de ingang is als je een docker run minus minus privilijst dat betekent dat je alle capacities en dat ook betekent dat je onrestrict de device access die is natuurlijk een security risic Right, dat is de laatste slide en we hebben al al een paar vragen tijdens de session en ik ben bang het is vijf minuten vorig half dus we moeten eindigen ik wil je bedankt voor het zijn hier en voor het me te luisteren