https://wiki.openstack.org/wiki/Rootwrap
一.nova-rootwrap的作用
部署玩過openstack的都應該知道,它會生成一個nova用戶來管理所有服務.nova身份在linux中屬於普通用戶級別,避免了一些需要root身份運行的操作,提高linux系統的安全性.
但是openstack在實際過程中會調用很多外部命令,例如就network服務而言就有:`ip`,`ovs-vsctl`,`iptables`,`dnsmasq`,`brctl`等等,這些命令在linux中都是需要以root身份來運行的,如果是普通用戶通常的做法是在命令前加`sudo`切換到root身份再執行這些命令,但是這個方法在執行命令的時候需要輸入密碼確認操作,爲了避免輸入密碼,我們需要配置一下sudo.
建議的方法是在/etc/sudoers.d/目錄下新建一個文件,例如:
[root@localhost sudoers.d]# pwd /etc/sudoers.d
[root@localhost sudoers.d]# echo 'zhengtianbao ALL = (root) NOPASSWD: ALL' > stack_sh [root@localhost sudoers.d]# cat stack_sh zhengtianbao ALL = (root) NOPASSWD: ALL |
這樣當我們切換到’zhengtianbao’這個用戶的時候,只要在想執行的命令前加’sudo’,不需要輸入密碼就能以root身份運行.
[root@localhost sudoers.d]# su zhengtianbao [zhengtianbao@localhost sudoers.d]$ ls ls: cannot open directory .: Permission denied [zhengtianbao@localhost sudoers.d]$ sudo ls stack_sh |
關於sudoers的配置文件如何定義,這裏簡單介紹下:
通用格式:
user host run_as command
user:一位或幾位用戶,在/etc/group中可以用一個%代替它,組對象的名稱一定要用百分號%開頭.
host:一個或幾個主機名.
run_as:作爲哪個用戶運行,常見選項是root和ALL.
command:想讓用戶或組運行的一個或幾個根級別命令.
例如:
hans ALL=(root) useradd,userdel
授權hans用戶在所有計算機上以root身份運行useradd,userdel命令.
%smith ALL=(ALL) NOPASSWD:useradd,userdel
授權smith組全部成員在所有計算機上以所有用戶的身份運行useradd,userdel命令;且運行時不必輸入密碼.
一點點疑問:能否控制命令的參數呢?接下來做個測試:
[root@localhost sudoers.d]# echo 'zhengtianbao ALL = (root) NOPASSWD: /bin/ls -l, /bin/ls -a' > stack_sh [root@localhost sudoers.d]# su zhengtianbao [zhengtianbao@localhost sudoers.d]$ ls -a ls: cannot open directory .: Permission denied [zhengtianbao@localhost sudoers.d]$ sudo ls -a . .. stack_sh
[zhengtianbao@localhost sudoers.d]$ sudo ls -l total 8 -rw-r--r-- 1 root root 59 Jan 26 14:43 stack_sh [zhengtianbao@localhost sudoers.d]$ sudo ls -a -l [sudo] password for zhengtianbao: |
可見能夠控制的命令參數還是很嚴格的.
放在openstack中這也是可行的,但是隨着項目的增大,單純的修改sudoers影響了openstack的可維護性,因此引入了root warpper來管理命令權限相關的內容.
二.nova-rootwrap工作原理
如果是根據devstack來安裝openstack的話,查看devstack中的stack.sh裏面有關於root權限的內容:
# root Access
# -----------
# OpenStack is designed to be run as a non-root user; Horizon will fail to run # as **root** since Apache will not serve content from **root** user). # ``stack.sh`` must not be run as **root**. It aborts and suggests one course of # action to create a suitable user account. if
[[ $EUID - eq 0 ]];
then echo "You are running this script as root." echo "Cut it out." echo "Really." echo "If you need an account to run DevStack, do this (as root, heh) to create $STACK_USER:" echo "$TOP_DIR/tools/create-stack-user.sh" exit 1
fi # We're not **root**, make sure ``sudo`` is available is_package_installed
sudo || install_package
sudo |
好,它這裏表示需要先通過腳本tools/create-stack-user.sh來創建一個用戶,再通過那個用戶來執行,看腳本內容:
# Needed to get ``ENABLED_SERVICES`` source
$TOP_DIR /stackrc # Give the non-root user the ability to run as **root** via ``sudo`` is_package_installed
sudo || install_package
sudo if ! getent group $STACK_USER > /dev/null ; then echo "Creating a group called $STACK_USER" groupadd $STACK_USER fi if ! getent
passwd $STACK_USER > /dev/null ; then echo "Creating a user called $STACK_USER" useradd -g $STACK_USER -s
/bin/bash -d $DEST -m $STACK_USER
fi echo "Giving stack user passwordless sudo privileges" # UEC images ``/etc/sudoers`` does
not have a ``#includedir``, add one grep
-q "^#includedir.*/etc/sudoers.d"
/etc/sudoers || echo "#includedir /etc/sudoers.d"
>> /etc/sudoers ( umask 226 &&
echo "$STACK_USER ALL=(ALL) NOPASSWD:ALL"
\ > /etc/sudoers .d /50_stack_sh ) |
$STACK_USER的值在stackrc文件中定義,當前環境是root身份時則爲’stack’:
# Determine stack user if
[[ $EUID - eq 0 ]]; then STACK_USER=stack else STACK_USER=$( whoami ) fi |
ok,這裏發現它在/etc/sudoers.d/目錄下生成了一個50_stack_sh的文件,裏面的內容是:
stack ALL=(ALL) NOPASSWD:ALL
顯然它創建的stack用戶現在可以在使用`sudo`執行任何命令都能省略輸入密碼的過程了.
接下來繼續看nova的安裝過程,在devstack/lib/目錄下的nova腳本中有configure_nova()的方法,它會在stack.sh中被調用到,正如名字所示,它用來設置nova的config文件,創建一些數據等工作:
# configure_nova() - Set config files, create data dirs, etc function
configure_nova() { # Put config files in ``/etc/nova`` for everyone to find
if [[ ! -d $NOVA_CONF_DIR ]]; then sudo mkdir
-p $NOVA_CONF_DIR fi sudo chown
$STACK_USER $NOVA_CONF_DIR cp -p $NOVA_DIR /etc/nova/policy .json $NOVA_CONF_DIR configure_nova_rootwrap ... |
注意裏面的configure_nova_rootwrap,查看該方法:
# configure_nova_rootwrap() - configure Nova's rootwrap function
configure_nova_rootwrap() { # Deploy new rootwrap filters files (owned by root).
# Wipe any existing rootwrap.d files first
if [[ -d $NOVA_CONF_DIR /rootwrap .d ]]; then sudo rm
-rf $NOVA_CONF_DIR /rootwrap .d fi # Deploy filters to /etc/nova/rootwrap.d
sudo mkdir
-m 755 $NOVA_CONF_DIR /rootwrap .d sudo cp
$NOVA_DIR /etc/nova/rootwrap .d/*.filters $NOVA_CONF_DIR /rootwrap .d sudo chown
-R root:root $NOVA_CONF_DIR /rootwrap .d sudo chmod
644 $NOVA_CONF_DIR /rootwrap .d/* # Set up rootwrap.conf, pointing to /etc/nova/rootwrap.d
sudo cp
$NOVA_DIR /etc/nova/rootwrap .conf $NOVA_CONF_DIR/ sudo sed
-e "s:^filters_path=.*$:filters_path=$NOVA_CONF_DIR/rootwrap.d:" -i $NOVA_CONF_DIR /rootwrap .conf sudo chown
root:root $NOVA_CONF_DIR /rootwrap .conf sudo chmod
0644 $NOVA_CONF_DIR /rootwrap .conf # Specify rootwrap.conf as first parameter to nova-rootwrap
ROOTWRAP_SUDOER_CMD= "$NOVA_ROOTWRAP $NOVA_CONF_DIR/rootwrap.conf *" # Set up the rootwrap sudoers for nova
TEMPFILE=`mktemp` echo "$STACK_USER ALL=(root) NOPASSWD: $ROOTWRAP_SUDOER_CMD" >$TEMPFILE
chmod 0440 $TEMPFILE
sudo chown
root:root $TEMPFILE sudo mv
$TEMPFILE /etc/sudoers .d /nova-rootwrap } |
顯然,它在/etc/sudoers.d/目錄下創建了nova-rootwrap的文件,裏面的內容可能是:
nova ALL = (root) NOPASSWD: /usr/bin/nova-rootwrap /etc/nova/rootwrap.conf *
nova:指用戶名.
ALL:指主機名.
root:指運行用戶名.
NOPASSWD:指運行下面命令時不需要輸入密碼.
/usr/bin/nova-rootwrap /etc/nova/rootwrap.conf *:指能夠運行的命令.
上面的文件定義就是說:
以nova身份運行命令`sudo /usr/bin/nova-rootwrap /etc/nova/rootwrap.conf * `
時是不需要輸入密碼的,其中的’*’指的是任意字符串,例如:`ip route show …`.
/usr/bin/nova-rootwrap是一個可執行的腳本文件,/etc/nova/rootwrap.conf則是rootwrap相關的配置,裏面定義了filters-path所在路徑,以及缺省的可執行命令所在路徑,具體的過濾邏輯如下:
1)獲取要執行的命令,如:ip
2)通過filters-path加載配置文件中定義的可以執行的命令列表
3)判斷命令是否在可執行命令列表中
4)若在則通過python的subprocess模塊執行Popen方法;不在則給出錯誤信息,退出.
這些都可以在nova的bin/nova-rootwrap文件中查看.
三.nova中執行外部命令過程分析
所有的nova代碼在執行外部命令的時候都會用到execute函數,這個函數定義在nova頂層目錄下的utils.py模塊下.
例如:
from nova import utils
utils.execute(‘chmod’, ’777′, tmpdir, run_as_root=True)
execute函數首先根據run_as_root參數進行了一些處理,如下所示:
def
_get_root_helper(): return 'sudo nova-rootwrap %s'
% CONF.rootwrap_config
def execute( * cmd, * * kwargs): """Convenience wrapper around oslo's execute() method.""" if 'run_as_root'
in kwargs and
not 'root_helper'
in kwargs:
kwargs[ 'root_helper' ] =
_get_root_helper() return processutils.execute( * cmd, * * kwargs) |
然後丟給processutils中的execute,查看代碼:
def
execute( * cmd, * * kwargs): ... if run_as_root
and hasattr (os,
'geteuid' )
and os.geteuid() ! = 0 :
if not
root_helper: raise NoRootWrapSpecified(
message = _( 'Command requested root, but
did not ' 'specify a root helper.' )) cmd =
shlex.split(root_helper) +
list (cmd) cmd =
map ( str , cmd) while attempts >
0 : ... obj =
subprocess.Popen(cmd, stdin = _PIPE, stdout = _PIPE, stderr = _PIPE, close_fds = close_fds, preexec_fn = preexec_fn, shell = shell) ... |
最終執行的命令cmd就是`sudo /usr/bin/nova-rootwrap /etc/nova/rootwrap.conf chmod 777 tmpdir`
這裏`chmod`這個命令在rootwrap.d目錄下的filters文件中可以找到對應的配置:
chmod: CommandFilter, chmod, root
CommandFilter的定義是在oslo的rootwrap/filters.py中,裏面還定義了其他的filter(RegExp,Path,Kill,ReadFile,Ip,Env,Chaining,IpNetnsExec).
更加詳細的內容請查看nova/rootwarp目錄下的filters.py與wrapper.py.