跳转至内容

Sudo

来自 ArchWiki
(从 Sudoers 重定向而来)

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。