内容


充分发挥 sudo 的作用

Comments

简介

系统管理员可以通过 sudo 实用程序让用户或组能够作为另一个用户运行命令。换句话说,可以分派命令特权,而不需要另一个用户的密码。root 用户通过在 /etc/sudoers 文件中设置 sudo 条目完成这个过程。使用 visudo 命令编辑此文件。在分派特权时,必须相信得到特权的用户会慎重地使用它。这里要澄清一个误解:sudo 不仅用于让用户作为 root 用户运行某些命令;它主要用于让另一个用户作为应用程序用户/所有者运行应用程序或系统命令。如果系统上当前安装了 sudo,它不会覆盖现有的 sudoers 文件。但是,一定要保留 /etc/sudoers 的备份并阅读升级说明。

安装 sudo

下载 sudo 的最新版本。

对于本文,我使用 sudo version 1.7.2。如果您使用 AIX® 5.3,那么一定要有最新的 gcc 版本 (4.0.0)。

# export LIBPATH=/usr/lib
# ./configure --with-aixauth
# make
# make install

作为 root 用户使用以下命令检查安装的版本,查看构建选项和当前安装的配置:

# sudo -V 
Sudo version 1.7.2
Sudoers path: /etc/sudoers
Authentication methods: 'aixauth'

< rest of output truncated>

在默认情况下:

  • sudo 放在 /usr/local/bin 中。使用它作为另一个用户运行命令。
  • visudo 放在 /usr/local/sbin 中。使用它编辑 sudoers 文件。
  • sudoers 文件(如果还没有的话)放在 /etc 中。这个文件包含 sudo 条目。

sudoers 文件

/etc/sudoers(通常简称为 sudoers)文件控制谁可以使用 sudo 以及使用 sudo 运行什么。 root 用户或具有根特权的用户可以设置这些条目。sudoers 中 sudo 条目最基本的形式如下:

<user> <host> = <user to alias> <password required> < command to run>

作为某一用户运行以下命令,可以看到此用户能够使用 sudo 运行什么命令以及其他限制:

sudo -l

运行 sudo 命令的一般格式为:

sudo -u < user to run as> <command to run>

日志记录

通过在 /etc/syslog.conf 文件中使用以下设置,可以使用 syslog 在 /var/adm/messages 中记录通过 sudo 运行的所有命令:

*.debug   /var/adm/messages

但是,我认为 sudo 命令应该记录在一个单独的文件中,这样便于查看已经运行的 sudo 命令。当然,这也有助于监视失败的 sudo 活动。创建 /var/adm/sudo.log 文件,然后在 /etc/sudoers 文件中添加以下条目:

Defaults logfile=/var/adm/sudo.log
Defaults !syslog

现在,无论执行成功还是失败,所有 sudo 活动都会记录在 /var/adm/sudo.log 中。

管理 sudoers

随着时间的推移,sudoers 文件中的条目会越来越多。这是因为服务器上设置的应用程序环境越来越多,也可能因为进一步分派当前任务以隔离责任。随着条目增加,输入错误会越来越常见。让 root 用户更方便地管理 sudoers 文件是有积极意义的。我们来讨论两种实现这个目标(至少建立良好的标准)的方法。如果有许多静态条目(在有 sudo 的每台机器上都运行相同的命令),那么把这些命令放在一个单独的 sudoers 文件中,这可以使用 include 指令来实现。

如果有许多用户条目,在添加或删除条目时会耗时很长。对于这种情况,好做法是对用户进行分组。可以把用户直接分组在一起,这些组是有效的 AIX 组。

现在详细讨论这两种方法。

包含文件

在大型企业环境中,维护 sudoers 文件是一项重要且必须经常执行的任务。简化这项任务的一个解决方案是重新组织 sudoers 文件。一种做法是提取出静态或可重用的条目,它们在每台机器上都运行相同的命令。与审计/安全或 storix 备份或一般性能报告一样,现在可以对 sudo 使用 include 指令。主 sudoers 文件可以包含本地条目,包含文件包含静态条目,所以很少需要编辑。在调用 visudo 时,它会扫描 sudoers。当看到 include 条目时,它会扫描此文件,然后返回到主 sudoers 文件继续扫描。在从主 sudoers 文件退出 visudo 时,它会进入包含文件进行编辑。退出包含文件之后,返回到 AIX 提示。可以有多个包含文件,但是我不认为有必要这么做。

假设我们的辅助 sudoers 文件名为 sudo_static.<hostname>。在本文的示例中,使用的主机名是 rs6000。在主 sudoers 文件中,设置以下条目:

#include /etc/sudo_static.rs6000

接下来,在 /etc/sudo_static.rs6000 文件中添加一些条目。不必设置所有 sudoers 指令或小节。如果这个文件包含不必要的条目,就不要包含它们。例如,我的包含文件只包含以下文本。

bravo     rs6000 = (root) NOPASSWD: /usr/opt/db2_08_01/adm/db2licd -end
bravo     rs6000 = (root) NOPASSWD: /usr/opt/db2_08_01/adm/db2licd
bravo     rs6000 = (db2inst) NOPASSWD: /home/db2inst/sqllib/adm/db2start
bravo     rs6000 = (db2inst) NOPASSWD: /home/db2inst/sqllib/adm/db2stop force

在运行 visudo、保存并退出文件时,visudo 会让您按 Enter 开始编辑包含 sudoers 文件。与主文件一样,编辑此文件之后,sudo 会指出语法错误(如果有的话)。另外,可以使用

visudo -f /etc/sudo_static.rs6000

直接编辑包含文件。

使用组

属于有效的 AIX 组的用户可以包含在 sudoers 中,这样针对单一用户的条目更少,sudoers 文件更容易管理。在使用组重新组织 sudoers 条目时,可能需要在 AIX 中创建一个新的组,其中只包含允许使用 sudo 运行某些命令的用户。要想使用组,只需在条目前面加上前缀 ‘%’。假设您有 devopsdevuat 组,其中包含以下用户:

# lsgroup -f -a users devops

devops:
        users=joex,delta,charlie,tstgn

 # lsgroup -f -a users devuat
devuat:
        users=zebra,spsys,charlie

允许 devops 组作为 dbdftst 运行 /usr/local/bin/data_ext.sh 命令。

允许 devuat 组作为 dbukuat 运行命令 /usr/local/bin/data_mvup.sh 和 /usr/local/bin/data_rep.sh。

可以设置以下 sudoers 条目:

%devops rs6000 =  (dbdftst) NOPASSWD: /usr/local/bin/data_ext.sh
%devuat rs6000 =  (dbukuat) /usr/local/bin/data_mvup.sh
%devuat rs6000 =  (dbukuat) /usr/local/bin/data_rep.sh

注意,对于前面的条目,devops 组中的用户在执行 /usr/local/bin/data_ext.sh 时不会提示他们输入密码。但是,会提示 devuat 组中的用户输入密码。用户 “charlie” 是这两个组(devopsdevuat)的成员,所以他可以执行以上所有命令。

对 sudo 使用超时

sudo 的一个特性使用时间票据判断自从最后一次运行 sudo 命令以来已经过了多长时间。在指定的时间段内,用户可以重新运行命令而不会提示输入密码(此用户自己的密码)。超过这段时间之后,用户必须再次输入密码才能重新运行命令。如果用户提供了正确的密码,就执行命令,票据复位,计时重新开始。如果在 sudoers 中此用户的条目中有 NOPASSWD,票据特性是无效的。默认的超时是 5 分钟。如果希望修改默认值,只需在 sudoers 中添加一个条目。例如,可以使用以下条目把用户 “bravo” 运行任何命令的超时值设置为 20 分钟:

Defaults:bravo timestamp_timeout=20

作为用户使用以下命令销毁票据:

$ sudo -k

销毁票据之后,在运行 sudo 命令时会再次提示用户输入密码。

请不要设置所有用户的超时值,因为这会造成问题,尤其是在运行花费时间比较长的作业集合时。在 timestamp_timeout 变量中使用 -1 值可以禁用这个特性。时间票据是 /var/run/sudo 中包含用户名的目录条目。

变量

正如前面提到的,sudo 会缩减系统变量,这可能有危险。可以使用 sudo -V 检查哪些变量保持不变,哪些变量会被缩减。输出显示保持不变和缩减的变量列表。缩减 LIBPATH 显然很不方便。有两个解决方法 —— 编写一个包装器脚本或在命令行上指定环境。先看一下包装器脚本解决方案,假设您的一个应用程序会停止或启动 DB2® 实例。可以编写一个脚本来保持变量不变。在 清单 1. rc.db2 中,使用以下代码访问实例配置文件,进而导出各个 LIBPATH 和 DB2 环境变量,这会保持环境变量不变:

 . /home/$inst/sqllib/db2profile

sudoers 中执行此脚本的条目如下,它们不会缩减任何系统环境变量:

bravo     rs6000 = (dbinst4) NOPASSWD: /home/dbinst4/sqllib/adm/db2start
bravo     rs6000 = (dbinst4) NOPASSWD: /home/dbinst4/sqllib/adm/db2stop force
bravo     rs6000 = (dbinst4) NOPASSWD: /usr/local/bin/rc.db2 stop db2inst4
bravo     rs6000 = (dbinst4) NOPASSWD: /usr/local/bin/rc.db2 start db2inst4

在这个示例中,用户 “bravo” 可以作为用户 “dbinst4” 执行以上命令。通常,这个用户会运行:

sudo -u dbinst4 /usr/local/bin/rc.db2 stop db2inst4
sudo -u dbinst4 /usr/local/bin/rc.db2 start db2inst4
清单 1. rc.db2
#!/bin/sh
# rc.db2
# stop/start db2 instances

# check to see if db2 inst is runningdb2_running(){state=`ps -ef |grep db2sysc 
                    |grep -v grep| awk '$1=="'${inst}'" { print $1 }'`
if [ "$state" = "" ] 
then 
    return 1 
else 
    return 0 
fi}

usage ()

{
echo "`basename $0` start | stop <instance>"
}

# stop db2 
stop_db2 ()
{
echo "stopping db2 instance as user $inst" 
    if [ -f /home/$inst/sqllib/db2profile ]; then 
        . /home/$inst/sqllib/db2profile 
else 
    echo "Cannot source DB2..exiting" 
exit 1
fi 
    /home/$inst/sqllib/adm/db2stop force
}

# start db2
start_db2 ()
{
echo "starting db2 instance as user $inst" 
    if [ -f /home/$inst/sqllib/db2profile ]; then 
        . /home/$inst/sqllib/db2profile 
else 
    echo "Cannot source DB2..exiting" 
exit 1
fi
/home/$inst/sqllib/adm/db2start
} 

# check we get 2 params
if [ $# != 2 ]
then 
    usage 
    exit 1
fi

inst=$2 

case "$1" in 
Start|start) 
    if db2_running  
        then  
        echo "db2 instance $inst appears to be already running"
         exit 0 
    else 
        echo " instance not running as user $inst..attempting to start it" 
        start_db2 $inst

        fi
        ;; 
Stop|stop) 
    if db2_running 
        then 
        echo "instance running as $inst..attempting to stop it"
        stop_db2 $inst 
    else 
        echo "db2 instance $inst appears to be not running anyway" 
        exit 0 
    fi 
    ;; 
*) usage  
;;
esac

保留系统环境变量的另一种方法是在 sudoers 中使用 Defaults !env_reset 指令:

Defaults !env_reset

然后,在命令行上指定环境变量名和值:

$ sudo LIBPATH=″/usr/lib:/opt/db2_09_05/lib64″ -u delta /usr/local/bin/datapmp

如果没有设置 !env_reset 条目,那么在试图运行命令时会从 sudo 收到以下错误:

sudo: sorry, you are not allowed to set the following environment variables: LIBPATH

如果发现 sudo 还会缩减其他环境变量,那么可以在 sudoers 中指定变量名(使用 Defaults env_keep += 指令),让 sudo 保持这些变量不变。例如,假设对于我的一个使用 sudo 的脚本,sudo 会缩减应用程序变量 DSTAGE_SUP 和 DSTAGE_META。为了保留这些变量,可以在 sudoers 中设置以下条目:

Defaults env_keep += "DSTAGE_SUP"
Defaults env_keep += "DSTAGE_META"

注意,我指定了变量名,但是没有指定变量值。值已经包含在我的脚本中,比如:

export DSTAGE_SUP=/opt/dstage/dsengine; export DSTAGE_META=/opt/dstage/db2

现在,在执行这个脚本时,这两个环境变量会保持不变。

保护 sudo 路径

可以使用 secure_path 指令修改 sudoers 中默认的 PATH。这个指令指定当用户执行 sudo 命令时在什么地方寻找二进制代码和命令。这个选项的目的显然是要限制用户运行 sudo 命令的范围,这是一种好做法。在 sudoers 中使用以下指令,指定安全 PATH 及其搜索目录:

Defaults secure_path="/usr/local/sbin:/usr/local/bin:/opt/freeware/bin:/usr/sbin"

实施限制

可以对用户能够运行的命令实施限制。假设有一个名为 dataex 的组,其成员是 “alpha”、“bravo” 和 “charlie”。现在,已经允许这个组运行 sudo 命令 /usr/local/bin/mis_ext *,这里的星号代表传递给脚本的许多参数。但是,如果参数是 import,就不允许用户 “charlie” 执行此脚本。可以使用逻辑否 '!' 操作符设置这种条件。下面是在 sudoers 中的实现方法:

 %dataex rs6000 = (dbmis) NOPASSWD: /usr/local/bin/mis_ext *
 charlie rs6000 = (dbmis) NOPASSWD: !/usr/local/bin/mis_ext import

注意,逻辑否操作符条目出现在无限制的条目后面。可以在同一行上应用多个逻辑否条目;只需以逗号分隔它们,比如:

charlie rs6000 = (dbmis) NOPASSWD: /usr/local/bin/aut_pmp *

charlie rs6000 = (dbmis) NOPASSWD: !/usr/local/bin/aut_pmp create,
!/usr/local/bin/aut_pmp delete, !/usr/local/bin/aut_pmp amend

提交 sudo 命令

在企业环境中,把 sudo 命令提交给远程主机最好是作为 root 用户使用 ssh 脚本来完成,对于无密码登录,还应该在主机之间交换密钥。我们来看一个示例。对于地理上远程的机器,如果遇到某种硬件问题(磁盘或内存),IBM® 工程师会到现场更换发生故障的硬件。在这种情况下,他们需要有 root 密码才能完成工作。一种实现方法是让工程师必须使用 sudo 访问 root 用户。在工程师访问站点之前告诉他们密码是有好处的。清单 2 演示提交这种配置的一种方法。仔细看看 清单 2,这里使用一个 for 循环包含要提交配置的主机列表。(但是,一般情况下,把这些主机名放在一个文本文件中并使用 while 循环读取它们。)使用 ‘here’ 文档方法建立 sudoers 的备份,然后在 sudoers 中添加下面的条目:

# -- ibmeng sudo root
ibmeng host1 = (root) NOPASSWD:ALL

接下来,创建用户 “ibmeng”,使用 chpasswd 设置此用户的密码。在这个示例中,密码是 ibmpw。然后在此用户的配置文件中添加一条消息,告诉用户如何使用 sudo 访问 root 用户。因此,在工程师登录时,他会看到以下消息:

IBM Engineer, to access root account type: sudo -u root su -

当然,在工程师离开之后,应该锁住 ibmeng 账户。

清单 2. dis_ibm
#!/bin/sh
# dis_ibm
dest_hosts='host1 host2 host3 host4'

for host in $dest_hosts
do
echo "doing [$host]"

$ssh -T -t -l root $host<<'mayday'
host=`hostname`
cp /etc/sudoers /etc/sudoers.bak

    if [ $? != 0 ]
    then
    echo "error: unable to cp sudoers file"
    exit 1
    fi
echo "# -- ibmeng sudo root\nibmeng $host = (root) NOPASSWD:ALL">>/etc/sudoers

mkuser su=false ibmeng
if [ $? = 0 ]
    then
    echo "ibmeng:ibmpw" | chpasswd -c
    else
    echo "error: unable to create user ibmeng and or passwd"
    exit 1
fi
    chuser gecos='IBM engineer acc' ibmeng
if [ -f /home/ibmeng/.profile ]
    then
    echo "echo \"IBM Engineer, to access root account type: sudo -u root su -"\"
>>/home/ibmeng/.profile
fi
mayday
done

结束语

sudo 允许您控制谁可以作为谁运行什么命令。但是,必须充分理解 sudoers 的特性,才能完全了解它的影响和责任。


相关主题

  • 进一步了解并下载 sudo
  • 下载 IBM 产品评估版 或者在 IBM SOA Sandbox 中在线试用这些来自 DB2®、Lotus®、Rational®、Tivoli® 和 WebSphere® 的应用程序开发工具和中间件产品。
  • AIX and UNIX 专区:developerWorks 的“AIX and UNIX 专区”提供了大量与 AIX 系统管理的所有方面相关的信息,您可以利用它们来扩展自己的 UNIX 技能。
  • AIX and UNIX 新手入门:访问“AIX and UNIX 新手入门”页面可了解更多关于 AIX 和 UNIX 的内容。
  • AIX and UNIX 专题汇总:AIX and UNIX 专区已经为您推出了很多的技术专题,为您总结了很多热门的知识点。我们在后面还会继续推出很多相关的热门专题给您,为了方便您的访问,我们在这里为您把本专区的所有专题进行汇总,让您更方便的找到您需要的内容。
  • developerWorks 技术活动网络广播:随时关注 developerWorks 技术活动和网络广播。

评论

添加或订阅评论,请先登录注册

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=10
Zone=AIX and UNIX
ArticleID=449088
ArticleTitle=充分发挥 sudo 的作用
publish-date=11232009