cgexec 无法继承 LD_PRELOAD 环境变量
原文链接 https://drmingdrmer.github.io/tech/cgroup/2018/08/16/cgexec.html
注:以下为加速网络访问所做的原文缓存,经过重新格式化,可能存在格式方面的问题,或偶有遗漏信息,请以原文为准。
<!-- mdtoc start -->
<!-- mdtoc end -->
问题: cgexec 会忽略掉 LD_PRELOAD
的环境变量
有时需要替换malloc库的时候,
我们会使用LD_PRELOAD=/usr/lib64/libtcmalloc.so.4 my_prog
方便地将运行的malloc库替换成tcmalloc.
但是如果使用cgroup来启动my_prog
, 这个环境变量就无法生效了.
验证这个问题, 可以通过一个 test_preload.sh
的脚本:
echo env:
env | grep LD # 显示是否继承了LD_PRELOAD的环境变量.
echo lsof:
lsof -p $$ | grep tcmalloc # 显示是否当前进程加载了tcmalloc
运行:
LD_PRELOAD=/usr/lib64/libtcmalloc.so.4 ./test_preload.sh
> env:
> LD_PRELOAD=/usr/lib64/libtcmalloc.so.4
> lsof:
> test_prel 7588 root mem REG 253,0 301136 36021701 /usr/lib64/libtcmalloc.so.4.4.5
通过cgexec运行时, 没有输出:
LD_PRELOAD=/usr/lib64/libtcmalloc.so.4 cgexec ./test_preload.sh
> env:
> lsof:
说明环境变量没有继承过来. 也没有被加载.
解决方案
使用cgexec时, 必须把环境变量的指定放到cgexec启动之后:
cgexec sh -c 'LD_PRELOAD=/usr/lib64/libtcmalloc.so.4 ./test_preload.sh'
> env:
> LD_PRELOAD=/usr/lib64/libtcmalloc.so.4
> lsof:
> test_prel 10059 root mem REG 253,0 301136 36021701 /usr/lib64/libtcmalloc.so.4.4.5
原因
可执行文件如果设置了capability属性或setuid属性(表示某些只有root级别才有的权限, 网卡抓包等), 就不允许LD_PRELOAD=tcmalloc.so 的环境变量来加载额外的lib, 因为安全性考虑, 不允许任意的代码跑到root身份上运行.
例子:
# ping是设置了网络相关的capability所以普通用户才能抓包.
getcap /bin/ping
> /bin/ping = cap_net_admin,cap_net_raw+p
# 用非root用户运行:
LD_PRELOAD=/usr/lib64/libtcmalloc.so.4 ping baidu.com
# 拿到pid 29841:
lsof -p 29841 | grep tcmalloc
# 啥也没有
# 用root用户运行同样的命令:
LD_PRELOAD=/usr/lib64/libtcmalloc.so.4 ping baidu.com
# 可以看到允许被加载tcmalloc
lsof -p 2256 | grep tcmalloc
> ping 2256 root mem REG 253,0 301136 36021701 /usr/lib64/libtcmalloc.so.4.4.5
同样不允许通过环境变量加载so的属性还有setuid;
setuid属性: 文件被执行时将自己的用户修改为文件所有者的用户, 而不是调用者的.
以及cgexec
How to use cgroup... 文中写的:
They are required to launch, then classify as the application makes use of
LD_LIBRARY_PATH
which is not available when running a binary with setuid (such as cgexec).
总之有机会把任意代码提升权限的事情都不会让做.
使用cgexec加LD_PRELOAD
需要这么做:
# 没有cgexec时:
LD_PRELOAD=/usr/lib64/libtcmalloc.so.4 ./test_preload.sh "a b" "c"
# 使用cgexec时:
cgexec sh -c 'LD_PRELOAD=/usr/lib64/libtcmalloc.so.4 ./test_preload.sh "a b" "c"'