<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Marcin Wasiucionek</title>
    <description>The latest articles on DEV Community by Marcin Wasiucionek (@wasiucionekm).</description>
    <link>https://dev.to/wasiucionekm</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1301265%2F957a7ec6-48aa-4e1f-baed-54be74f3ecb2.jpg</url>
      <title>DEV Community: Marcin Wasiucionek</title>
      <link>https://dev.to/wasiucionekm</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/wasiucionekm"/>
    <language>en</language>
    <item>
      <title>A hands-on lab: Why running as root in Kubernetes containers is dangerous?</title>
      <dc:creator>Marcin Wasiucionek</dc:creator>
      <pubDate>Wed, 24 Jul 2024 18:35:54 +0000</pubDate>
      <link>https://dev.to/wasiucionekm/kubernetes-security-in-practice-implications-of-running-containers-as-root-474n</link>
      <guid>https://dev.to/wasiucionekm/kubernetes-security-in-practice-implications-of-running-containers-as-root-474n</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;In the world of Kubernetes security, one commonly heard recommendation is to run containers as non-root users. But what are the real security implications of running as root within containers? This best practice is often emphasized in Docker images and Kubernetes configurations. In Kubernetes manifests it can be done with the following code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;securityContext:
   runAsNonRoot: true # Verifies that the UID specified by runAsUser is not 0. Pod will not run if UID is set to 0. 
   runAsUser: 1000 # Sets the user ID for the processes running in the container to 1000 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can set only the &lt;code&gt;runAsUser&lt;/code&gt; field without including &lt;code&gt;runAsNonRoot&lt;/code&gt;. However, if you specify &lt;code&gt;runAsNonRoot&lt;/code&gt;, you must also define &lt;code&gt;runAsUser&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why running as root in containers can be dangerous?  ⚠️
&lt;/h2&gt;

&lt;p&gt;There are several potential attack vectors, but the most apparent ones that come to mind are outlined below. Let’s explore these through experimentation.&lt;/p&gt;

&lt;h3&gt;
  
  
  Setting up the lab 🛠️
&lt;/h3&gt;

&lt;p&gt;To illustrate the risks of running containers as root, we'll conduct a practical test using two similar containers:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;An &lt;code&gt;alpine:3.20.2&lt;/code&gt; container running as &lt;code&gt;root&lt;/code&gt; by default.&lt;/li&gt;
&lt;li&gt;A custom &lt;code&gt;alpine:3.20.2&lt;/code&gt; container configured to run as a &lt;code&gt;non-root&lt;/code&gt; user.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Here’s the Dockerfile for the non-root container:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# Use the Alpine image as the base
FROM alpine:3.20.2

# Add a non-root user named 'nonroot' with user ID 1000
RUN adduser -D -u 1000 nonroot

# Set the user to 'nonroot' for subsequent commands
USER nonroot

# Optional: Set a default command
CMD ["sh"]

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Using &lt;code&gt;Minikube 1.32.0&lt;/code&gt; with Kubernetes version &lt;code&gt;v1.28.3&lt;/code&gt; for this local setup, I build the image and make it accessible to my Minikube cluster with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;eval $(minikube docker-env)
docker build . -t nonroot:1.0.0

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next, I deploy these containers in Kubernetes. For this demonstration, I use a hostPath volume to test privileges, though I strongly advise against using hostPath outside your homelab! The &lt;code&gt;root&lt;/code&gt; pod definition is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;apiVersion: v1
kind: Pod
metadata:
  name: root
spec:
  containers:
  - name: alpine
    image: alpine:3.20.2
    command: ["/bin/sh", "-c"]
    args: ["while true; do sleep 100; done"]
    volumeMounts:
    - mountPath: /data
      name: host
  volumes:
  - name: host
    hostPath:
      path: /testDir
      type: Directory
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And the &lt;code&gt;nonroot&lt;/code&gt; pod definition is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;apiVersion: v1
kind: Pod
metadata:
  name: nonroot
spec:
  containers:
  - name: alpine
    image: nonroot:1.0.0
    command: ["/bin/sh", "-c"]
    args: ["while true; do sleep 100; done"]
    securityContext:
      runAsUser: 1000          # Ensure the container runs as non-root user with UID 1000
    volumeMounts:
    - mountPath: /data
      name: host
  volumes:
  - name: host
    hostPath:
      path: /testDir
      type: Directory
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Testing potential attacks 🧪
&lt;/h3&gt;

&lt;h4&gt;
  
  
  Downloading malware 🦠
&lt;/h4&gt;

&lt;p&gt;One common attack vector is downloading and executing malicious packages. I tested this by attempting to just fetch some data from &lt;a href="https://dev.to"&gt;https://dev.to&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftubwl338343hklxznaln.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftubwl338343hklxznaln.png" alt="Attempt to run curl" width="678" height="455"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Since curl was not initially installed in the containers, I tried to install it:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0zplsrdhsx4qt75mfhul.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0zplsrdhsx4qt75mfhul.png" alt="Installing curl in containers" width="672" height="727"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The installation succeeded in the &lt;code&gt;root&lt;/code&gt; container but failed in the &lt;code&gt;nonroot&lt;/code&gt;. Let's try to fetch the data.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fw5xl3f098zt99jcesgoo.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fw5xl3f098zt99jcesgoo.png" alt="Fetching the data" width="672" height="727"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It worked correctly. This indicates that running as a non-root user can effectively mitigate this attack vector. Of course, other measures, such as using a read-only filesystem, can further enhance security. I will cover that in a future article.&lt;/p&gt;

&lt;h4&gt;
  
  
  Access to host resources 🔒
&lt;/h4&gt;

&lt;p&gt;I have a host directory mounted to the pod (again - please do not do that outside of your homelabs!). With this kind of access attacker can try to access the directory with the static pods manifests and try to run the malicious pods (downloading malicious images should be blocked by your cluster policies though). It can be done by adding a new manifest to static manifest directory, which is usally &lt;code&gt;/etc/kubernetes/manifests&lt;/code&gt; on the kubernetes node. &lt;/p&gt;

&lt;p&gt;Having this access attacker can try to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;perform Man-in-the-Middle attack by deploying the pod that intercepts network traffic within the cluster and capture sensitive information,&lt;/li&gt;
&lt;li&gt;deploy a backdoor pod with reverse shell (you can find examples at &lt;a href="https://www.revshells.com/" rel="noopener noreferrer"&gt;https://www.revshells.com/&lt;/a&gt;) accepting connections from hacker's machine,&lt;/li&gt;
&lt;li&gt;run a pod that transfers the data from the volumes containing sensitive data to an external entity,&lt;/li&gt;
&lt;li&gt;expand the attack vector by reading secrets from ETCD store and penetrating the infrastructure further,&lt;/li&gt;
&lt;li&gt;run crypto miner using your resources for financial gain. &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let's deploy a simulation of crypto miner using the following manifest:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;apiVersion: v1
kind: Pod
metadata:
  name: crypto-miner
spec:
  containers:
  - name: miner-container
    image: busybox
    command: ["/bin/sh", "-c", "while true; do echo 'Mining in progress...'; sleep 5; done"]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And adding the static pod file from both pods:&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fq8l7zj65g9n6pjrtwd6h.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fq8l7zj65g9n6pjrtwd6h.png" alt="Image description" width="800" height="171"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As you can see the command on the non-root pod was denied. It run successfully on root pod and added the crypto miner manifest to the static pods directory. Was the pod created in a cluster?&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frc98hykskxcwii27pvid.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frc98hykskxcwii27pvid.png" alt="Image description" width="800" height="345"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Yes, it runs in the cluster and will be rescheduled in case of failures or restarts. &lt;/p&gt;

&lt;p&gt;Another thing that attacker can do with access to hostPath on a node is to read /etc/passwd file on the host machine. This file does not contain the password in plain text, but it gives attacker a knowledge about users existing in a system. This knowledge combined with some other data sources and /etc/shadow may let the attacker exploit the system further.&lt;/p&gt;

&lt;h4&gt;
  
  
  Privilege Escalation 🚫
&lt;/h4&gt;

&lt;p&gt;Can't you just change the &lt;code&gt;non-root&lt;/code&gt; user to &lt;code&gt;root&lt;/code&gt; and do exactly the same? Let's try it.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ft4ctudwo4k4lan5a96w8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ft4ctudwo4k4lan5a96w8.png" alt="Trying to switch to root" width="672" height="266"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;No, you can't. The attempt to switch from &lt;code&gt;non-root&lt;/code&gt; to &lt;code&gt;root&lt;/code&gt; failed, demonstrating that without sudo access, privilege escalation is not feasible. Therefore, if a &lt;code&gt;non-root&lt;/code&gt; user isn’t in the sudoers list, the risk is mitigated.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to prevent? 🛡️
&lt;/h2&gt;

&lt;p&gt;To prevent security issues related to running containers as root, follow these best practices:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Use non-root users:&lt;/strong&gt; Always define and use non-root users in your Docker container. 🧑‍💻&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Leverage Kubernetes Security Context:&lt;/strong&gt; Specify the user for container execution using Kubernetes security contexts. 🔐&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Conclusion ✨
&lt;/h2&gt;

&lt;p&gt;I hope this article has shed light on the importance of running containers as non-root users within Kubernetes. &lt;/p&gt;

&lt;p&gt;I’d love to hear your thoughts and insights - drop a comment below or share your feedback! Stay tuned for more insights as I continue to explore and describe various aspects of Kubernetes security. &lt;/p&gt;

&lt;p&gt;Stay secure!🔐&lt;/p&gt;

&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;&lt;a href="https://www.docker.com/blog/understanding-the-docker-user-instruction/" rel="noopener noreferrer"&gt;Understanding the Docker USER instruction&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://kubernetes.io/docs/tasks/configure-pod-container/security-context/" rel="noopener noreferrer"&gt;Kubernetes Security Context&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://hub.docker.com/_/alpine" rel="noopener noreferrer"&gt;Alpine Docker images on Docker Hub&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://kubernetes.io/docs/concepts/security/pod-security-standards/" rel="noopener noreferrer"&gt;Kubernetes Pod Security Standards&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;

</description>
      <category>kubernetes</category>
      <category>security</category>
      <category>cloud</category>
      <category>docker</category>
    </item>
  </channel>
</rss>
