一、引言

注意:setcap 属于和 sudo 同级别的危险操作,本质上都是获取 root 权限去做什么。不同的是,setcap 可以将影响范围控制在进程及其子进程,授权粒度限制在某几个特定权限。

二、准备用户

准备一个权限用户和一个普通用户

执行setcap命令必须拥有以下权限:

  • 该用户有执行setcap命令的权限
  • 该用户有想赋予进程权限的权限,即用户想赋予进程cap_dac_override(该权限可忽略dac对系统文件进程读写执行操作),他本身必须拥有该权限

普通用户

useradd user

三、配置Java环境

  1. 复制jdk至用户目录下并改变所属用户:用户组

    cp -r ./jdk8u352-b08 /home/user
    chown user:user -R /home/user/jdk8u352-b08
  2. 切换普通用户,设置环境变量

    # 切换普通用户
    su user
    
    # 编辑环境变量并使之生效
    vi ~/.bashrc
    export JAVA_HOME=/home/user/jdk8u352-b08
    export PATH=$JAVA_HOME/bin:$PATH
    export CLASSPATH=.:$JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar 
    source ~/.bashrc

    执行java -version并观察结果,设置成功结果如下

    openjdk version "1.8.0_352"
    OpenJDK Runtime Environment (Temurin)(build 1.8.0_352-b08)
    OpenJDK 64-Bit Server VM (Temurin)(build 25.352-b08, mixed mode)
  3. 行执行Java程序观察进程信息以方便后续做对照

    # 执行java进程
    java -jar xxxxx
    
    # 找到那条进程的pid 
    jps 
    
    # 查看cap权限
    cat /proc/$pid/status |grep -i cap
    # 主要是这三行,从上至下为cap继承集、cap最大集、cap有效集
    CapInh: 0000000000000000
    CapPrm: 0000000000000000
    CapEff: 0000000000000000

四、setcap赋权

注意:setcap对脚本文件不生效,例如以#!/bin/python或#!/bin/bash开头的文件。所以赋权的对象应当是二进制可执行文件
  1. 切换回赋权用户,并对Java二进制文件赋权

    # 退出普通用户登录
    exit
    
    # 将系统管理任务执行权 cap_system_admin
    # 忽略文件DAC访问权限 cap_dac_override
    # 进程信息追踪权 cap_sys_ptrace
    # 加入cap继承集、cap最大集、cap有效集
    setcap 'cap_system_admin,cap_dac_override,cap_sys_ptrace+eip' $JAVA_HOME/bin/java
    
    # getcap验证
    getcap $JAVA_HOME/bin/java
    # 结果
    /home/user/jdk8u352-b08/bin/java=cap_system_admin,cap_dac_override,cap_sys_ptrace+eip
  2. 赋权完毕后以普通用户执行java -version,如下

    su bes
    java -version
    # 结果
    java: error while loading shared libraries: libjli.so: cannot open shared object file: No such file or directory

    报java加载动态库错误,原因是当一个可执行文件提升了权限后,运行时加载程序(rtld)— ld.so,它不会与不受信任路径中的库链接。Linux 会为使用了 setcap 或 suid 的程序禁用掉 LD_LIBRARY_PATH。所以就出现了 java 程序加载不到 libjli.so 的情况了,这是 JDK 的一个 bug(见JDK-7157699:授予 posix 功能后无法运行java)。详细分析见解决setcap导致Java加载libjli.so 失败问题

  3. 把libjli.so链接加入受信库,并执行ldconfig。这一步需要root权限

    sudo ln -s $JAVA_HOME/jre/lib/amd64/jli/libjli.so /usr/lib/
    sudo ldconfig
  4. 重新执行java -version

    java -version
    # 结果
    openjdk version "1.8.0_352"
    OpenJDK Runtime Environment (Temurin)(build 1.8.0_352-b08)
    OpenJDK 64-Bit Server VM (Temurin)(build 25.352-b08, mixed mode)

五、验证

  1. 再次执行Java进程并查看进程cap权限

    CapInh: 0000000000000000
    CapPrm: 0000000000280002
    CapEff: 0000000000280002
  2. 执行命令验证

    # 执行解码
    capsh --decode=0000000000280002
    # 结果
    0x0000000000280002=cap_dac_override,cap_sys_ptrace,cap_sys_admin

可以看见该进程的cap最大集和有效集显示有赋予Java的三种权限了

六、思考

明明赋予Java二进制文件继承集权限,为什么启动的Java的进程的继承集没有呢?这么设计的目的是什么?

启动命令java -jar xxxxx这条进程有继承集、最大集合和有效集这三种权限,而它启动的子进程只有最大集合和有效集。

这样设计保证了权限只会在进程和它的子进程之间流转,避免权限泛滥。

七、参考文献

【blog】MAKE JAVA RUN ON PRIVILEGED PORTS IN CENTOS 7
【CSDN】Linux Capabilities机制
【博客园】解决setcap导致Java加载libjli.so 失败问题

最后修改:2026 年 09 月 14 日
如果觉得我的文章对你有用,请随意赞赏