跳转至内容

Sudo

来自 ArchWiki

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

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

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

安装

安装 sudo 软件包。

提示: sudo-rssudo 的另一个内存安全实现版本(具有一些局限性)。

用法

要开始以非特权用户身份使用 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'

配置

本文章或章节需要扩充。

原因: 创建一个关于 Defaults 的介绍,也许包含一个列出常用设置的表格(在 Talk:Sudo 中讨论)。

默认骨架 (Defaults skeleton)

sudoers(5) § SUDOERS OPTIONS 列出了所有可与 /etc/sudoers 文件中的 Defaults 命令一起使用的选项。

参见 [1] 获取以 sudoers 优化格式列出的选项列表(从 1.8.7 版本源代码解析)。

更多信息请参见 sudoers(5),例如如何配置密码超时。

查看当前设置

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

使用 visudo

sudo 的配置文件是 /etc/sudoers。应当始终使用 visudo(8) 命令进行编辑。visudo 会锁定 sudoers 文件,将修改保存到临时文件,并在将其复制到 /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 文件的副本,并使用 visudo -c /copy/of/sudoers 进行检查。如果你想规避 visudo 对文件的锁定,这可能会派上用场。

要永久更改编辑器,请参见 环境变量#每用户。要在系统范围内仅为 visudo 永久更改所选编辑器,请在 /etc/sudoers 中添加以下内容(假设 nano 是你首选的编辑器):

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

示例条目

要允许用户在命令前加 sudo 时获得完整的 root 权限,请添加以下行:

USER_NAME   ALL=(ALL:ALL) ALL

要允许用户在主机名为 HOST_NAME 的机器上以任何用户身份运行所有命令:

USER_NAME   HOST_NAME=(ALL:ALL) ALL

要允许 wheel 组的成员拥有 sudo 访问权限:

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

要禁用用户 USER_NAME 的密码询问:

警告: 这将允许任何以你的用户名运行的进程无需询问许可即可使用 sudo。
Defaults:USER_NAME      !authenticate

仅为主机 HOST_NAME 上的用户 USER_NAME 启用显式定义的命令:

USER_NAME HOST_NAME=/usr/bin/halt,/usr/bin/poweroff,/usr/bin/reboot,/usr/bin/pacman -Syu
注意: 最具定制化的选项应放在文件的末尾,因为后面的行会覆盖前面的行。特别是,如果你的用户在 %wheel 组中,这样的行应该放在 %wheel 行之后。

仅为主机 HOST_NAME 上的用户 USER_NAME 启用显式定义的命令且无需密码:

USER_NAME HOST_NAME= NOPASSWD: /usr/bin/halt,/usr/bin/poweroff,/usr/bin/reboot,/usr/bin/pacman -Syu

详细的 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

技巧与提示

禁用密码提示符超时

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

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)

Defaults passprompt="^G[sudo] password for %p: "

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

本文章或章节需要扩充。

原因: 使用铃声时是否可以保留本地化提示?当使用 SUDO_PROMPT 时也存在同样的问题。是否有替代方案,例如默认使用 "-B" 选项?(在 Talk:Sudo 中讨论)。

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

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

禁用分终端 sudo

警告: 这将允许任何进程使用你的 sudo 会话。

如果你对 sudo 的默认设置(每次打开新终端都需要输入密码)感到厌烦,请将 timestamp_type 设置为 global

Defaults timestamp_type=global

减少输入密码的次数

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

Defaults timestamp_timeout=10

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

环境变量

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

$ sudo -E pacman -Syu

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

/etc/sudoers
Defaults env_keep += "ftp_proxy http_proxy https_proxy no_proxy"

Root 密码

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

Defaults targetpw

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

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}
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 或 kill 失控进程时,他们可以切换到该用户。

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

但 "archie" 无法切换到 root 用户。

$ sudo -i -u root

如果 "archie" 想以 admin 身份启动 gnu-screen 会话,可以这样做:

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

使用 /etc/sudoers.d 中的补充文件配置 sudo

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

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

这些补充文件中的条目格式与 /etc/sudoers 本身相同。要直接编辑它们,请使用 visudo -f /etc/sudoers.d/somefile。详情请参见 sudoers(5) § Including other files from within sudoers

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

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

编辑文件

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
Defaults insults

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

启用密码输入回显反馈

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

/etc/sudoers
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

创建或编辑 /etc/sudo-rs/config.toml

/etc/sudo-rs/config.toml
[defaults]
askpass = false
timeout = 15

sudo-rs 同时支持 /etc/sudoers/etc/sudoers-rs,如果后者存在则使用后者。

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

# ln -s /usr/bin/sudo-rs /usr/local/bin/sudo

故障排除

没有 TTY 时的 SSH 问题

本条目或章节是合并至 #配置 的候选对象。

备注: 请使用模板的第二个参数提供更详细的说明。(在 Talk:Sudo 中讨论)。

在运行远程命令时,SSH 默认不会分配 tty。如果没有分配 tty,sudo 就无法防止密码被显示。你可以使用 ssh 的 -t 选项强制其分配 tty。

Defaults 选项 requiretty 仅在用户拥有 tty 时才允许其运行 sudo。

# 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

宽松的 umask

本条目或章节是合并至 #配置 的候选对象。

备注: 请使用模板的第二个参数提供更详细的说明。(在 Talk:Sudo 中讨论)。

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

Defaults umask = 0022
Defaults umask_override

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