分析各场景下涉及的配置文件和最终 PATH 变量值:

  1. sudo -u admin -i 会重置初始 PATH 为 /etc/sudoers 指定值.
$ sudo strace -e open sudo -u admin -i
... ...
open("/etc/passwd", O_RDONLY|O_CLOEXEC) = 5
open("/etc/sudoers", O_RDONLY)          = 5
open("/etc/sudoers.d/README", O_RDONLY) = 6
... ...
open("/etc/group", O_RDONLY|O_CLOEXEC)  = 4
open("/etc/passwd", O_RDONLY|O_CLOEXEC) = 5
open("/etc/passwd", O_RDONLY|O_CLOEXEC) = 7
open("/etc/security/pam_env.conf", O_RDONLY) = 7
open("/etc/environment", O_RDONLY)      = 7
open("/etc/security/pam_env.conf", O_RDONLY) = 7
open("/etc/default/locale", O_RDONLY)   = 7
open("/etc/krb5.conf", O_RDONLY)        = 7
open("/dev/urandom", O_RDONLY)          = 7
open("/dev/urandom", O_RDONLY)          = 7
open("/etc/passwd", O_RDONLY|O_CLOEXEC) = 7
open("/etc/security/capability.conf", O_RDONLY) = 7
open("/etc/login.defs", O_RDONLY)       = 7
open("/etc/login.defs", O_RDONLY)       = 7
open("/etc/passwd", O_RDONLY|O_CLOEXEC) = 7
open("/etc/group", O_RDONLY|O_CLOEXEC)  = 7
open("/etc/krb5.conf", O_RDONLY)        = 7
open("/dev/urandom", O_RDONLY)          = 7
open("/etc/passwd", O_RDONLY|O_CLOEXEC) = 7
open("/var/run/utmp", O_RDONLY|O_CLOEXEC) = 7
... ...
$ echo $PATH
/opt/apache-maven-3.2.3/bin:/opt/jdk1.8.0_65/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/home/admin/usr/local/bin
  1. su admin -l, /etc/login.defs 的 PATH 配置会覆盖 /etc/environment 的配置
$ sudo strace -e open sudo su admin -l
... ...
open("/etc/group", O_RDONLY|O_CLOEXEC)  = 4
open("/etc/passwd", O_RDONLY|O_CLOEXEC) = 5
open("/etc/passwd", O_RDONLY|O_CLOEXEC) = 7
open("/etc/security/pam_env.conf", O_RDONLY) = 7
open("/etc/environment", O_RDONLY)      = 7
open("/etc/security/pam_env.conf", O_RDONLY) = 7
open("/etc/default/locale", O_RDONLY)   = 7
open("/etc/krb5.conf", O_RDONLY)        = 7
open("/dev/urandom", O_RDONLY)          = 7
open("/dev/urandom", O_RDONLY)          = 7
open("/etc/passwd", O_RDONLY|O_CLOEXEC) = 7
open("/etc/security/capability.conf", O_RDONLY) = 7
open("/etc/login.defs", O_RDONLY)       = 7
open("/etc/login.defs", O_RDONLY)       = 7
open("/etc/passwd", O_RDONLY|O_CLOEXEC) = 7
... ...
$ echo $PATH
/opt/apache-maven-3.2.3/bin:/opt/jdk1.8.0_65/bin:/usr/local/bin:/usr/bin:/bin:/usr/local/games:/usr/games:/home/admin/usr/local/bin
  1. ssh 登录, 生效的 PATH 来自 /etc/environment, 但好像两个文件都没有读.
Tue Dec  1 11:31:42 2015 CST open "/etc/profile", O_RDONLY
Tue Dec  1 11:31:42 2015 CST open "/etc/bash.bashrc", O_RDONLY
... ...
Tue Dec  1 11:31:42 2015 CST open "/etc/profile.d/bash_completion.sh", O_RDONLY
... ...
Tue Dec  1 11:31:42 2015 CST open "/etc/profile.d/local.sh", O_RDONLY
Tue Dec  1 11:31:42 2015 CST open "/home/hanyong/.bash_profile", O_RDONLY
Tue Dec  1 11:31:42 2015 CST open "/home/hanyong/.bash_login", O_RDONLY
Tue Dec  1 11:31:42 2015 CST open "/home/hanyong/.profile", O_RDONLY
Tue Dec  1 11:31:42 2015 CST open "/home/hanyong/.bashrc", O_RDONLY
Tue Dec  1 11:31:42 2015 CST open "/home/hanyong/.bash_history", O_RDONLY
... ...
Tue Dec  1 11:31:42 2015 CST open "/home/hanyong/.inputrc", O_RDONLY
Tue Dec  1 11:31:42 2015 CST open "/etc/inputrc", O_RDONLY
  1. 直接控制台登录 (本机登录), 先读 /etc/login.defs, 再读 /etc/environment, 后者生效.
Tue Dec  1 12:00:56 2015 CST open "/etc/passwd", O_RDONLY|O_CLOEXEC
Tue Dec  1 12:00:56 2015 CST open "/etc/shadow", O_RDONLY|O_CLOEXEC
Tue Dec  1 12:00:56 2015 CST open "/etc/security/capability.conf", O_RDONLY
Tue Dec  1 12:00:56 2015 CST open "/etc/passwd", O_RDONLY|O_CLOEXEC
Tue Dec  1 12:00:56 2015 CST open "/etc/login.defs", O_RDONLY
Tue Dec  1 12:00:56 2015 CST open "/etc/passwd", O_RDONLY|O_CLOEXEC
Tue Dec  1 12:00:56 2015 CST open "/etc/shadow", O_RDONLY|O_CLOEXEC
Tue Dec  1 12:00:56 2015 CST open "/etc/passwd", O_RDONLY|O_CLOEXEC
Tue Dec  1 12:00:56 2015 CST open "/etc/security/pam_env.conf", O_RDONLY
Tue Dec  1 12:00:56 2015 CST open "/etc/environment", O_RDONLY
Tue Dec  1 12:00:56 2015 CST open "/etc/security/pam_env.conf", O_RDONLY
Tue Dec  1 12:00:56 2015 CST open "/etc/default/locale", O_RDONLY
Tue Dec  1 12:00:56 2015 CST open "/etc/passwd", O_RDONLY|O_CLOEXEC
Tue Dec  1 12:00:56 2015 CST open "/proc/1/limits", O_RDONLY
Tue Dec  1 12:00:56 2015 CST open "/etc/security/limits.conf", O_RDONLY
... ...
Tue Dec  1 12:00:56 2015 CST open "/etc/passwd", O_RDONLY|O_CLOEXEC
Tue Dec  1 12:00:56 2015 CST open 00007ffc9286a368, O_RDONLY
Tue Dec  1 12:00:56 2015 CST open "/etc/profile", O_RDONLY
Tue Dec  1 12:00:56 2015 CST open "/etc/bash.bashrc", O_RDONLY

总结

现象:

  1. 如果在登录相关脚本如 .profile, .bashrc 设置了环境变量, 登录和非登录会产生不一样的环境.
  2. su -l 和直接登录的初始 PATH 是不一样的, su -l 取的是 login.defs.
  3. sudo 直接重置初始 PATH. sudo 默认要带上 -H (或使用 -i 才) 会重置 HOME, 由配置 env_reset 控制.

最佳实践:

  1. 登录相关脚本如 /etc/profile, /etc/profile.d/*, .profile 应该只包含优化交互体验的配置, 不包含其他配置.
  2. 环境变量包括 PATH 统一配置在 /etc/environment 维护, 注意这个文件是简单环境变量语法, 不支持 shell 语法如 "PATH=$PATH:/xx/bin" 等.
  3. sudo 属于临时提权, 只能执行 sudo 指定的标准 PATH 下的命令.
  4. 以用户身份执行用户 PATH 下的命令可使用 sudo su <login> -c.
  5. 不能配置单个用户自定义的 PATH, 如果配置在 .profile, 则需要 sudo su <login> -l -c, 此时初始 PATH 变为 login.defs 配置, 不推荐. 除非不修改默认初始 PATH 或者 login.defs/etc/environment 的配置要同步, 不推荐.