alpine-chrome icon indicating copy to clipboard operation
alpine-chrome copied to clipboard

Running the container with seccomp option doesn't work anymore

Open mflorea opened this issue 2 months ago • 6 comments

Describe the bug

The container fails to start when using the seccomp option. I'm simply following the https://github.com/jlandure/alpine-chrome?tab=readme-ov-file#-the-best-with-seccomp section. This used to work fine for me one or two weeks ago.

To Reproduce Steps to reproduce the behavior:

  1. Image version:
    zenika/alpine-chrome@sha256:dbbe4612e677d952996ce1281ada255f58c5b6f8115f02e0dc075f2742a3de2b
    
  2. Download chrome.json
    wget https://raw.githubusercontent.com/jfrazelle/dotfiles/master/etc/docker/seccomp/chrome.json
    
  3. Start the container
    docker container run -it --rm --security-opt seccomp=$(pwd)/chrome.json zenika/alpine-chrome
    

What is the expected behavior?

The container starts.

What is the actual behavior?

The container fails to start with this error:

docker: Error response from daemon: failed to create task for container: failed to create shim task: OCI runtime create failed: runc create failed: unable to start container process: error during container init: error closing exec fds: get handle to /proc/thread-self/fd: unsafe procfs detected: openat2 fsmount:fscontext:proc/thread-self/fd/: function not implemented: unknown

Possible solution

No idea.

Logs

I don't have other logs.

Versions

Software Version(s) How-to
Chromium 124.0.6367.78 docker container run -it --rm --entrypoint "" zenika/alpine-chrome chromium-browser --version
Image dbbe4612e677d952996ce1281ada255f58c5b6f8115f02e0dc075f2742a3de2b docker image inspect --format='{{index .RepoDigests 0}}' zenika/alpine-chrome
Docker Engine 28.5.2 docker version

Additional context

$ docker version
Client: Docker Engine - Community
 Version:           28.5.2
 API version:       1.51
 Go version:        go1.25.3
 Git commit:        ecc6942
 Built:             Wed Nov  5 14:43:22 2025
 OS/Arch:           linux/amd64
 Context:           default

Server: Docker Engine - Community
 Engine:
  Version:          28.5.2
  API version:      1.51 (minimum version 1.24)
  Go version:       go1.25.3
  Git commit:       89c5e8f
  Built:            Wed Nov  5 14:43:22 2025
  OS/Arch:          linux/amd64
  Experimental:     false
 containerd:
  Version:          v1.7.29
  GitCommit:        442cb34bda9a6a0fed82a2ca7cade05c5c749582
 runc:
  Version:          1.3.3
  GitCommit:        v1.3.3-0-gd842d771
 docker-init:
  Version:          0.19.0
  GitCommit:        de40ad0

I upgraded a few days ago to Ubuntu 25.10:

$ uname -a
Linux yoga 6.17.0-6-generic #6-Ubuntu SMP PREEMPT_DYNAMIC Tue Oct  7 13:34:17 UTC 2025 x86_64 GNU/Linux

but it was failing on Ubuntu 25.04 before the upgrade. It used to work on Ubuntu 25.04 one or two weeks ago. I'm not sure what changed...

mflorea avatar Nov 10 '25 08:11 mflorea

I asked Copilot to analyze the issue and I got this response (pasting only relevant parts):

The issue is that the newer Chrome version (124) in the zenika/alpine-chrome:124 Docker image is trying to use the openat2 system call, which is not included in your current seccomp profile (chrome.json).

The error message specifically mentions:

openat2 fsmount:fscontext:proc/thread-self/fd/
function not implemented

This indicates that the openat2 system call is being blocked by the seccomp profile.

Why this happened:

  1. Chrome evolution: Chrome 124 introduced new security features that require additional system calls, particularly around the newer filesystem APIs (openat2, fsmount, etc.)
  2. System call additions: The openat2 system call was added to Linux kernel 5.6 (March 2020) and provides enhanced functionality over the traditional open/openat calls
  3. Container security: Modern Chrome versions are increasingly using newer kernel features for enhanced security and performance

Additional system calls you might need: The system calls I added (openat2, fsopen, fsconfig, fsmount, move_mount, fspick) are part of the newer Linux filesystem API that Chrome might use for:

  • Enhanced file access controls
  • Mount namespace operations
  • Improved security isolation

The suggested addition was:

{
    "name": "openat2",
    "action": "SCMP_ACT_ALLOW",
    "args": null
},
{
    "name": "fsopen",
    "action": "SCMP_ACT_ALLOW",
    "args": null
},
{
    "name": "fsconfig",
    "action": "SCMP_ACT_ALLOW",
    "args": null
},
{
    "name": "fsmount",
    "action": "SCMP_ACT_ALLOW",
    "args": null
},
{
    "name": "move_mount",
    "action": "SCMP_ACT_ALLOW",
    "args": null
},
{
    "name": "fspick",
    "action": "SCMP_ACT_ALLOW",
    "args": null
},

With this I got a different error:

docker: Error response from daemon: failed to create task for container: failed to create shim task: OCI runtime create failed: runc create failed: unable to start container process: error during container init: error closing exec fds: get handle to /proc/thread-self/fd: reopen fsmount:fscontext:proc/thread-self/fd/: check safety of /proc/thread-self/fd/3 magiclink: get root mount id: statx(STATX_MNT_ID_...) fsmount:fscontext:proc/: could not get mount id: operation not permitted: unknown

The analysis for this was:

The error shows that Chrome is now trying to use even more advanced system calls. The new error mentions statx with STATX_MNT_ID which is a newer variant of the stat family of system calls that provides extended file metadata including mount IDs.

System calls I added and why:

  1. statx - This is the key one causing your current error. It's an extended version of stat that can provide mount IDs and other advanced filesystem metadata. Chrome 124 is using this with the STATX_MNT_ID flag to get mount information.
  2. pidfd_open, pidfd_send_signal, pidfd_getfd - These are newer process file descriptor APIs that modern Chrome versions use for better process management and security.
  3. close_range - A more efficient way to close multiple file descriptors at once, which Chrome might use during process cleanup.
  4. epoll_pwait2 - An enhanced version of epoll_pwait with better timeout handling.

The suggested addition was:

{
    "name": "statx",
    "action": "SCMP_ACT_ALLOW",
    "args": null
},
{
    "name": "pidfd_open",
    "action": "SCMP_ACT_ALLOW",
    "args": null
},
{
    "name": "pidfd_send_signal",
    "action": "SCMP_ACT_ALLOW",
    "args": null
},
{
    "name": "pidfd_getfd",
    "action": "SCMP_ACT_ALLOW",
    "args": null
},
{
    "name": "close_range",
    "action": "SCMP_ACT_ALLOW",
    "args": null
},
{
    "name": "epoll_pwait2",
    "action": "SCMP_ACT_ALLOW",
    "args": null
}

With this last change I managed to start the container with the seccomp option and all my automated tests are passing again. Here's the final diff:

@@ -1530,6 +1530,66 @@
             "name": "writev",
             "action": "SCMP_ACT_ALLOW",
             "args": null
+        },
+        {
+            "name": "openat2",
+            "action": "SCMP_ACT_ALLOW",
+            "args": null
+        },
+        {
+            "name": "fsopen",
+            "action": "SCMP_ACT_ALLOW",
+            "args": null
+        },
+        {
+            "name": "fsconfig",
+            "action": "SCMP_ACT_ALLOW",
+            "args": null
+        },
+        {
+            "name": "fsmount",
+            "action": "SCMP_ACT_ALLOW",
+            "args": null
+        },
+        {
+            "name": "move_mount",
+            "action": "SCMP_ACT_ALLOW",
+            "args": null
+        },
+        {
+            "name": "fspick",
+            "action": "SCMP_ACT_ALLOW",
+            "args": null
+        },
+        {
+            "name": "statx",
+            "action": "SCMP_ACT_ALLOW",
+            "args": null
+        },
+        {
+            "name": "pidfd_open",
+            "action": "SCMP_ACT_ALLOW",
+            "args": null
+        },
+        {
+            "name": "pidfd_send_signal",
+            "action": "SCMP_ACT_ALLOW",
+            "args": null
+        },
+        {
+            "name": "pidfd_getfd",
+            "action": "SCMP_ACT_ALLOW",
+            "args": null
+        },
+        {
+            "name": "close_range",
+            "action": "SCMP_ACT_ALLOW",
+            "args": null
+        },
+        {
+            "name": "epoll_pwait2",
+            "action": "SCMP_ACT_ALLOW",
+            "args": null
         }
     ]
 }

I know very little about Linux system calls, so I would appreciate it if others review the change and tell me if there is something unsafe there. Thanks!

mflorea avatar Nov 10 '25 09:11 mflorea

This repo here seems dead. The last published image in the GHCR registry was Chrome 124 more than a year ago.

And now a new image shows up in the Docker Registry at https://hub.docker.com/layers/zenika/alpine-chrome/124/images/sha256-2911036299faade5a651adb4f54e989121022702b38307c25072f0600ed7d3db, and it requires changes to the seccomp profile?

What is going on here?

mpdude avatar Nov 12 '25 15:11 mpdude

@mpdude No support was given and I am no longer part of the company Zenika. Don't know what container is pushed now in the repo. DockerHub cannot move Repo from organization to another organization so it's complicated. From my side, I have no support and no access to Zenika DockerHub. Feel free to send an email to the Zenika DockerHub org to know what is the newly pushed content.

jlandure avatar Nov 15 '25 15:11 jlandure

I just pushed new info on the README. Please see https://github.com/jlandure/alpine-chrome/blob/master/README.md

jlandure avatar Nov 15 '25 15:11 jlandure

@jlandure 👌🏻. Thank you for the information, notice, response and the work you put into this in the past!

For me, it means I'll treat this newly pushed image as having unclear origins and not rely on it.

mpdude avatar Nov 16 '25 11:11 mpdude