修改物理机上的登录账号的权限,修改了/etc/sudoers以后,发现nova-compute工作异常了。日志中报错:
2016-05-17 10:49:00.202 18893 ERROR oslo_service.periodic_task File "/usr/lib/python2.7/site-packages/nova/utils.py", line 272, in execute
2016-05-17 10:49:00.202 18893 ERROR oslo_service.periodic_task return processutils.execute(*cmd, **kwargs)
2016-05-17 10:49:00.202 18893 ERROR oslo_service.periodic_task File "/usr/lib/python2.7/site-packages/oslo_concurrency/processutils.py", line 275, in execute
2016-05-17 10:49:00.202 18893 ERROR oslo_service.periodic_task cmd=sanitized_cmd)
2016-05-17 10:49:00.202 18893 ERROR oslo_service.periodic_task ProcessExecutionError: Unexpected error while running command.
2016-05-17 10:49:00.202 18893 ERROR oslo_service.periodic_task Command: sudo nova-rootwrap /etc/nova/rootwrap.conf chown 162 /var/lib/nova/instances/_base/78eab795858f16749531aa820efa700d1facb279
2016-05-17 10:49:00.202 18893 ERROR oslo_service.periodic_task Exit code: 1
2016-05-17 10:49:00.202 18893 ERROR oslo_service.periodic_task Stdout: u''
2016-05-17 10:49:00.202 18893 ERROR oslo_service.periodic_task Stderr: u'sudo: sorry, you must have a tty to run sudo/n'
是nova在执行命令行时sudo出错了。按道理说我只修改了登录账号的权限,nova用户是非登录账户(nologin),并且没在我修改的涉及范围之内,不应该受影响才对。为了搞清楚这个问题,研究了一下 nova-rootwrap 和 sudoers的相关知识。
rootwrap 主要用来做权限控制。在程序中,非 root 用户想执行需要 root 权限相关的命令时,用 rootwrap 来控制。openstack 里很多项目都用到了它,例如 nova、neutron、cinder。
举个常见的例子,假如在 nova-compute 的代码中想要执行一个底层的 mv 命令,由于 nova-compute 进程属于 nova 用户和用户组,在代码中直接调用 mv 可能会由于权限不够而出错。那么要成功地执行mv 有两种方式: 执行“ sudo mv ..." 和 执行” sudo nova-rootwrap /etc/nova/rootwrap.conf mv ...“ 。第一种方法看似简单但是没有安全地被管理。第二种方法经过了 nova-rootwrap,最终还是会调用 mv。nova-rootwrap 会维护一份它允许执行的命令清单,里面写了 nova 需要执行哪些命令和执行时需要的权限。我们要执行 mv 命令,nova-rootwrap 会检查 mv 命令是否在允许执行的清单中,如果允许执行,那么就以清单中事先写好的需要的用户权限执行。这样一来,如果我们私自改了 nova-compute 代码,使用 processutils.py 封装好的函数调用系统命令执行时,如果在 nova-rootwrap 管理的清单中没有允许这个命令执行,那么这段代码就会失败。这就相当于 nova-rootwrap 做了安全管理,用白名单过滤的方式。
nova-rootwrap 管理的白名单可以称之为 filter 。nova-rootwrap 的配置文件是 /etc/nova/rootwrap.conf ,在配置文件中能看到 filters 的存放路径。
filters_path=/etc/nova/rootwrap.d,/usr/share/nova/rootwrap
过滤规则放到这两个路径下 /etc/nova/rootwrap.d 和 /usr/share/nova/rootwrap。例如 nova compute 的规则: /usr/share/nova/rootwrap/compute.filters 。 过滤规则的写法看一下这个文件就明白了。过滤规则文件是包含在 nova-compute rpm 里的,安装好 rpm 包就有了。
根据官方文档的指示,同时也要求 nova 用户有权限执行 nova-rootwrap ,在 sudoers 中配置nova ALL = (root) NOPASSWD: /usr/bin/nova-rootwrap /etc/nova/rootwrap.conf *
openstack 其他项目的 rootwrap 与 nova-rootwrap 类似,配置文件的路径略有不同。rootwrap 更详细的介绍 https://wiki.openstack.org/wiki/Rootwrap
回到我们遇到的问题本身,日志中报错提示是 执行 cmd 失败了
Command: sudo nova-rootwrap /etc/nova/rootwrap.conf chown 162 /var/lib/nova/instances/_base/78eab795858f16749531aa820efa700d1facb279
那么这个命令就是去 chown 虚拟机的镜像文件,162 是 nova 用户 id 。检查 /usr/share/nova/rootwrap/compute.filters 文件,里面有 chown 命令的规则
chown: CommandFilter, chown, root
并没有问题。 问题应该不是出在 rootwrap 上。
手动执行命令来模拟 nova 中的调用:
sudo -u nova sudo nova-rootwrap /etc/nova/rootwrap.conf chown 162 /var/lib/nova/instances/_base/78eab795858f16749531aa820efa700d1facb279
前半句 sudo -u nova 是将手动执行的这个命令行在 nova 用户下执行,后半句 sudo nova-rootwrap ... 和程序中报错的 cmd 是完全一样的,要以 root 权限来执行 chown。执行上面的命令后提示要输入 nova 的密码(因为要 sudo 到 root)。此时就发现了疑点,查看 nova 的代码,出错的那段代码中并没有看到输入 nova 密码的逻辑。在一个正常的环境上也执行同样的指令,成功返回,不需要输入密码。 所以问题很可能出现在输入密码这个逻辑。
我们的问题的起因是修改 /etc/sudoers 来修改登录账号的权限,应该是这个改动导致了 nova 在 sudo 时需要输入密码。
在修改 /etc/sudoers 时,只是限制了登录账号的一些权限,例如使用 ssh 登录的账号 mubai ,并没有修改 nova 账号相关的功能。二者看似并没有联系。用 man sudoers 可以学习下 sudoers 的功能。
在仔细对比 /etc/sudoers 前后,发现修改时把 #includedir /etc/sudoers.d 这一行当成无用的注释给删除了。#include 和 #includedir 是 sudoers 中的固有语法。 sudo 程序在逐行读取 /etc/sudoers 时,如果遇到 #include 和 #includedir 会暂停执行后面的行,立即跳转到 include 文件和路径中的规则,执行完 include 中的规则后再返回原来的断点继续往下执行。 上面介绍 rootwrap 时曾提到,官方要求在sudoers 中 配置 nova 的 sudo 规则,规则文件是 /etc/sudoers.d/nova 。 由于修改 /etc/sudoers 文件时误把 #includedir 当成注释给删了,所以 /etc/sudoers.d/nova 规则就不生效了。
正是在 /etc/sudoers.d/nova 中设置了 nova 用户 sudo 时无需密码 “nova ALL = (root) NOPASSWD” ,所以如果 /etc/sudoers.d/nova 在执行 sudo 时没被读取,就出现了我们遇到的问题。
另外再提几个 sudoers 的小知识点:
1)sudoers 配置文件的写法可以参考 man sudoers 中的介绍,里面很详细。 我们就是踩到了 #includedir 的坑,以为 # 开头的都是注释。
2)/etc/sudoers.d/ 下面的配置文件是根据命名排序来决定执行顺序的,例如 /etc/sudoers.d/neutron 会比 /etc/sudoers.d/nova 先执行
3)sudoers 中如果同一个用户的规则重复出现,那么以后出现的为准,就是后面的会覆盖前面的,所以在 /etc/sudoers 和 /etc/sudoers.d/ 中的配置如果有冲突,要确认好生效顺序。
这个问题的原因是把 /etc/sudoers 中的 “ #includedir /etc/sudoers.d ” 一行误删了,导致写在 /etc/sudoers.d/nova 中的 sudo 规则不会生效,从而 nova 中执行 sudo 的地方都需要输入密码而失败。
在解决这个问题时,涉及到了 rootwrap 和 sudoers 的相关知识点。