openstack root-wrap

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 -eq0 ]]; 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"
    exit1 fi
 # We're not **root**, make sure ``sudo`` is availableis_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
( umask226 && 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=stackelse
    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
        sudomkdir -p $NOVA_CONF_DIR
    fi
    sudochown $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
        sudorm -rf $NOVA_CONF_DIR/rootwrap.d
    fi
    # Deploy filters to /etc/nova/rootwrap.d    sudo mkdir -m 755 $NOVA_CONF_DIR/rootwrap.d
    sudocp $NOVA_DIR/etc/nova/rootwrap.d/*.filters $NOVA_CONF_DIR/rootwrap.d
    sudochown -R root:root $NOVA_CONF_DIR/rootwrap.d
    sudochmod 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/
    sudosed -e "s:^filters_path=.*$:filters_path=$NOVA_CONF_DIR/rootwrap.d:"-i $NOVA_CONF_DIR/rootwrap.conf
    sudochown root:root $NOVA_CONF_DIR/rootwrap.conf
    sudochmod 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
    chmod0440 $TEMPFILE
    sudochown root:root $TEMPFILE
    sudomv $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
  
 defexecute(*cmd,**kwargs):
    """Convenience wrapper around oslo's execute() method."""
    if'run_as_root' inkwargs and not 'root_helper' in kwargs:
        kwargs['root_helper']= _get_root_helper()
    returnprocessutils.execute(*cmd,**kwargs)

然後丟給processutils中的execute,查看代碼:

def execute(*cmd,**kwargs):
    ...
    ifrun_as_root andhasattr(os, 'geteuid') andos.geteuid() !=0:
        ifnot root_helper:
            raiseNoRootWrapSpecified(
                message=_('Command requested root, but did not '
                          'specify a root helper.'))
        cmd= shlex.split(root_helper)+ list(cmd)
  
    cmd= map(str, cmd)
  
    whileattempts > 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.

發佈了59 篇原創文章 · 獲贊 6 · 訪問量 10萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章