使用 shell 脚本更改多个服务器的用户密码,Linux Debian10

使用 shell 脚本更改多个服务器的用户密码,Linux Debian10

我需要配置几台服务器,所有服务器都有相同的用户,我需要让用户输入用户名和新密码,这样我就可以运行此脚本来配置所有服务器。这是我目前面临的错误,抱歉,我是 Linux 脚本的新手,请帮帮我,谢谢!

$ cat serverlist.txt
192.168.1.8
192.168.1.7
192.168.1.6

$ cat changepasswd.sh
#!/bin/bash
read -p "Enter username: " username
read -p "Enter new password: " new_password
for server in 'cat serverlist.txt'
do echo -e "Server IP is: $server"
ssh $server 'echo "new_password\nnew_password" | passwd username
done

$ chmod 777 changepasswd.sh

$ sh changepasswd.sh
Enter username: vncuser
Enter new password: abc12345
changepasswd.sh: 9: changepasswd.sh: Syntax error: Unterminated quoted string

答案1

就像错误所说的那样,有一个未终止的引号字符串。不成对的单引号 ( ') 位于$server和之间echo。如果你只像这样修复它

ssh $server 'echo "new_password\nnew_password" | passwd username'

你将会遇到更多问题。


我注意到的问题(从一般问题开始,不一定是最严重的问题):

  1. 您使用了 shebang ( #!/bin/bash),然后通过 调用了脚本sh changepasswd.sh。在这种情况下,shebang 被完全忽略,解释器是sh。通常最好依靠 shebang 并以 运行脚本./the_script;这样,调用者就不需要考虑使用哪个解释器,正确的解释器就在 shebang 中。

  2. 您使脚本可执行(使用chmod),但sh changepasswd.sh不需要它是可执行的。另一方面,调用 则./changepasswd.sh需要它是可执行的。

  3. 您使用sh作为解释器,但read -p它不可移植。您的sh可能支持它,其他实现可能支持也可能不支持。使用bash不仅允许您-p可靠地使用,而且-s(不回显密码)。无论如何,您都应该使用-rIFS=''(空字符串)尤其是在读取密码时。在 Bash 中查看help read以了解这些选项的作用。

  4. 如果您决定使用 shebang,最好去掉后缀.sh。如果您将脚本移植到另一个解释器(甚至编译二进制可执行文件),该名称changepasswd仍然合适,但changepasswd.sh需要更改,否则会造成混淆。

  5. chmod 777使任何人都可以写入该脚本。如果脚本位于其他用户无法写入的目录中享有一定的权利,他们将能够更改脚本。例如,他们可以注入一条将为$new_password他们发送/存储的线路。744可能是正确的模式。

  6. 建议两次询问新密码,如果两个字符串不匹配则中止。

  7. for server in 'cat serverlist.txt'cat serverlist.txt(string) 赋值给变量。你的意思是`cat serverlist.txt`$(cat serverlist.txt)。后者更好

  8. 一般来说 你应该双引号$( )以防止单词拆分和文件名生成;但在这种情况下,您确实需要单词拆分(但您不想生成文件名)。循环遍历文件的行(serverlist.txt在您的例子中)可以使用read(但存在陷阱)。但是,如果您的文件仅包含 IP 地址或简单主机名,则for server in $(cat serverlist.txt)似乎没问题。只需记住这不是一个好的通用方法

  9. 您仍然应该用双引号括住诸如这样的变量$server

  10. "new_password\nnew_password"只是一个字符串。您想要扩展变量:"$new_password\n$new_password"。一般来说,在连接时,您可能想要使用${new_password}

  11. “已修复” 片段

     ssh $server 'echo "$new_password\n$new_password" | passwd $username'
    

不会扩大$new_password,也不会$username局部扩大,因为这些是单引号。远程 shell 将扩展它们(最有可能扩展为空字符串)。若要在本地扩展,应将变量用双引号引起来:

    ssh "$server" "echo '$new_password\n$new_password' | passwd '$username'"

但这仍然容易出错,请继续阅读。

  1. SSH 服务器将获取整个命令字符串作为字符串。远程 shell 将对其进行解释。这意味着 (locally expand) $new_password(或$username) 中的单引号字符将破坏代码。这样你甚至可以注入代码。这与可能的代码注入无关(我知道你可以执行任何代码);而是与设置意外密码有关。有几种方法可以解决这个问题:
  • 一般来说,ssh如果服务器允许,你可以告诉将一些变量传递到远程端。然后远程 shell 可以安全地扩展它们。这取决于服务器配置;此外,在客户端,ssh动态定义变量很困难(而且不优雅)。在这种情况下,你不应该这样做,但如果你想了解更多信息,请PermitUserEnvironment在 中进行调查man 5 sshd_config
  • Bash 允许以特殊方式扩展变量,因此在 shell 中解析结果后,它会再次变为原始字符串。这是通过 实现的${variable@Q}。扩展的字符串被正确引用和/或转义。
  • 由于您正在从远程端的管道读取,因此您可以通过管道传输到ssh本地,并避免将(本地扩展)$new_password作为即将被解析的字符串传递。

无论你选择什么,请记住你需要正确引用本地 shell 和分别地用于远程 shell(如果${variable@Q}本地Q操作员处理此部分)。

  1. 不要用来echo传递“随机”字符串,更喜欢printf。如果你决定在 Bash 中将变量通过管道传输到ssh,那么这里是字符串可能是一个好方法。对于你的情况,你需要传递$new_password两次(加上换行符),所以无论如何我都会选择这个方法printf

该脚本可能如下所示:

#!/bin/bash
IFS= read -rp "Enter username: " username
IFS= read -rsp "Enter new password: " new_password
echo    # just to get a newline
IFS= read -rsp "Repeat new password: " new_password_2
echo
[ "$new_password" = "$new_password_2" ] || { echo "Mismatch. Aborting."; exit 1; }
for server in $(cat serverlist.txt)
   do echo "Server IP is: $server"
   printf '%s\n' "$new_password" "$new_password" | ssh "$server" "passwd -- ${username@Q}"
done

您需要使其可执行(chmod u+x changepasswd)并使用运行它./changepasswd

笔记:

  • 该脚本假设不会passwd询问当前密码。此假设源自您发布的脚本。
  • 该脚本假设所有远程passwd支持双划线
  • ssh "$server" "passwd -- ${username@Q}"可能是ssh "$server" "passwd '$username'",如果你确定扩大$username安全的. 基本验证可以在本地完成。

相关内容