在Shell脚本中使用passwd命令

88

我正在编写一个shell脚本,以自动添加新用户并更新其密码。我不知道如何让passwd从shell脚本中读取而不是交互式地提示我输入新密码。以下是我的代码。

adduser $1
passwd $1 << EOF
$2
$2
EOF

类似问题:http://askubuntu.com/questions/94060/run-adduser-non-interactively/667842#667842 - ThorSummoner
15个回答

112
从 "man 1 passwd" 中:
   --stdin
          This option is used to indicate that passwd should read the new
          password from standard input, which can be a pipe.

在你的情况下
adduser "$1"
echo "$2" | passwd "$1" --stdin

您的passwd命令可能没有--stdin选项:请使用chpasswd实用程序,如ashawley所建议。
如果您使用的是除bash之外的其他shell,echo可能不是一个内置命令,而是调用/bin/echo。这是不安全的,因为密码将显示在进程表中,并且可以使用诸如ps之类的工具查看。
在这种情况下,您应该使用另一种脚本语言。以下是Perl的一个示例:
#!/usr/bin/perl -w
open my $pipe, '|chpasswd' or die "can't open pipe: $!";
print {$pipe} "$username:$password";
close $pipe

你应该引用你的参数扩展。此外,这种做法绝不是可移植的,甚至不建议使用。请查看我的回复以获取更多相关信息。 - lhunath
2
请不要使用“echo ... | password!”这样的命令。这会使密码对于运行ps命令的其他用户可见。而且,您在不同的程序、文件、管道等中传递密码的次数越多,泄露的风险就越高。 - Brian Campbell
@lhunath: 您提到的引用问题是正确的,我会进行修复。@Brian: “echo” 是 Bash 内置命令,在“ps”输出中不会显示(至少在 Bash 中是这样,不确定 sh 是否也是如此)。 - 8jean
4
如果是Bash语言,那么执行passwd "$1" <<< "$2"的命令。其中,"$1"和"$2"是传递给该命令的第一个和第二个参数。 - glenn jackman
1
@glennjackman请注意,这里的字符串隐式地创建了临时文件,可能会被外部源读取。 - lhunath
@8jean 依赖于 echo 将其参数保持在公共进程树之外的副作用,可能不是最佳实践。这种副作用可能在所有系统上都不成立,echo 并不总是内置的,而且看到密码作为参数传递应该是一个大红旗,你应该尽量避免。 - lhunath

77

唯一适用于Ubuntu 12.04的解决方案:

echo -e "new_password\nnew_password" | (passwd user)

但是第二个选项只有在我从以下内容进行更改时才有效:

echo "password:name" | chpasswd

To:

echo "user:password" | chpasswd

请参阅原帖解释:通过脚本更改密码


3
使用sudo权限,将"user:newpassword"作为输入,通过chpasswd命令更改用户的密码。如果您是root用户,则可以省略sudo。 - ThorSummoner
如果密码包含“\n”怎么办?有没有处理的方法?例如:my@secret\npasword123 - Eduardo Baitello
1
类似于第一个,但会自动重复密码。另外,请使用sudo,否则它会先要求输入当前密码,从而搞砸这个过程。echo password | sed 's/.*/\0\n\0/' | sudo passwd -q $USER 2> /dev/null - Cameron Tacklind

41
现在,你可以使用这个命令作为超级用户:
echo "user:pass" | chpasswd

或者以允许的用户身份使用sudo:
echo "user:pass" | sudo chpasswd

5
尝试运行您的命令时,我遇到了“认证令牌操作错误”的错误。在chpasswd前面加上sudo对我很有帮助,问题得以解决。 - Laurent Van Winckel
2
@LaurentVanWinckel - chpasswd 只能在 sudo/root 权限下运行。因此,要将其作为除 root 以外的用户执行,该用户必须在 sudo 组中:echo 'user:pass' | sudo chpasswd。在提示符上执行此单行命令时,请确保在前面添加一个额外的空格。否则,包括敏感密码的命令可能会被写入用户的提示符历史文件,例如 ~/.bash_history - Abdull

27

阅读以下来自:

我引用:

在bash中无论做什么都不可能奏效。passwd(1)不会从标准输入中读取密码。这是有意的,目的是为了保护您。密码从未旨在被输入到程序中或由程序生成。密码只应由实际人类的手指(带有一个正常的大脑),并且永远不要在任何地方写下。

尽管如此,我们仍然得到许多用户询问如何绕过35年的Unix安全性。

这篇文章还解释了如何正确设置您的shadow(5)密码,并向您展示了GNU-我只在不需要太多思考的情况下关心安全-滥用passwd(1)的方式。

最后,如果您真的要使用愚蠢的GNU passwd(1)扩展--stdin,请不要将密码作为命令行参数传递。

echo $mypassword | passwd --stdin # Eternal Sin.
echo "$mypassword" | passwd --stdin # Eternal Sin, but at least you remembered to quote your PE.
passwd --stdin <<< "$mypassword" # A little less insecure, still pretty insecure, though.
passwd --stdin < "passwordfile" # With a password file that was created with a secure `umask(1)`, a little bit secure.

使用GNU passwd是最后的选择,但我仍然不建议这样做。

在命令行上输入密码意味着任何有机会访问该电脑的人都可以监视ps或类似的命令并窃取密码。即使你认为你的电脑很安全;你应该下意识地尽量避免这种情况发生(是的,即使需要花费更多时间来完成工作,也要坚持这一点)。


1
我可以问一下为什么这仍然是“相当不安全”的吗? $ passwd --stdin <<< $(pass somehost/someuser) - Jack Tang
3
@JackTang 你的参数扩展需要加上引号。<<<比使用具有良好权限的文件进行<操作不够安全的原因是,<<<可能会隐式创建一个可被外部人员读取的临时文件。 - lhunath

6
< p > < em > Here-document 可以在您的 < code > passwd 不支持 < code > --stdin 并且由于某些原因您不想(或无法)使用 < code > chpasswd 的情况下使用。

< p > < strong > 示例:

#!/usr/bin/env bash

username="user"
password="pass"

passwd ${username} << EOD
${password}
${password}
EOD

在Arch Linux下测试通过。此处的passwdshadow-utils的一个元素,并且安装自core/filesystem软件包,通常这个软件包是默认安装的,因为它是由core/base所需。


超级邪恶,但这是唯一真正对我起作用的东西 :) - Andrew Faulkner

2

对于需要通过登录sudoers文件中的用户帐户远程运行root权限的脚本的人,我发现了一个非常不安全的恶意黑客攻击方法:

sshpass -p 'userpass' ssh -T -p port user@server << EOSSH
sudo -S su - << RROOT
userpass
echo ""
echo "*** Got Root ***"
echo ""
#[root commands go here]
useradd -m newuser
echo "newuser:newpass" | chpasswd
RROOT
EOSSH

2
您可以使用chpasswd命令。 echo $1:$2 | chpasswd

2

我遇到了同样的问题,但在我使用的passwd版本中(Ubuntu 14.04),一些原因导致--stdin选项不可用。

如果你们中有任何人也遇到了同样的问题,可以像我一样通过使用chpasswd命令来解决:

echo "<user>:<password>" | chpasswd

1
这是一篇有关Teradata节点管理员的权威答案。请前往您的“/etc/hosts”文件,并在文本文件中创建一个IP地址或节点名称列表。保留HTML标签,不做解释。
SMP007-1
SMP007-2
SMP007-3

请将以下脚本放入文件中。

#set a password across all nodes
printf "User ID: "
read MYUSERID
printf "New Password: "
read MYPASS

while read -r i; do
    echo changing password on "$i"
    ssh root@"$i" sudo echo "$MYUSERID":"$MYPASS" | chpasswd
    echo password changed on "$i"
done< /usr/bin/setpwd.srvrs

好的,我知道我在ssh和root方面犯了一个基本的安全规则,但我会让你们安全专家处理它。

现在,请将这个命令放入您的/usr/bin子目录中,并与您的setpwd.srvrs配置文件一起使用。

当您运行该命令时,它会首先提示您输入用户ID,然后提示您输入密码。然后脚本遍历setpwd.srvrs文件中的所有节点,并对每个节点执行无需用户交互或二次密码验证的无密码ssh,然后设置密码。


我已经修复了其中最严重的错误,但这真的不是最佳实践。在/usr/bin中硬编码本地数据文件只是一个可怕的想法。 - tripleee

1

我在一台CentOS VMWare镜像上测试了这个,我通常会为此保留这样的东西。请注意,您可能希望避免将密码作为命令行参数,因为整个机器上的任何人都可以从'ps -ef'中读取它们。

话虽如此,这将起作用:

user="$1"
password="$2"
adduser $user
echo $password | passwd --stdin $user

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接