Sudo

来自 ArchWiki
(重定向自 Visudo)

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

Sudo 是 su 的替代品,用于以 root 身份运行命令。与启动 root shell 并允许后续所有命令以 root 权限访问的 su 不同,sudo 仅为单个命令授予临时权限提升。通过仅在需要时启用 root 权限,sudo 的使用降低了因输入错误或调用命令中的 bug 而破坏系统的可能性。

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

安装

安装 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'

配置

本文或本节需要扩充。

原因: 创建一个介绍 Defaults 的引言,或许可以包含一个列出常用设置的表格(在 Talk:Sudo 中讨论)

Defaults 骨架

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

要将 nano 建立为当前 shell 会话期间的 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

要允许 group 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 行之后。

仅为主机 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。文件权限必须设置为 0440。这些权限是默认设置的,但如果您不小心更改了它们,应立即将其改回,否则 sudo 将会失败。

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

技巧与诀窍

禁用密码提示超时

常见的烦恼是某个后台终端上运行的长时间运行的进程,该进程以普通权限运行,仅在需要时才提升权限。这会导致 sudo 密码提示被忽略并超时,此时进程终止,已完成的工作丢失,或者充其量只是缓存。常见的建议是启用无密码 sudo,或延长 sudo 记住密码的超时时间。这两者都具有负面的安全影响。提示超时也可以禁用,并且由于这不服务于任何合理的安全目的,因此应该成为此处的解决方案

Defaults passwd_timeout=0

Bash 补全

本文或本节正在考虑删除。

原因: 没有理由不能安装 bash-completion。(在 Talk:Sudo 中讨论)

为了使功能完善的 bash 补全可用于 sudo 命令,请安装 bash-completion 软件包。如果由于某种原因无法安装此软件包,则为 sudo 命令提供减少的自动补全功能的替代方法是将以下内容添加到您的 .bashrc

complete -cf sudo

传递别名

以下内容仅在 bash 补全不可用时相关(完整或如上所述减少):Zsh 和 Bash 中的别名通常仅针对命令中的第一个单词展开。这意味着您的别名通常在运行 sudo 命令时不会展开。使下一个单词展开的一种方法是为以空格结尾的 sudo 创建别名。将以下内容添加到您的 shell 的配置文件

alias sudo='sudo '

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

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

以及 bash(1) § ALIASES

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

在密码提示中添加终端响铃

为了引起对后台终端中 sudo 提示的注意,用户可以简单地使其回显一个 响铃字符

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 密码而不是用户密码,方法是将 targetpw(目标用户,默认为 root)或 rootpw 添加到 /etc/sudoers 中的 Defaults 行

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

或者,编辑 /etc/shadow 并将 root 的加密密码替换为 !*

root:!*:12345::::::

要再次启用 root 登录

$ sudo passwd root

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

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

在系统紧急情况下,恢复提示将要求您输入 root 密码,从而无法登录到恢复 shell。要在紧急情况下自动解锁 root 帐户,请使用 drop-in 文件将 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 应用程序。默认情况下,即使 root 帐户被禁用,kdesu 也可能会尝试使用 su。幸运的是,可以告诉 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 中的 drop-in 文件配置 sudo

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

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

这些 drop-in 文件中条目的格式与 /etc/sudoers 本身相同。要直接编辑它们,请使用 visudo -f /etc/sudoers.d/somefile。有关详细信息,请参阅 sudoers(5) § Including other files from within sudoers

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

注意: drop-in 文件中条目的顺序很重要:确保语句不会相互覆盖。
警告: /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

启用侮辱

用户可以通过使用 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 一起使用,因为它可以有效消除公共区域中肩窥的风险,同时仍然让您可以通过简单的物理触摸有意识地控制批准提示。

请参阅 Universal 2nd Factor#无密码 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 上的 How does the vim “write with sudo” trick work? 文章

故障排除

没有 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。