一、引言
注意:setcap属于和sudo同级别的危险操作,本质上都是获取 root 权限去做什么。不同的是,setcap可以将影响范围控制在进程及其子进程,授权粒度限制在某几个特定权限。
二、准备用户
准备一个权限用户和一个普通用户
执行setcap命令必须拥有以下权限:
- 该用户有执行setcap命令的权限
- 该用户有想赋予进程权限的权限,即用户想赋予进程cap_dac_override(该权限可忽略dac对系统文件进程读写执行操作),他本身必须拥有该权限
普通用户
useradd user三、配置Java环境
复制jdk至用户目录下并改变所属用户:用户组
cp -r ./jdk8u352-b08 /home/user chown user:user -R /home/user/jdk8u352-b08切换普通用户,设置环境变量
# 切换普通用户 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)行执行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开头的文件。所以赋权的对象应当是二进制可执行文件
切换回赋权用户,并对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赋权完毕后以普通用户执行
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 失败问题把libjli.so链接加入受信库,并执行ldconfig。这一步需要root权限
sudo ln -s $JAVA_HOME/jre/lib/amd64/jli/libjli.so /usr/lib/ sudo ldconfig重新执行
java -versionjava -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)
五、验证
再次执行Java进程并查看进程cap权限
CapInh: 0000000000000000 CapPrm: 0000000000280002 CapEff: 0000000000280002执行命令验证
# 执行解码 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 失败问题