You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Docker容器内ls -l显示文件权限等字段为问号的问题排查

问题背景

此前同类ls -l输出问号的问题多由权限问题导致,本次场景存在本质差异。

环境信息

已下线Docker宿主机环境

  • Kernel 3.10
  • docker 18.06
  • glibc 2.17
  • libseccomp 2.3.1
  • coreutils 8.22

SLES 15 Docker镜像环境

  • glibc 2.31
  • coreutils 8.32

故障现象与初步排查

使用命令docker run -it --rm -u root <docker-image> bash启动容器后,家目录下存在bin目录,执行ls可正常显示该目录,但执行ls -l时出现大量问号,输出如下:

$ ls
bin
$ ls -l
ls: cannot access 'bin': Operation not permitted
total 0
d????????? ? ? ? ?            ? bin

前期初步排查曾判断是宿主机3.10内核未实现statx系统调用(该调用在Linux 4.11版本正式加入,glibc 2.28版本才新增对应库支持,coreutils 8.32及以上版本的ls会优先调用该接口)导致,但后续测试发现,使用docker run -it --rm --security-opt seccomp=unconfined -u root <docker-image> bash关闭seccomp配置启动容器时,ls -l可正常运行,输出如下:

$ ls
bin
$ ls -l
total 0
drwxr-xr-x 2 abcuser abcuser 6 Jul  4 2022 bin

该现象说明问题与seccomp配置直接相关,但存在两个已知逻辑与现象矛盾:

  1. 公开提交记录显示statx早在Docker 18.04版本就已加入默认seccomp白名单,本次使用的Docker 18.06理应放行该系统调用
  2. coreutils实现逻辑中,若statx调用不可用,会自动回退使用stat系列系统调用,默认seccomp配置下本应正常运行
    通过strace抓取两种场景的系统调用轨迹,得到关键差异:
  • 默认seccomp配置下:statx调用返回-1 ENOSYS (Function not implemented)后,后续write、getdents64、close、fstat、openat等所有系统调用均返回ENOSYS错误,程序执行流程完全中断
  • 关闭seccomp配置下:statx调用同样返回ENOSYS错误,但后续会自动调用newfstatat等stat系列接口完成文件信息获取,流程正常执行
问题解答

1、为何使用默认seccomp配置时ls -l无法正常运行?

根因是宿主机搭载的旧版本libseccomp存在已知逻辑缺陷:

  • 当前宿主机的libseccomp版本为2.3.1,该版本未实现未知系统调用返回ENOSYS时的透传机制。虽然Docker 18.06的默认seccomp规则确实已经将statx加入了放行白名单,但白名单的放行逻辑只对内核真实存在的系统调用生效:当容器内程序调用3.10内核未实现的statx时,内核返回ENOSYS,旧版libseccomp不会将这个错误正常透传给用户态程序,反而会误判为进程非法调用了白名单外的系统调用,直接触发拦截逻辑,导致后续所有系统调用都无法正常执行,coreutils内置的statx失败回退逻辑根本没有机会运行。
  • 这个逻辑缺陷在libseccomp 2.4及以上版本才被修复,新版本libseccomp会识别内核返回的ENOSYS错误,直接透传给用户态程序,不会触发拦截。

2、底层内核未实现statx的场景下,为何关闭seccomp配置后ls -l可正常运行?

关闭seccomp配置后,系统调用路径上不存在seccomp过滤器的拦截篡改,所有返回值都会直接传递给用户态程序:

  1. 程序第一次调用statx时,内核因为自身未实现该系统调用,正常返回ENOSYS错误,这个错误被ls进程正常捕获
  2. coreutils 8.32的ls内置了兼容逻辑,捕获到statx调用失败的错误后,会自动回退调用newfstatat等在3.10内核中已经成熟支持的传统stat系列系统调用
  3. 这些传统系统调用没有被拦截,能正常返回文件的权限、属主、大小、时间戳等元数据,因此ls -l可以输出正常结果。

3、使用Docker默认seccomp配置时,如何在宿主机或容器内通过命令查询默认seccomp白名单包含的系统调用列表?

分宿主机、容器两个场景操作:

  • 宿主机侧查询(无需进入容器)
    Docker默认seccomp配置在不同发行版的存放路径略有差异,优先通过以下方式查询:
    1. 先查找默认seccomp配置文件路径,常见路径为/etc/docker/seccomp.json、/var/lib/docker/seccomp/default.json,找到后直接执行命令提取允许的系统调用:
      # 替换为环境中实际的seccomp配置文件路径,需提前安装jq工具
      jq -r '.syscalls[] | select(.action == "SCMP_ACT_ALLOW") | .names[]' /etc/docker/seccomp.json
      
    2. 如果找不到本地配置文件,可以启动一个临时特权容器,从容器内的Docker默认配置路径导出规则,再过滤放行的系统调用即可。
  • 容器内查询
    普通权限容器默认没有读取seccomp规则的权限,需要在启动容器时添加--cap-add SYS_ADMIN --security-opt apparmor=unconfined参数,进入容器后操作:
    1. 先安装seccomp相关工具,以SLES 15为例:
      zypper refresh
      zypper install -y libseccomp-tools strace
      
    2. 可以通过seccomp_dump工具直接导出当前进程生效的seccomp规则,也可以通过strace跟踪系统调用过程,过滤出被允许的系统调用列表。
      注意:如果容器启动时没有加对应权限,只能通过cat /proc/self/status | grep Seccomp看到seccomp的启用模式,无法获取完整的白名单内容。

内容的提问来源于stack exchange,提问作者Praveen Lobo

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.27 00:30:57