Locked Doors, Untrusted Keys: Securing Containers in the Wake of Leaky Vessel Vulnerabilities

Locked Doors, Untrusted Keys: Securing Containers in the Wake of Leaky Vessel Vulnerabilities

2024 2 2 nirmata runc blog v3

The recent buzz surrounding the actively exploited runc vulnerabilities “Leaky Vessels”  (CVE-2024-21626) serves as a timely reminder of two essential container security principles: image trust and comprehensive patching. Let’s delve into why these aspects are crucial for your containerized environments.

  1. Trustworthy Images are Foundational: It might seem self-evident, but avoiding untrusted images is paramount. My experience conducting Kubernetes security reviews highlights this as the most frequent pitfall. In the context of the runc vulnerability, pulling an image from an unreliable source could grant attackers a direct entry point into your Kubernetes cluster. Remember, the attack surface expands with every untrusted image you incorporate.
  2. Patching Permeates the Entire Stack: While patching container orchestration platforms like Kubernetes is common practice, don’t neglect low-level components like runc. Often overlooked due to their seemingly insignificant nature, these pieces play a critical role. In this case, runc establishes and executes the container sandbox, making its security integral. Leaving it unpatched creates a gaping hole in your defenses, nullifying the effectiveness of security measures applied elsewhere.

By adhering to these fundamental principles, you can significantly strengthen your container security posture. Scrutinize image sources meticulously and prioritize patching across your entire container ecosystem, from high-level platforms to foundational components like runc. Remember, a chain is only as strong as its weakest link. Don’t let a seemingly minor vulnerability become your downfall. 

Suppose you are already a Nirmata customer running Nirmata Policy Manager or are running Kyverno in production. In that case, you can use the following policy to give you a better view of how impactful this new vulnerability is going to be. 

The policy will check your container runtimes for the vulnerable versions and produce an actionable report:


apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
  name: check-container-run-time
spec:
  validationFailureAction: Audit
  background: true
  rules:
  - name: container-run-time-version
    match:
      any:
      - resources:
          kinds:
          - Node
    context:
    - name: cr_version
      variable:
        jmesPath: split(request.object.status.nodeInfo.containerRuntimeVersion, '://')[1]
    - name: cr_runtime
      variable:
        jmesPath: split(request.object.status.nodeInfo.containerRuntimeVersion, '://')[0]
    validate:
      message: "Your container runtime is vulnerable to CVE-2024-21626 & CVE-2024-23651: {{cr_runtime}}:{{cr_version}}"
      deny:
        conditions:
          any:
          - key: |-
              {{ cr_runtime == 'containerd' && semver_compare(cr_version, '<= 1.6.27') || false }}
            operator: Equals
            value: true
          - key: |-
              {{ cr_runtime == 'runc' && semver_compare(cr_version, '<= 1.1.11') || false }}
            operator: Equals
            value: true

Furthermore, you can detect containers trying to exploit the vulnerability by mounting /proc/self/fd/* note that it is recommended you run the policy in Audit prior to Enforce mode as potential false positives are expected.


apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
  name: disallow-proc-self-fd-workingdir
spec:
  validationFailureAction: Enforce
  background: true
  rules:
    - name: no-proc-self-fd-images
      match:
        any:
        - resources:
            kinds:
              - Pod
      validate:
        message: "Mounting /proc/self/fd is not allowed CVE-2024-21626."
        pattern:
          spec:
            containers:
              - =(workingDir): "!/proc/self/fd*"
            initContainers:
              - =(workingDir): "!/proc/self/fd*"
            ephemeralContainers:
              - =(workingDir): "!/proc/self/fd*"

You can use the following in order to block persons from blatantly trying to exploit Leaky Vessels. It will not be able to detect a container that has hidden its attempts to abuse the vulnerability deeper in its code, the scope of detection of such an attack is beyond Kyverno.


apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
  name: block-proc-self
spec:
  validationFailureAction: Enforce
  rules:
  - name: container-exec-block-proc-self
    match:
      any:
      - resources:
          kinds:
          - Pod/exec
          - Pod
    validate:
      message: "Pod {{ request.object.metadata.namespace }}/{{ request.object.metadata.name }} cannot use /proc/self/cwd, CVE-2024-21626"
      deny:
        conditions:
          any:
          - key: "{{ request.object.spec.containers[].args[] | contains(@, '/proc/self/cwd') }}"
            operator: Equals
            value: true
          - key: "{{ request.object.spec.initContainers[].args[] | contains(@, '/proc/self/cwd') }}"
            operator: Equals
            value: true
          - key: "{{ request.object.spec.ephemeralContainers[].args[] | contains(@, '/proc/self/cwd') }}"
            operator: Equals
            value: true
          - key: "{{ request.object.command | contains(@, '/proc/self/cwd') }}"
            operator: Equals
            value: true

apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
  name: block-proc-self-mounting
spec:
  validationFailureAction: Enforce
  rules:
  - name: no-proc-self-images
    match:
      any:
      - resources:
          kinds:
          - Pod
    validate:
      message: "Pod {{ request.object.metadata.namespace }}/{{ request.object.metadata.name }} with images having /proc/self/fd/ in their layers are not allowed CVE-2024-21626."
      foreach:
      - list: "request.object.spec.containers"
        context: 
        - name: imageData
          imageRegistry:
            reference: "{{ element.image }}"
        deny:
          conditions:
            any:
              - key: "{{ imageData.configData.history[].created_by | contains(@, '/proc/self/fd/') }}"
                operator: Equals
                value: true
      - list: "request.object.spec.initContainers"
        context: 
        - name: imageData
          imageRegistry:
            reference: "{{ element.image }}"
        deny:
          conditions:
            any:
              - key: "{{ imageData.configData.history[].created_by | contains(@, '/proc/self/fd/') }}"
                operator: Equals
                value: true
      - list: "request.object.spec.ephemeralContainers"
        context: 
        - name: imageData
          imageRegistry:
            reference: "{{ element.image }}"
        deny:
          conditions:
            any:
              - key: "{{ imageData.configData.history[].created_by | contains(@, '/proc/self/fd/') }}"
                operator: Equals
                value: true

Beyond Trust, Verify: Securing the Container Supply Chain with Signing

Container image signing plays a crucial role in ensuring the authenticity and integrity of the code within your containerized applications. It works like a digital fingerprint, allowing you to verify that the image you’re deploying hasn’t been tampered with or modified in any way. This explicitly blocks potential vulnerabilities such as Leaky Vessel by ensuring you never execute unknown code.

Benefits

  • Increased security: Signing helps prevent attackers from injecting malicious code into your images, potentially compromising your entire system.
  • Enhanced trust: Signing verifies the image’s origin and publisher, ensuring you’re deploying what you intended.
  • Improved compliance: Some industries and regulations mandate signed images for security and audit purposes.
  • Reduced risk: Identifying and mitigating vulnerabilities becomes easier when you know precisely what code you’re running.

Mechanisms

  • Signing Key Pair: You obtain a signing key pair with a private key (kept secret) and a public key (distributed).
  • Signing the Image: The private key is used to sign the container image cryptographically, creating a signature.
  • Verifying the Signature: On deployment, the public key and signature are used to confirm that the image hasn’t been altered.

Popular Tools

  • Cosign: An open-source tool based on the Sigstore standard, supporting keyless signing and various signing authorities.
  • Notary: An integrated solution from Docker for signing and verifying images within their ecosystem.
  • AWS Signer: A managed service from AWS for signing images stored in their Elastic Container Registry (ECR).
  • Venafi CodeSign Protect:  A managed X.509 and HSM service with enterprise-grade signing and verification.

 

Tie It All Together

If you do nothing else, please consider:

  1. Scan your nodes for vulnerable runc versions: Use the provided Kyverno policy to identify nodes running vulnerable versions of containerd or runc (CVE-2024-21626 & CVE-2024-23651). Patch affected nodes immediately.
  2. Audit your deployments for suspicious container behavior: Implement the Kyverno policy to detect containers attempting to mount /proc/self/fd/*, the technique used in the Leaky Vessels exploit. Run it in audit mode first to identify potential false positives.
  3. Block execution of untrusted images: Enforce the Kyverno policy that checks for images containing /proc/self/fd/ in their layers, potentially indicating tampering.

 

For long-term security, use Nirmata Policy Manager and Kyverno together to offer robust image security integrations, providing you with the ability to:

  1. Implement image signing and verification: This ensures the authenticity and integrity of your container images, preventing vulnerabilities like Leaky Vessels. Consider tools like Cosign, Notary, AWS Signer, or Venafi CodeSign Protect.
  2. Prioritize image trust: Only pull images from trusted sources and registries. Avoid untrusted images to minimize the attack surface.
  3. Maintain comprehensive patching: Patch not just Kubernetes but also low-level components like runc. A chain is only as strong as its weakest link.
  4. Use Nirmata Policy Manager: Leverage these tools to enforce security policies and automate vulnerability detection and mitigation.

 

If you would like to manage policies and governance easily across multiple clusters, check out what Nirmata is building at https://nirmata.com/.

Securing OpenTofu with Nirmata Powered by Kyverno
The Need for Speed: Optimizing Kyverno's Performance
No Comments

Sorry, the comment form is closed at this time.