多个 UID 为 0 的用户如何实现 root 用户的免密

简介: 多个 UID 为 0 的用户如何实现 root 用户的免密
前言:

由于客户的机器都是suse的,并且uid为0的用户有 root、sysop、appadmin 三个用户,导致有的时候远程连接,即使是以 root 用户的身份登录,也会出现当前用户不是 root 的情况,以至于部署和免密脚本会失败

以下是通过注释 /etc/passwd 文件的方式,来暂时注销 sysop 和 appadmin 这两个用户,以此来达到当前uid为0的用户只有 root

登陆 Linux 系统时,虽然输入的是自己的用户名和密码,但其实 Linux 并不认识你的用户名称,它只认识用户名对应的 ID 号(也就是一串数字)

Linux 系统将所有用户的名称与 ID 的对应关系都存储在 /etc/passwd 文件中

  • ip:

    • 192.168.72.12
    • 192.168.72.13
    • 192.168.72.14
  • user:

    • root
    • appadmin
    • sysop

创建用户

-m 创建用户的家目录(suse默认不会创建)

-U 创建用户的基本组(suse默认不会创建)

-o 允许创建重复的用户

-u 指定用户uid

useradd -mUo appadmin -u 0
useradd -mUo sysop -u 0

创建用户密码

创建用户密码,由于 susepasswd命令没有 --stdin参数,没法使用 echo '<user password>' | passwd --stdin <user name>这个方式创建用户密码

但是可以通过echo '<user name>:<user password>' | chpasswd的方式来创建用户密码

这两种方式,是为了免交互为用户创建密码,直接使用passwd或者chpasswd命令,是需要输入两次密码,对于写脚本不友好

echo 'appadmin:123.com' | chpasswd
echo 'sysop:123.com' | chpasswd
快速注释,方便快速回到root用户
for i in $(awk -F : '{if($3==0){print$1}}' /etc/passwd | grep -v root);do sed -i "/$i/s/^/#/g" /etc/passwd;done
快速取消注释
for i in $(awk -F : '{if($3==0){print$1}}' /etc/passwd);do sed -i "/$i/s/^#//g" /etc/passwd;done

免密脚本

我的用户密码全部设置为123.com了,需要使用下面的脚本,记得修改密码为自己的用户密码
#!/usr/bin/env bash
ips='
192.168.72.12
192.168.72.13
192.168.72.14
'
# 将上面的ip变量格式化成数组的形式
ip_arry=($(printf "%q\n" ${ips}))
# 备份root用户的.ssh目录
[[ -d "/root/.ssh" ]] && mv /root/.ssh{,-$(date +"%F_%T")}
# 生成新的公钥和私钥
ssh-keygen -t rsa -P "" -f /root/.ssh/id_rsa -q
for (( i=0; i<${#ip_arry[@]}; i++ ))
do
    expect -c "
    spawn ssh-copy-id -i /root/.ssh/id_rsa.pub root@${ip_arry[i]} -o \"StrictHostKeyChecking no\"
        expect {
                \"*assword*\" {send \"123.com\r\"; exp_continue}
                \"*assword*\" {send \"123.com\r\";}
               }"
done

当前主机用户不是root

通过 whoami命令,确保当前的用户不是 root,可以使用 ssh sysop@ip来切换用户

此时,执行上面的脚本会报错,如果-f指定的目录不是用户的家目录,ssh-keygen无法自动创建,需要手动创建

/usr/bin/ssh-copy-id: ERROR: failed to open ID file '/root/.ssh/id_rsa.pub': No such file or directory

当前主机用户是root,远程主机多个用户uid为0

执行上面的脚本没有问题,可以实现root用户免密

但是连接远程主机,查看当前用户,显示为非root用户

ssh root@192.168.72.13 "whoami"

利用shell脚本实现远程主机uid为0的用户只有root一个

#!/usr/bin/env bash
ips='
192.168.72.12
192.168.72.13
192.168.72.14
'

ip_arry=($(printf "%q\n" ${ips}))
user_name=$(awk -F : '{if($3==0){print$1}}' /etc/passwd | grep -v root)

for (( i=0; i<${#ip_arry[@]}; i++ ))
do
    for name in ${user_name}
    do
        j=($(printf "%q\n" ${name}))
        ssh root@${ip_arry[i]} "sed -i \"/${name[j]}/s/^/#/g\" /etc/passwd"
    done
done
当然,服务器是人家的,所以,自己的服务搞定以后,要取消sysop和appadmin的注释,不能影响客户的使用,使用一下脚本实现了取消注释
#!/usr/bin/env bash
ips='
192.168.72.12
192.168.72.13
192.168.72.14
'

ip_arry=($(printf "%q\n" ${ips}))
user_name=$(awk -F : '{if($3==0){print$1}}' /etc/passwd | sed 's/#//')

for (( i=0; i<${#ip_arry[@]}; i++ ))
do
    for name in ${user_name}
    do
        j=($(printf "%q\n" ${name}))
        ssh root@${ip_arry[i]} "sed -i \"/${name[j]}/s/^#//g\" /etc/passwd"
    done
done
仅以此篇来记录自己的学习和成长的过程,虽然并不适用与大家的场景,不过用来练习shell脚本,还是可以的

---------------------------------更新时间:2021年11月22日---------------------------------

多个UID=0用户之间的免密

由于会有新场景上线,因此会有新机器需要做免密的情况,此时就要对所有UID=0的用户都完成免密才可以保证服务部署过程中,避免出现需要密码的情况,因此有了下面的脚本

  • 脚本需要通过位置变量引入一个清单文件,清单文件名称以自己本地实际的为准,我这里使用的是list.txt来做参考
  • 脚本执行方式: bash same_uid_ssh.sh list.txt
  • list.txt文件内的格式 [以一个空格为分隔符,依次为IP地址用户名用户密码‘]:

192.168.70.26 sysop 123.com
192.168.70.88 sysop 234.com
192.168.70.89 sysop 345.com

  • 脚本依赖 expectdoc2unix 两个命令,执行前,建议先检查环境是否有这两个命令
#!/bin/bash
base_dir=$(cd `dirname $0`; pwd)
check_sanme_uid=$(awk -F ':' '{if ($3==0) {print $1}}' /etc/passwd | wc -l)
# 以数组的形式输出UID=0的所有用户
same_uid_user=($(awk -F ':' '{if ($3==0) {print $1}}' /etc/passwd))
# 脚本执行的时候没有带参数,则返回脚本执行方式
# list.txt非固定名称,只需要文件存在即可,文件内容以一个空格为分割,内容格式为: <ip地址> <用户名称> <用户密码>
if [[ "$#" == 0 ]];then
  echo "Usage: bash $0 list.txt"
  exit 0
fi
hosts_list=$1
user_host=($(awk '{print $1}' ${hosts_list}))
user_name=($(awk '{print $2}' ${hosts_list}))
user_pass=($(awk '{print $3}' ${hosts_list}))

if [[ "${check_sanme_uid}" > 1 ]];then
  echo "system have ${check_sanme_uid} same uid users"
fi

# 生成ssh公钥
function make_ssh_pub () {
  # 判断脚本所在路径下是否有 authorized_keys 文件,如果存在则清空文件内容
  [[ ! -f "${base_dir}/authorized_keys" ]] || > ${base_dir}/authorized_keys

  for (( i=0; i<${#same_uid_user[@]}; i++ ))
  do
    # 输当前循环的用户名
    echo "now is ${same_uid_user[i]}"

    # 通过for循环逐一注释 /etc/passwd 文件来达到当前UID=0的用户是唯一的
    for change in $(awk -F ':' '{if ($3==0) {print $1}}' /etc/passwd | grep -v ${same_uid_user[i]})
    do
      sed -i "/${change}/s/^/#/g" /etc/passwd
    done

    # 判断用户是否为root来区分用户的家目录
    #   [只是注释 /etc/passwd 文件无法达到切换环境变量的效果,无法使用系统变量 $HOME 来指定用户的家目录]
    if [[ "${same_uid_user[i]}"x == "root"x ]];then
        user_home="/root"
    else
        user_home="/home/${same_uid_user[i]}"
    fi

    # 判断用户家目录下是否存在 .ssh 目录,存在则备份,后缀为: 年月日-时:分
    [[ ! -d "${user_home}/.ssh" ]] || cp -r ${user_home}/.ssh{,.$(date +%Y%m%d-%H:%M)}

    # 为了保证环境干净,删除用户家目录的 .ssh 目录
    rm -rf ${user_home}/.ssh

    # 静默生成用户ssh公钥,公钥格式为rsa
    ssh-keygen -t rsa -P "" -f ${user_home}/.ssh/id_rsa -q

    # 将用户的公钥追加到脚本所在路径下的 authorized_keys 文件内
    # 后续只需要将脚本所在路径下的 authorized_keys 文件分发到其他节点指定用户家目录下的 .ssh 目录
    # 以此来达到免密的效果
    cat ${user_home}/.ssh/id_rsa.pub >> ${base_dir}/authorized_keys

    # 取消之前的 /etc/passwd 文件的注释,进入下一层循环时,会重新注释其他用户,避免漏注释,造成公钥缺失
    for change in $(awk -F ':' '{if ($3==0) {print $1}}' /etc/passwd | grep -v ${same_uid_user[i]})
    do
      sed -i "/${change}/s/^#//g" /etc/passwd
    done
  done

  # 受到umask的影响,默认生成的文件权限为644,authorized_keys 文件默认权限为600,此处做一个赋权
  chmod 600 ${base_dir}/authorized_keys
}

function make_ssh_auth () {
  for (( host=0; host<${#user_host[@]}; host++ ))
  do
    # 判断脚本所在路径下是否有 user_list.txt 这个文件,有则清空文件内容
    [[ ! -f "${base_dir}/user_list.txt" ]] || > ${base_dir}/user_list.txt
    # 通过expect远程登录其他节点,查看UID=0的用户有哪些,避免环境差异,导致免密失败
    # log_file将expect的输出重定向到指定文件中 [expect的log_file输出的文件格式不是unix的]
    # expect内如果需要ssh到其他节点使用awk命令,需要使用花括号来代替双引号,并且$符号前面需要加上转义符(\)
    expect -c "
    spawn ssh ${user_name[host]}@${user_host[host]} {awk -F ':' '{if (\$3==0) {print \$1}}' /etc/passwd}
    log_file ${base_dir}/user_list.txt
      expect {
        \"*es/no*\" {send \"yes\r\"; exp_continue}
        \"*assword*\" {send \"${user_pass[host]}\r\"; exp_continue}
        \"*assword*\" {send \"${user_pass[host]}\r\"; exp_continue}
      }"
    # 将dos格式的文件转换成unix格式的文件 [否则输出的内容会有dos文件的字符]
    dos2unix -o ${base_dir}/user_list.txt
    # 定义same_uid变量,以数组的形式定义从log_file文件内获取到的UID=0的用户
    same_uid=($(printf "%q " `egrep -v 'spawn|assword' ${base_dir}/user_list.txt`))

    for (( n=0; n<${#same_uid[@]}; n++ ))
    do
      # 定义用户的家目录,root用户与其他用户的家目录不同
      if [[ "${same_uid[n]}"x == "root"x ]];then
          user_home="/root"
      else
          user_home="/home/${same_uid[n]}"
      fi

      # 通过expect解决交互问题
      # 在用户的家目录下创建.ssh目录,并赋权700
      expect -c "
        spawn ssh ${user_name[host]}@${user_host[host]} \"mkdir ${user_home}/.ssh -m 700\"
          expect {
            \"*es/no*\" {send \"yes\r\"; exp_continue}
            \"*assword*\" {send \"${user_pass[host]}\r\"; exp_continue}
            \"*assword*\" {send \"${user_pass[host]}\r\";}
          }"
      # 有些用户家目录下可能已经存在.ssh目录,这里重新赋权700,避免权限问题影响免密
      expect -c "
        spawn ssh ${user_name[host]}@${user_host[host]} \"chmod 700 ${user_home}/.ssh\"
          expect {
            \"*es/no*\" {send \"yes\r\"; exp_continue}
            \"*assword*\" {send \"${user_pass[host]}\r\"; exp_continue}
            \"*assword*\" {send \"${user_pass[host]}\r\";}
          }"
      # 判断用户家目录下的.ssh目录下是否已有 authorized_keys 免密文件,有则备份
      # expect内如果用到'[]'来判断目录或文件是否存在,也需要用花括号来代替双引号
      expect -c "
        spawn ssh ${user_name[host]}@${user_host[host]} {[ ! -f ${user_home}/.ssh/authorized_keys ] || mv ${user_home}/.ssh/authorized_keys{,.bak}}
          expect {
            \"*es/no*\" {send \"yes\r\"; exp_continue}
            \"*assword*\" {send \"${user_pass[host]}\r\"; exp_continue}
            \"*assword*\" {send \"${user_pass[host]}\r\";}
          }"
      # 将脚本生成的 authorized_keys 免密文件分发到其他节点
      expect -c "
        spawn scp ${base_dir}/authorized_keys ${user_name[host]}@${user_host[host]}:${user_home}/.ssh/authorized_keys
          expect {
            \"*es/no*\" {send \"yes\r\"; exp_continue}
            \"*assword*\" {send \"${user_pass[host]}\r\"; exp_continue}
            \"*assword*\" {send \"${user_pass[host]}\r\";}
          }"
    done
  done
}

make_ssh_pub
make_ssh_auth
目录
相关文章
|
21天前
|
存储 JSON JavaScript
JSON 快速上手指南
JSON是一种轻量级数据交换格式,语法严格,键名需双引号、值类型有限,支持跨语言解析。本文详解其语法规则、与JS对象区别、序列化/解析方法(stringify/parse)、实战应用及常见避坑技巧,助你快速掌握JSON核心技能。
|
Linux 数据库 iOS开发
CrossOver 25.1.0 for macOS & Linux - 领先的 Wine 解决方案
CrossOver 25.1.0 for macOS & Linux - 领先的 Wine 解决方案
536 0
|
6月前
|
人工智能 自然语言处理 数据可视化
开源AI BI可视化工具-dataline
DataLine 是一个开源数据分析工具,支持自然语言交互,可快速生成图表与报告。数据默认存储本地,保障隐私安全,兼容 Postgres、MySQL、Excel 等多种数据源。提供可视化仪表盘、触发器及知识库功能,支持 Windows、Mac、Linux 平台运行,并可通过 Docker 部署,适合企业使用。
|
机器学习/深度学习 PyTorch TensorFlow
Pytorch学习笔记(二):nn.Conv2d()函数详解
这篇文章是关于PyTorch中nn.Conv2d函数的详解,包括其函数语法、参数解释、具体代码示例以及与其他维度卷积函数的区别。
3118 0
Pytorch学习笔记(二):nn.Conv2d()函数详解
|
Linux Python
Linux 中某个目录中的文件数如何查看?这篇教程分分钟教会你!
在 Linux 系统中,了解目录下文件数量是常见的需求。本文介绍了四种方法:使用 `ls` 和 `wc` 组合、`find` 命令、`tree` 命令以及编程实现(如 Python)。每种方法都附有详细说明和示例,适合不同水平的用户学习和使用。掌握这些技巧,可以有效提升系统管理和日常使用的效率。
4782 6
|
达摩院 供应链 Cloud Native
低代码这么火,它的人才认证你考了吗?
2021年超级🔥 的证书!限时免费认证中……
9039 0
低代码这么火,它的人才认证你考了吗?
|
SQL 运维 关系型数据库
记一次 MySQL 主从同步异常的排查记录,百转千回!
这篇文章主要讲述了在 MySQL 主从同步过程中遇到的一个问题,即从库的 SQL 线程因 Relay Log 损坏导致同步停止。作者首先介绍了现象,从库的 Slave_IO_Running 正常,但 Slave_SQL_Running 停止,报错信息提示可能是 binlog 或 relay log 文件损坏。
732 7
|
应用服务中间件 nginx
项目node_modules开发环境变大问题处理
项目node_modules开发环境变大问题处理
481 0
|
NoSQL Java Linux
【Linux IO多路复用 】 Linux 网络编程 认知负荷与Epoll:高性能I-O多路复用的实现与优化
【Linux IO多路复用 】 Linux 网络编程 认知负荷与Epoll:高性能I-O多路复用的实现与优化
576 0
|
应用服务中间件 API nginx
简单配置nginx反向代理,实现跨域请求
简单配置nginx去做反向代理,实现跨域请求 简单介绍nginx的nginx.conf最核心的配置,去做反向代理,实现跨域请求。 更多详细配置,参考nginx官方文档 先介绍几个nginx命令 打开nginx.
7600 0