跳转至内容

Sudo

来自 ArchWiki

Sudo 允许系统管理员委派权限,使特定用户(或用户组)能够以 root 或其他用户身份运行命令,同时提供命令及其参数的审计追踪。

Sudo 是以 root 身份运行命令时 su 的替代方案。与 su 不同(su 会启动一个允许后续所有命令拥有 root 权限的 root shell),sudo 为单个命令授予临时的权限提升。通过仅在需要时启用 root 权限,使用 sudo 可以降低因输入错误或所调用命令中的 bug 导致系统损毁的可能性。

Sudo 还可以用于以其他用户身份运行命令;此外,sudo 会将所有命令和失败的访问尝试记录到 日志 (journal) 中,以便进行安全审计。

安装

安装 sudo 软件包。

或者,安装 sudo-rs 软件包,它是 sudo 的内存安全实现,不过具有一些限制

用法

要开始以非特权用户身份使用 sudo,必须进行正确配置。参见 #配置

要使用 sudo,只需在命令及其参数前加上 sudo 和一个空格。

$ sudo cmd

例如,使用 pacman:

$ sudo pacman -Syu

更多信息请参见 sudo(8)

登录 shell

你不能仅仅通过在命令前加 sudo 就在另一个用户下运行所有命令。特别是在使用 重定向命令替换 时,你必须使用登录 shell,可以通过 sudo -iu user 轻松访问(如果目标用户是 root,可以省略 -u user)。

在以下示例中,命令替换在完整的 shell 中可以工作,但在前面加 sudo 时会失败:

$ sudo wpa_supplicant -B -i interface -c <(wpa_passphrase MYSSID passphrase)
Successfully initialized wpa_supplicant
Failed to open config file '/dev/fd/63', error: No such file or directory
Failed to read or parse configuration '/dev/fd/63'

配置

sudo 的主要配置文件是 /etc/sudoers务必使用 visudo(8) 命令进行编辑。它可用于设置别名(本质上是变量)、更改默认行为以及指定哪些用户可以运行哪些命令。有关更多信息,请参阅 sudoers(5)

sudo 还会解析 /etc/sudoers.d/ 目录中包含的文件。这意味着您可以更改设置并将其放在独立文件中,而不是直接编辑 /etc/sudoers。这样做有两个优点:

  • 无需合并 sudoers.pacnew 文件;
  • 如果新条目有问题,你可以删除该问题文件而不是编辑 /etc/sudoers(但请参阅下面的警告)。

这些插入式文件中的条目格式与 /etc/sudoers 本身相同。要直接编辑它们,请使用 visudo /etc/sudoers.d/somefile。有关详细信息,请参阅 sudoers(5) § 从 sudoers 中包含其他文件

/etc/sudoers.d/ 目录中的文件按字典顺序解析,包含 .~ 的文件名会被跳过。为了避免排序问题,文件名应以两位数字开头,例如 10-foo

注意: 补充文件中的条目顺序很重要:确保语句不会相互覆盖。
警告 /etc/sudoers.d/ 中的文件与 /etc/sudoers 本身一样脆弱:任何格式错误的文件都会导致 sudo 无法工作。因此,出于同样的原因,强烈建议使用 visudo

默认骨架 (Defaults skeleton)

正如 sudoers(5) § Defaults 中所解释的,许多行为“配置选项可以在运行时通过一个或多个 Default_Entry 行从其默认值更改”。sudoers(5) § SUDOERS 选项 列出了所有可以在配置中通过 Defaults 命令使用的选项。

查看当前设置

运行 sudo -ll 打印当前的 sudo 配置,或运行 sudo -lU user 查看特定用户的配置。

使用 visudo

visudo 会锁定指定文件(默认为 /etc/sudoers),将编辑内容保存到临时文件,并在写入前检查语法错误。

警告
  • sudoers 绝对不能有语法错误!任何错误都会导致 sudo 无法使用。务必使用 visudo 进行编辑以防止错误。
  • visudo(8) 警告说,将 visudo 配置为遵循用户环境变量中选择的编辑器可能存在安全漏洞,因为它允许拥有 visudo 权限的用户只需将该变量设置为其他内容,即可在不留日志的情况下以 root 身份运行任意命令。

visudo 的默认编辑器是 visudo 软件包编译时带有 --with-env-editor 选项,并支持使用 SUDO_EDITORVISUALEDITOR 变量。当设置了 VISUAL 时,不会使用 EDITOR

要在当前 shell 会话期间将 nano 设为 visudo 编辑器,请导出 EDITOR=nano;若只想使用一次不同的编辑器,只需在调用 visudo 之前设置变量:

# EDITOR=nano visudo /etc/sudoers.d/10-editor

要永久更改编辑器,请参阅 环境变量#针对用户

仅针对 visudo 永久更改系统范围内的编辑器(例如使用 rnano(1),即 nano 的受限模式):

/etc/sudoers.d/10-editor
# Set default EDITOR to restricted version of nano, and do not allow visudo to use EDITOR/VISUAL.
Defaults      editor=/usr/bin/rnano, !env_editor

或者,您可以编辑文件副本,然后使用 visudo -c /copy/of/file 进行检查。如果您想绕过 visudo 对文件的锁定,这可能会派上用场。

示例条目

若要允许 archie 用户在单台机器(hostname)上以任何用户和组的身份运行所有命令(只要他们在命令前加上 sudo),请创建:

/etc/sudoers.d/90-archie
archie   hostname=(ALL:ALL) ALL

若要允许 wheel 组成员拥有所有访问权限:

/etc/sudoers.d/10-wheel
%wheel      ALL=(ALL:ALL) ALL
提示: 在创建新的管理员时,通常希望为 wheel 组启用 sudo 访问权限并将用户添加到该组,因为默认情况下 Polkitwheel 组的成员视为管理员。如果用户不是 wheel 组的成员,使用 Polkit 的软件可能会要求使用 root 密码而不是用户密码进行验证。

若要禁用 archie 用户的密码询问:

警告: 这将允许任何以你的用户名运行的进程无需询问许可即可使用 sudo。
/etc/sudoers.d/90-archie_nopasswd
Defaults:archie      !authenticate

仅为 archie 用户在 hostname 上启用特定定义的命令且无需密码:

/etc/sudoers.d/90-archie_commands
archie hostname= NOPASSWD: /usr/bin/halt,/usr/bin/poweroff,/usr/bin/reboot,/usr/bin/pacman -Syu
注意 最定制化的选项应放在文件末尾(或在插入式文件名中使用靠后的字典顺序),因为后面的行会覆盖前面的行。特别是,如果您的用户属于 %wheel 组,则该行应位于 %wheel 行之后。

详细的 sudoers 示例位于 /usr/share/doc/sudo/examples/sudoers。否则,请参阅 sudoers(5) 获取详细信息。

Sudoers 默认文件权限

sudoers 文件的所有者和组都必须是 0(root)。文件权限必须设置为 0440。这些权限是默认设置的,但如果你不小心更改了它们,应立即改回,否则 sudo 将失效。

# chown -c root:root /etc/sudoers
# chmod -c 0440 /etc/sudoers

通过 SSH 运行命令

SSH 在运行远程命令时默认不会分配 tty。没有分配 tty 时,sudo 无法防止密码显示出来。您应该使用 ssh 的 -t 选项强制分配 tty,并设置 Defaults 选项 requiretty,以仅允许拥有 tty 的用户运行 sudo。

/etc/sudoers.d/10-requiretty
# Disable "ssh hostname sudo <cmd>", because it will show the password in clear text. You have to run "ssh -t hostname sudo <cmd>".
#
Defaults    requiretty
Defaults    cmddenial_message="Usage without a TTY is not allowed: run 'ssh -t'!"

宽松的 umask

Sudo 会将用户的 umask 值与其自身的 umask(默认为 0022)合并。这可以防止 sudo 创建比用户 umask 允许的权限更开放的文件。虽然如果没有使用自定义 umask,这是一个合理的默认设置,但这可能导致 sudo 运行的工具创建的文件权限与直接由 root 运行不同。如果因此出现错误,sudo 提供了一种修复 umask 的方法,即使所需的 umask 比用户指定的 umask 更宽松:

/etc/sudoers.d/10-umask_override
Defaults umask = 0022
Defaults umask_override

这将 sudo 的 umask 设置为 root 的默认 umask (0022) 并覆盖默认行为,始终使用指定的 umask,无论用户设置了什么 umask。

技巧与提示

减少输入密码的次数

如果你对每 5 分钟(默认值)就需要重新输入一次密码感到厌烦,可以通过为 timestamp_timeout 设置一个较长的值(以分钟为单位)来更改:

/etc/sudoers.d/10-timestamp_timeout
Defaults timestamp_timeout=10
提示 要始终要求身份验证,请将值设为 0
警告 将该值设为负数会将超时设为无限。将其与 global 时间戳结合使用,将允许任何进程使用您的 sudo 会话,直到您注销或重启系统。sudo-rs 明确不允许将该值设为负数。

如果你在长脚本中使用 sudo 命令,并且不想在超时过期时等待用户输入,可以在循环中单独运行 sudo -v 来刷新超时(而 sudo -K 会立即撤销它)。

禁用密码提示符超时

一个常见的烦恼是,在后台终端运行的长时进程平时以普通权限运行,仅在需要时提升权限。这会导致 sudo 密码提示符出现后未被注意到并超时,此时进程会死掉,所做的工作也会丢失,或者充其量只是被缓存了。常见的建议是启用免密码 sudo,或者延长 sudo 记住密码的超时时间。这两者都有负面的安全影响。提示符 (prompt) 超时也可以禁用,由于这不具备任何合理安全目的,它应该是此处的解决方案:

/etc/sudoers.d/10-passwd_timeout
Defaults passwd_timeout=0

传递别名 (Passing aliases)

以下内容仅在 bash completion 不可用时(无论是完整的还是如上所述简化的)才相关:ZshBash 中的别名通常仅在命令的第一个单词展开。这意味着在运行 sudo 命令时,你的别名通常不会被展开。使下一个单词展开的一种方法是为 sudo 创建一个以空格结尾的别名。将以下内容添加到你的 shell 配置文件中:

alias sudo='sudo '

zshmisc(1) § ALIASING 描述了其工作原理:

如果替换文本以空格结尾,则 shell 输入中的下一个单词始终符合别名展开的目的。

以及 bash(1) § ALIASES

如果别名值的最后一个字符是空格,则别名后面的下一个命令单词也会被检查是否进行别名展开。

在密码提示符中添加终端铃声

为了吸引用户注意后台终端中的 sudo 提示符,用户可以简单地让它回显一个 铃声字符 (bell character)

/etc/sudoers.d/10-passprompt
Defaults passprompt="^G[sudo] password for %p: "

注意 ^G 是一个字面铃声字符。例如在 vim 中,使用序列 Ctrl+v Ctrl+g 插入。如果 Ctrl+v 被映射(例如用于粘贴),通常可以使用 Ctrl+q 代替。在 nano 中,使用 Alt+v Ctrl+g

另一个选项是设置 SUDO_PROMPT 环境变量。例如,将以下内容添加到你的 shell 配置文件中:

export SUDO_PROMPT=$'\a[sudo] password for %p: '

允许保留本地化提示的替代方案是在您的 shell 配置文件中添加别名:

alias sudo='sudo -B'

禁用分终端 sudo

如果您对 sudo 每次打开新终端时都要输入密码的默认行为感到厌烦,可以将 timestamp_type(默认为 tty)更改为 ppid,这样所有具有相同父进程 ID 的进程将共享 5 分钟的超时时间。这对于 终端复用器 或带标签页的图形终端模拟器特别有用。另一个选项是将其设为 global

警告 使用 global 时间戳将允许任何进程使用您的 sudo 会话。
/etc/sudoers.d/10-timestamp_type
Defaults timestamp_type=ppid

环境变量

如果你有很多环境变量,或者你通过 export http_proxy="..." 导出了代理设置,使用 sudo 时这些变量不会传递给 root 账户,除非你使用 -E/--preserve-env 选项运行 sudo。

$ sudo -E pacman -Syu

推荐的保留环境变量的方法是将它们追加到 env_keep

/etc/sudoers.d/10-env_keep
Defaults env_keep += "ftp_proxy http_proxy https_proxy no_proxy"

Root 密码

用户可以配置 sudo 询问 root 密码而不是用户密码,方法是在 /etc/sudoers 的 Defaults 行中添加 targetpw(目标用户,默认为 root)或 rootpw

/etc/sudoers.d/10-targetpw
Defaults targetpw

为了防止向普通用户泄露 root 密码,你可以将其限制在特定组:

/etc/sudoers.d/10-targetpw
Defaults:%wheel targetpw
%wheel ALL=(ALL) ALL

禁用 root 登录

用户可能希望禁用 root 登录。没有 root 账户,攻击者必须首先猜出配置为 sudoer 的用户名以及该用户的密码。参见例如 OpenSSH#Deny

警告
  • 请小心,禁用 root 登录可能会导致你被锁在系统外。Sudo 不是自动安装的,其默认配置既不允许无密码 root 访问,也不允许使用你自己的密码进行 root 访问。在禁用 root 账户之前,请确保某个用户已正确配置为 sudoer!
  • 如果你已经更改了 sudoers 文件以将 rootpw 作为默认设置,那么请不要使用以下任何命令禁用 root 登录!
  • 如果你已经被锁在外面,请参阅 密码恢复 获取帮助。

账户可以通过 passwd 锁定:

# passwd -l root

类似的命令可以解锁 root:

$ sudo passwd -u root

或者,你可以使用以下命令删除密码,然后锁定 root 账户: 

$ sudo passwd -dl root

再次启用 root 登录:

$ sudo passwd root

注意,这仅仅是禁用了基于密码的登录。用户仍可能能够使用其他身份验证令牌(例如 SSH 密钥)登录。要彻底禁用该账户,请使用:

$ usermod --expiredate 1 root

本文或本章节的准确性存在争议。

原因: 在大多数情况下,当用户最终进入紧急 shell 时,他们使用的是 initramfs,除非添加到 mkinitcpio 配置 中的 FILES,否则不会使用以下配置。(在 Talk:Sudo 中讨论)。

在系统紧急情况下,恢复提示符会要求你输入 root 密码,从而导致无法登录恢复 shell。要在紧急情况下自动解锁 root 账户,请使用 补充文件 (drop-in file)SYSTEMD_SULOGIN_FORCE=1 环境变量添加到 rescue.service

/etc/systemd/system/rescue.service.d/SYSTEMD_SULOGIN_FORCE.conf
[Service]
Environment=SYSTEMD_SULOGIN_FORCE=1
提示: 要获取交互式 root 提示符,即使在禁用 root 账户后,也可以使用 sudo -i

kdesu

kdesu 可在 KDE 下使用,以 root 权限启动 GUI 应用程序。默认情况下,kdesu 可能会尝试使用 su,即使 root 账户已被禁用。幸运的是,可以告诉 kdesu 使用 sudo 代替 su。创建/编辑文件 ~/.config/kdesurc

[super-user-command]
super-user-command=sudo

或者使用以下命令:

$ kwriteconfig6 --file kdesurc --group super-user-command --key super-user-command sudo

Sudo 加固示例

假设你创建了 3 个用户:admin、devel 和 archie。用户 "admin" 用于 journalctl、systemctl、mount、kill 和 iptables;"devel" 用于安装软件包和编辑配置文件;而 "archie" 是你登录使用的用户。要让 "archie" 能够重启、关机和使用 netctl,我们将执行以下操作:

编辑 /etc/pam.d/su/etc/pam.d/su-l。要求用户必须在 wheel 组中,但不要将任何人放入该组。

#%PAM-1.0
auth            sufficient      pam_rootok.so
# Uncomment the following line to implicitly trust users in the "wheel" group.
#auth           sufficient      pam_wheel.so trust use_uid
# Uncomment the following line to require a user to be in the "wheel" group.
auth            required        pam_wheel.so use_uid
auth            required        pam_unix.so
account         required        pam_unix.so
session         required        pam_unix.so

将 SSH 登录限制为 'ssh' 组。只有 archie 会成为该组成员。

# groupadd -r ssh
# gpasswd -a archie ssh
# echo 'AllowGroups ssh' >> /etc/ssh/sshd_config

重启 sshd.service

将用户添加到其他组。

# for g in power network ;do ;gpasswd -a archie $g ;done
# for g in network power storage ;do ;gpasswd -a admin $g ;done

设置配置文件权限以便 devel 可以编辑它们。

# chown -R devel:root /etc/{http,openvpn,cups,zsh,vim,screenrc}
/etc/sudoers.d/80-hardened
Cmnd_Alias  POWER       =   /usr/bin/shutdown -h now, /usr/bin/halt, /usr/bin/poweroff, /usr/bin/reboot
Cmnd_Alias  STORAGE     =   /usr/bin/mount -o nosuid\,nodev\,noexec, /usr/bin/umount
Cmnd_Alias  SYSTEMD     =   /usr/bin/journalctl, /usr/bin/systemctl
Cmnd_Alias  KILL        =   /usr/bin/kill, /usr/bin/killall
Cmnd_Alias  PKGMAN      =   /usr/bin/pacman
Cmnd_Alias  NETWORK     =   /usr/bin/netctl
Cmnd_Alias  FIREWALL    =   /usr/bin/iptables, /usr/bin/ip6tables
Cmnd_Alias  SHELL       =   /usr/bin/zsh, /usr/bin/bash
%power      ALL         =   (root)  NOPASSWD: POWER
%network    ALL         =   (root)  NETWORK
%storage    ALL         =   (root)  STORAGE
root        ALL         =   (ALL)   ALL
admin       ALL         =   (root)  SYSTEMD, KILL, FIREWALL
devel	    ALL         =   (root)  PKGMAN
archie	    ALL         =   (devel) SHELL, (admin) SHELL

通过这种设置,你几乎永远不需要以 root 用户身份登录。

archie 可以连接到他们的家庭 Wi-Fi。

$ sudo netctl start home
$ sudo poweroff

archie 不能以任何其他用户的身份使用 netctl。

$ sudo -u admin -- netctl start home

archie 需要使用 journalctl 或杀死失控进程时,他们可以切换到该用户。

$ sudo -i -u devel
$ sudo -i -u admin

archie 不能切换到 root 用户。

$ sudo -i -u root

如果 archie 想以管理员身份启动 GNU Screen 会话,可以这样做:

$ sudo -i -u admin
[admin]$ chown admin:tty `echo $TTY`
[admin]$ screen

编辑文件

sudo 提供了 sudoedit 命令(相当于 sudo -e)。这对于编辑只能由 root 编辑的文件非常有用,同时仍然以普通用户身份运行编辑器并使用该用户的配置。

要编辑文件,请将 SUDO_EDITOR 设置为编辑器的名称,并将文件名传递给 sudoedit。例如:

$ SUDO_EDITOR=vim sudoedit /etc/file

参见 #使用 visudosudo(8) § e 了解设置编辑器的方法,但要警惕可能的安全问题

如果将多个名称传递给 sudo,所有文件将在一次调用中在编辑器中打开。这对于合并文件很有用:

$ SUDO_EDITOR=vimdiff sudoedit /etc/file /etc/file.pacnew

启用侮辱性信息 (Insults)

用户可以通过使用 visudosudoers 文件中添加以下行,来启用 sudo 中的侮辱性彩蛋。

/etc/sudoers.d/10-insults
Defaults insults

在输入错误密码时,这会将 Sorry, try again. 消息替换为幽默的侮辱性语言。

启用密码输入回显反馈

默认情况下,输入密码时没有视觉反馈。这是出于额外的安全考虑。但是,如果你希望有视觉输入反馈,可以通过添加此行来启用:

/etc/sudoers.d/10-pwfeedback
Defaults pwfeedback

彩色密码提示符

要使用颜色和/或粗体字体自定义密码提示符,请在 shell 初始化文件中设置 SUDO_PROMPT 环境变量,并使用 tput(1)

例如,要将密码提示符设置为粗体红色的 Password: ,请使用:

export SUDO_PROMPT="$(tput setaf 1 bold)Password:$(tput sgr0) "

或者对默认消息使用不同的颜色,如下所示:

export SUDO_PROMPT="$(tput setab 1 setaf 7 bold)[sudo]$(tput sgr0) $(tput setaf 6)password for$(tput sgr0) $(tput setaf 5)%p$(tput sgr0): "

更多信息请参见 控制台彩色输出Bash/提示符自定义

使用 U2F

U2F 非常适合与 sudo 配合使用,因为它可以有效消除在公共区域被 窥屏 (shoulder surfing) 的风险,同时仍然让你能够通过简单的物理触摸来有意识地批准提示。

参见 通用第二因子 (U2F)#免密码 sudo

写入受保护的文件

使用 sudo 时,你可能想要写入受保护的文件。使用 tee 可以实现这种分离:

$ input stream | sudo tee --option protected_file_1 protected_file_2...

此时简单的 >/>> 将无法工作。

在 Vim 中

当你编辑一个由其他用户拥有的文件却忘记使用 sudo 启动 Vim 时,一个类似的概念很有用。在这种情况下,你可以在 Vim 中执行以下操作来保存文件:

:w !sudo tee %

你可以将其添加到 ~/.vimrc 中,以便在命令模式下通过 :w!! 映射轻松使用此技巧:

~/.vimrc
" Allow saving of files as sudo when I forgot to start vim using sudo
 cmap w!! w !sudo tee > /dev/null %

> /dev/null 部分显式地丢弃标准输出,因为我们不需要将任何内容传递给另一个管道命令。

有关此技巧的工作原理及原因的更详细解释,请参阅 StackOverflow 上的文章:Vim “以 sudo 写入”的技巧是如何工作的?

在不安装 sudo 软件包的情况下使用 sudo-rs

你可以将 sudo-rs 作为 sudo 的独立替代品使用,而不需要 sudo 软件包。

参考 sudo默认配置创建 /etc/pam.d/sudo

同时为 sudo -i 创建 /etc/pam.d/sudo-i

# ln -s /etc/pam.d/sudo /etc/pam.d/sudo-i

sudo-rs 同时支持 /etc/sudoers/etc/sudoers-rs,并优先使用后者(如果存在)。请至少创建其中一个。一个有效的最小配置可以是:

本文或本节正考虑移除。

原因:此处不需要配置片段,用户可以编写自己的配置或复制 sudo 的配置。(在 Talk:Sudo 中讨论)
/etc/sudoers-rs
# Keep your editor when running visudo
Defaults!/usr/bin/visudo-rs env_keep += "SUDO_EDITOR EDITOR VISUAL"
# The same if you choose to symlink visudo-rs to visudo
Defaults!/usr/local/bin/visudo env_keep += "SUDO_EDITOR EDITOR VISUAL"

 
# Sanitize your path
Defaults secure_path="/usr/local/sbin:/usr/local/bin:/usr/bin"
 
# "root", and all members of the group "wheel" can run any command after providing a password.
root ALL=(ALL:ALL) ALL
%wheel ALL=(ALL:ALL) ALL

可选地,你可以通过在优先级较高的 PATH 目录中创建符号链接,将 sudo 替换为 sudo-rs

# ln -s /usr/bin/sudo-rs /usr/local/bin/sudo
# ln -s /usr/bin/su-rs /usr/local/bin/su
# ln -s /usr/bin/visudo-rs /usr/local/bin/visudo
# ln -s /usr/bin/sudoedit-rs /usr/local/bin/sudoedit

© . This site is unofficial and not affiliated with Arch Linux.

Content is available under GNU Free Documentation License 1.3 or later unless otherwise noted.