<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[Exploring cybersecurity]]></title><description><![CDATA[My own journey in cybersecurity, starting from scratch ]]></description><link>https://orelokavi.com</link><image><url>https://cdn.hashnode.com/res/hashnode/image/upload/v1593680282896/kNC7E8IR4.png</url><title>Exploring cybersecurity</title><link>https://orelokavi.com</link></image><generator>RSS for Node</generator><lastBuildDate>Sat, 30 May 2026 03:35:28 GMT</lastBuildDate><atom:link href="https://orelokavi.com/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[Containers Are Not Magic]]></title><description><![CDATA[In this blog post, I will cover the fundamentals of containers and the magic behind them, and open paths to researching the mechanisms behind containers in the future.
To understand what a container i]]></description><link>https://orelokavi.com/containers-are-not-magic</link><guid isPermaLink="true">https://orelokavi.com/containers-are-not-magic</guid><dc:creator><![CDATA[orel okavi]]></dc:creator><pubDate>Sun, 29 Mar 2026 07:30:00 GMT</pubDate><enclosure url="https://cdn.hashnode.com/uploads/covers/69c8c4f87816e434a0fa3705/b443f057-68e7-4919-a39b-b1c4bd3416c2.jpg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>In this blog post, I will cover the fundamentals of containers and the magic behind them, and open paths to researching the mechanisms behind containers in the future.</p>
<p>To understand what a container is, we need to ask ourselves five main questions:</p>
<ol>
<li><p>What is a process?</p>
</li>
<li><p>Why do we need a container?</p>
</li>
<li><p>How to run a container?</p>
</li>
<li><p>What is a container? And what is it actually made of?</p>
</li>
<li><p>How is a container being created under the hood?</p>
</li>
</ol>
<p>In order to truly understand containers, we need to start low, which is to first understand what a process is.</p>
<h3><strong>What is a process?</strong></h3>
<p>Simply put, a process is an executing instance of a program. It holds the state, memory mapping, and resources required for the CPU to execute the code. (There is much more to talk about what a process is, but that would be enough for our discussion.) For our aiming goal, we will focus only on a specific feature of Linux's process that makes containers happen.</p>
<p>Let's take a look at the Linux source code of the process found here:</p>
<p><a href="https://github.com/torvalds/linux/blob/master/include/linux/sched.h">include/linux/sched.h</a></p>
<p>We can find a row that reveals the powerful feature that is responsible for containers:</p>
<pre><code class="language-c">/* Namespaces: */
struct nsproxy			*nsproxy;
</code></pre>
<p><strong>Introducing Namespaces.</strong></p>
<p>What are namespaces? Let's look at the source code that will reveal a little bit more. <a href="https://github.com/torvalds/linux/blob/master/include/linux/nsproxy.h#L32">include/linux/nsproxy.h#L32</a></p>
<pre><code class="language-c">struct nsproxy { 
refcount_t count; 
struct uts_namespace *uts_ns; 
struct ipc_namespace *ipc_ns; 
struct mnt_namespace *mnt_ns; 
struct pid_namespace *pid_ns_for_children; 
struct net *net_ns; 
struct time_namespace *time_ns; 
struct time_namespace *time_ns_for_children; 
struct cgroup_namespace *cgroup_ns; 
};
</code></pre>
<p>Remember this code, as we will understand it a bit later as we dive in.</p>
<p>Now let's get back to containers and connect the dots, as we explain what we see in the source code above.</p>
<h3>Why do we need a container?</h3>
<p>Containers allow us to create a virtualized "bubble" of a thin, minimal machine to execute an application. The main purpose is to create an easy-to-ship environment of the application to production without worrying about dependencies and environment differences between development and production. The key is that we can control what will live inside the container bubble, which fits our application needs.</p>
<h3>How to run a container?</h3>
<p>Let's say we want to run a simple nginx server that will run our application.</p>
<p>For the purpose of this tutorial, I use Docker Engine on WSL.</p>
<pre><code class="language-plaintext">docker run --name my-nginx -p 8080:80 -d ubuntu/nginx
</code></pre>
<p>Here is our simple container running on our local machine.</p>
<img src="https://cdn.hashnode.com/uploads/covers/69c8c4f87816e434a0fa3705/6bf5bfb9-0726-4cab-ba4f-037ad866b726.png" alt="" style="display:block;margin:0 auto" />

<p>go to: <code>http://127.0.0.1:8080/</code> and voilà, we have our nginx container running on our local machine.</p>
<img src="https://cdn.hashnode.com/uploads/covers/69c8c4f87816e434a0fa3705/963a132a-ec9d-4b91-9bad-4e5e928248fb.png" alt="" style="display:block;margin:0 auto" />

<h3>What is a container? And what is it actually made of?</h3>
<p>Start with the end in mind: a container is basically just a process. But an isolated one.</p>
<p>Let's get inside the container and explore a little bit of what it means.</p>
<pre><code class="language-plaintext">docker exec -it my-nginx /bin/bash
</code></pre>
<p>Now we get a Bash shell inside the containers. Run the <code>lsns</code> command, and we get all the namespaces that manage the container:</p>
<img src="https://cdn.hashnode.com/uploads/covers/69c8c4f87816e434a0fa3705/fbf2d491-90dc-4dc6-920b-9997c65a1002.png" alt="" style="display:block;margin:0 auto" />

<p>Looks familiar, right? We remember the <code>struct nsproxy</code> above. Let's break things down to what namespaces are.</p>
<ul>
<li><p>time - A relatively new namespace. Usually, containers share the same kernel time.</p>
</li>
<li><p>cgroup - Creates the "bubble" around resource management. By hiding the host's actual tree hierarchy, it tricks the container into thinking it sits at the absolute 'Root' of the system.</p>
</li>
<li><p>user - each container can get its own root user or regular user without conflicting with the local host machine.</p>
</li>
<li><p>net - each container can have a different network interface and IP address.</p>
</li>
<li><p>ipc - Inter-Process Communication, it’s about whether processes can communicate with each other or see each other's data.</p>
</li>
<li><p>mnt - Mount, responsible for the isolation of the file system of each container, the mount point where each container starts its root file system in the host machine.</p>
</li>
<li><p>uts - Responsible for the hostname and domain name.</p>
</li>
<li><p>pid - Just like a regular OS boot, the container gets its own isolated PID tree starting from 1.</p>
</li>
</ul>
<img src="https://cdn.hashnode.com/uploads/covers/69c8c4f87816e434a0fa3705/3e14ac19-f88c-4811-bc01-1c28f17c6951.png" alt="" style="display:block;margin:0 auto" />

<p>We can see in the images above that the container has its own hostname, its own 'root' user, its own new network interface and IP address, and it can't see the files above its '/' root file, which tells us something: Docker takes care of these configurations by default, and maybe we can change and control some of them.</p>
<h3>How is a container being created?</h3>
<p>All hints lead to this point. As we mentioned, a container is an isolated process. The isolation is being implemented via namespaces, which control the aspects of how isolated the container will be. Can it share the same IP address and network interface as the host or other containers? Share hostname? Can it see other processes? Share file system? and many other questions that regard the security elements of containers. In this blog post, we used Docker to create a basic container, and we saw that Docker takes care of some defaults in isolation, such as network, hostname, and filesystem.</p>
<p>If a container is just a process, it means we can see it on our local machine. To prove this point, I will switch to my VMWare Ubuntu machine (because WSL on Windows makes things complicated for Docker)</p>
<img src="https://cdn.hashnode.com/uploads/covers/69c8c4f87816e434a0fa3705/60952f59-0c8a-43dc-bc6a-2a4693d3618c.png" alt="" style="display:block;margin:0 auto" />

<p>First, we use the Docker 'inspect' command to find the container's "real" Pid in the eyes of the operating system, and then we verify that the same process is shown in the local host machine.</p>
<p>In the context of the code we saw earlier, every process being created in the OS shares the same features, such as file system, network interface, host name, etc., meaning all these (or most) structs point to the same resources, while when creating a container, these namespaces point to different resources.</p>
<p>Let's see it with our own eyes and compare from both sides, the OS side and the container side. Let's open two terminals and analyze what we see.</p>
<p>Run the following command in one of them:</p>
<pre><code class="language-plaintext">docker exec -it my-nginx /bin/bash
</code></pre>
<p>Now let's compare:</p>
<p>The first screenshot is the container perspective of the world:</p>
<img src="https://cdn.hashnode.com/uploads/covers/69c8c4f87816e434a0fa3705/49209c79-8104-4a62-8594-c6ae89b732d5.png" alt="" style="display:block;margin:0 auto" />

<p>And here is the OS perspective:</p>
<img src="https://cdn.hashnode.com/uploads/covers/69c8c4f87816e434a0fa3705/df86ddca-5de9-46f6-8e85-69b3527a8f73.png" alt="" style="display:block;margin:0 auto" />

<p>Comparing the two, we can see the differences:<br />(notice that I changed to a root user in my local machine), Both have root user, different hostname. The network is an interesting part. We can see that inside the container, we have the <code>eth0@if4</code> interface and IP address <code>172.17.0.2/16</code> looks normal, right? But when we see what happens in the OS, we see something new.</p>
<p>Let's explain: what Docker does in order to transfer network data inside and outside containers is by using DNAT.</p>
<ol>
<li><p>It creates two new network interfaces: a default gateway named <code>docker0</code> and another <code>eth0@if4</code> inside the container.</p>
</li>
<li><p>It creates a 'virtual cable' named <code>veth&lt;id&gt;@if2</code> responsible for connecting both interfaces and delivering packets between the container and the host bridge.</p>
</li>
</ol>
<p>But to actually translate network addresses and map ports without conflict, Docker uses Linux iptables to create the DNAT.<br />run: <code>sudo iptables -t nat -L -n</code> on the host and see port mapping in action.</p>
<img src="https://cdn.hashnode.com/uploads/covers/69c8c4f87816e434a0fa3705/39beeaa7-7471-4ea9-9503-de1c038cc1eb.png" alt="" style="display:block;margin:0 auto" />

<p>In regard of the file system, we can see that running <code>cat /proc/self/cgroup</code> in both terminals, we will also get different answers. The container shows that there is nothing above, and it lives in the root files, while the OS shows us the hard, messy truth of the file system that it lives inside the Docker file system created in order to create the virtual isolation. We can actually peek inside the container file system from the outside, in the host.</p>
<p>Let's create a file in the container in the root folder <code>touch my-container-file.txt</code> and...</p>
<img src="https://cdn.hashnode.com/uploads/covers/69c8c4f87816e434a0fa3705/047eccd4-1e3a-454d-a664-1f06fd2cecb2.png" alt="" style="display:block;margin:0 auto" />

<p>Let's summarize our main takeaway here: A container is just an illusion created by the OS, while all along it's just an isolated process being manipulated via namespaces.</p>
<p>Final Note:</p>
<p>There are other Linux features responsible for creating what we call a 'Container'. Here is a diagram that will give you a hint for future research, as I hope to cover them in the future.</p>
<img src="https://cdn.hashnode.com/uploads/covers/69c8c4f87816e434a0fa3705/66b7c336-f89e-46bf-99ea-62bbcb3ccfcc.png" alt="" style="display:block;margin:0 auto" />

<p>Today, we have scratched the surface of the most fundamental Linux feature, 'namespaces', which allow us to create the magic called 'Containers.'</p>
<p>Here is something to think about: what if you could control all these isolation features? Or even build our own container using the same namespace feature?</p>
]]></content:encoded></item></channel></rss>