本文针对centos7及以后版本,以前的版本不适用,并且单指root密码在开机过程中的修改。
修改密码本身的操作不复杂,但是过程中发生的事情和用到的命令值得研究一下。
简略版:
按下开机键;
在核心启动项按下“e”键;
在启动脚本引导语句后填入rd.break;
按下ctrl+x,等待系统引导至switch_root模式重新可输入;
输入命令:mount -o remount,rw /sysroot
输入命令:chroot /sysroot
输入命令:echo "YourNewPassWord" | passwd --stdin root
输入命令:touch /.autorelabel
输入命令:exit
输入命令:reboot
以上方法,取自官方文档。其中有一些需要注意的地方
解析步骤:
了解开机修改密码的过程,本质上是对linux启动过程,以及安全上下文(SELinux)的研究,因为这两个方面都很深奥,所以本文只针对修改开机密码时发生的事情简单的研究下所涉及到的这两方面的应用,并不涉及到更高深的原理,如果想深入研究,请自行探索。
一.需要预先知道的一些事情
Linux开机最简化流程:供电-BIOS-initframfs-内核-shell
SELinux状态:强制、许可、禁用
二.分步
0.首先看下你的系统是否适合使用此方法
执行命令:
cat /etc/redhat-release
会返回当前系统版本
如图,系统版本是CentOS 7.9 Centos7 以下的系统不适用此方法,因为开机启动方式和引导方式都有变化
1.开机时,在系统核心按下“e”键。
此处是GRUB2菜单,也就是centos采用的bootloader。
根据文字提示,第一个就是,下方的时支援核心,用于系统故障排查,如果这一步看到了多个核心,说明该清理无用核心了,这种情况可能是更新造成的,请根据实际情况清理你的系统。
此页提示:使用方向键上下改变选项,按下e键编辑所选项,或者按下c打开命令提示符,开机时请尽快选择,此页只会停留5s,之后自动启动核心,按任意键取消五秒倒计时,改为手动操作。
2.在引导语句后方填入rd.break
按下E键后,看到的会是这样一段脚本,但是没有启动项这一行
用方向键下,往下滚动。。。
框选出来的,linux16开头的这行便是核心项目,可以看出指定了一些文件、地址和参数.
在末尾添加 rd.break 注意与前方有一处空格。
如果是虚拟机,有可能会出现之后无法进入switch_root的问题,rd.break后添加console=tty0即可,原因是因为虚拟机有可能会无法对接正确的终端,也就是没把控制台传递给显示的终端导致用户看不到。加上这一句,直接告诉系统直接走tty0这个当前终端的别名上,tty0会收容所有的系统信息,解决这个问题。
如果想看到全部的启动信息,还需要再去掉前面的rhgb、quiet参数,rhgb(redhat graphics boot)把启动过程中的文本信息用图片替代,quiet会过滤掉一些硬件自检信息
我使用虚拟机测试没遇到过这些问题,现在也不想接受那些信息,所以只需要添加rd.break即可,如需添加以上命令,命令之间用空格隔开。
为什么是rd.break
此步骤是在引导内核启动阶段前期使用rd.break中断,利用initramfs获取到一个root shell。
系统早期启动时,引导加载程序(bootloader)会在内存中加载vmlinuz(可引导的内核)和initramfs(内存虚拟文件系统)。因为当启动初期,核心需要动态加载的模块在磁盘中中,但磁盘本身也是通过这些模块才能正常使用(被挂载),所以一个现在使用的解决方案:使用一个虚拟文件系统,让内核可以加载驱动和启动需要的检测、服务。
initramfs会被挂载为rootfs,挂载点为/,就是根文件系统。内核通过提供的程序接口加载这些文件系统与磁盘接口的驱动核心模块,然后内核会检测是否存在init程序,如果有就会执行,init直接将控制权交给systemd去调用target加载需要的服务(Centos7以后),我们就在这个时机停下,告诉init不要转交控制权。这样引导启动就不会继续转移控制权,此时真正的内核文件系统还没有根文件系统,initramfs具仍有root权限,可以给我们提供修改密码的必要的条件。(此处设置仅为临时的控制,仅供下次启动时可以中断并继续操作,不是永久修改)
3.按下组合键ctrl+x,系统重新引导至switch_root模式
根据上一步,屏幕下方的提示,按下ctrl+x重启,系统本次重启就会携带上我们自己编辑好的参数启动,进入到switch_root模式,switch_root又是什么?
根据上一步所说,系统在启动过程中被中断,没有继续交给systemd启动,那么这时候就需要我们手动来实现根节点的挂载,根据截图,会发现命令行身份标识是# 证明我们在rootfs中,具有最高权限,但此时我们也没有进入到需要的linux核心中,initramfs并不是我们需要修改密码的地方,我们需要切换到核心中去操作。
switch_root:是busybox提供的一个切换跟的解决方案,可以让我们很方便的通过命令直接切换到需要的根节点。此命令必须由PID=1的进程来调用,而我们目前所使用的进程正是init,PID=1
4.重新挂载核心节点,切换跟
我们可以先来看下目前的挂载情况,使用mount即可查看
列出了全部挂载情况,框选处,我们可以看到 root 被挂载在/sysroot处,接着往后看详细信息 描述了它的读写情况为只读,readonly,我试了下在这种情况下直接改密码,提示了一些乱码,应该是描述了错误信息,无视并启动之,发现修改的密码无效,旧密码能够正常进入,说明修改失败,所以此处还是需要先把核心挂载为可读写状态才可以修改。
1.首先需要重新挂载已经挂载的根,命令如下:
mount -o remount,rw /sysroot
2.挂载好后,将跟修改为sysroot/,命令如下:
chroot /sysroot
如果不放心,可以重新挂载后,再次执行mount查看下是不是已经可读写了
当看到此处,我们已经进入到了核心的挂载点。
5.修改密码并创建自动恢复标记
切换好跟后,我们就可以修改密码了。修改密码后,还要再创建一个自动恢复的文件.autorelable,这是什么?为什么要创建它?先看下操作步骤...
1.使用passwd命令来重新创建密码,命令如下
echo "YourNewPassWord" | passwd --stdin root
2.在根目录下创建自动恢复标记文件,命令如下
touch /.autolabel
修改密码没什么可说的,但是为何要创建.autorelable文件,还是需要先大概了解下SElinux是什么,最简单的解释是Linux通过这样一个核心模块,确保整个文件系统的安全性。是Linux的子系统。如果希望手动控制策略,涉及到的内容非常复杂,但是用户可以简单的命令和修改他的主配置文件去修改当前运行的基本状态,其中,安全上下文是SElinux的核心部分,他要求进程访问的文件上下文标记必须对应上,而这个标记是由SElinux自动标记的,不需要我们手动干涉,但是一些操作会让安全上下文完整性遭到破坏,比如我们在开机时修改密码的操作。
我们使用passwd命令去修改密码时,会创建一个新的/etc/shadow文件,然后把SELinux标记应用于新的文件上,再把老的shadow文件复制过来并写入新密码。这个步骤本身没有问题,但是在此时,并没有实际运行一个真正地操作系统,多数进程包括SElinux都没有运行(这里可以使用getenforce来看下状态,会发现状态是Disabled)。我们修改密码的过程并没有被验证过,新的shadow文件不具备上下文安全标识,在SElinux没有运行时是没有问题的,但问题是SElinux默认开启,且运行策略为强制执行,也就是说当我们在开机时,会出现修改密码后的文件因为上下文的原因无发被使用,直接造成的后果是无论使用新旧密码都无法进入系统。
但是有几种方式可以解决这个问题,首先是在根目录创建.autorelable,这会使SElinux在开机时重新标记整个文件系统,当然就包含我们修改密码时影响的shadow文件,同时这也是最安全,且由redhat官方推荐的方式。我测试使用这个方法恢复,系统会在initramfs重启后先经过一次标记过程,完成后再次重启,开机后在/下无法找到这个文件,目前猜测可能是流程结束后被SElinux自动移除。整体过程耗时比较长,在虚拟机新创建没有任何其他文件的情况下,从执行重启命令到开机需要四分钟左右(精简安装),而尤其对硬件性能不加或者文件过多的情况下尤其明显,所以我们可能并不会采用这种方式,使用的其他方式,下文在介绍。
6.退出并重启
这一步就是从临时跟系统中退出,在initramfs中执行重启,没有什么可多说,使用对应命令即可。
1.退出,命令如下:
exit
此时回到switch_root
2.重启
reboot
重启后可以使用修改后的密码登录root账户
修改密码的其他方式
一. 手动恢复文件
我们可以使用一些方式跳过SElinux对系统的整体重新标记,首先要修改下之前的步骤,前面步骤不变,到修改密码后,不需要再执行touch /.autorelabel这步操作,直接执行退出重启。
当再次开机到GRUB时,继续按e编辑内核启动项,但这次只添加一句:enforcing=0 即可,这条命令代表将SElinux运行模式调整为许可模式,开机时携带此参数,那么在本次开机时,SElinux的运行模式就会被调整。
此时开机就可以使用之前修改后的密码登录了。启动后使用getenforce检查一下,发现运行状态是许可模式,进一步查看配置文件,发现仍然是强制模式,因为我们只是在本次开机修改了模式,并没有将他写在配置中,只是利用许可模式不强制执行策略的特点进入系统中,此时要做的是手动回复上下文不完整的文件。
可以看到当前是Permissive运行,但配置仍然是enforcing。
我们可以通过restorecon命令来手动恢复文件的上下文,这里可以选择只恢复/etc/shadow这个文件,也可以恢复一个目录,如果恢复文件,只需要执行
restorecon /etc/shadow 即可,但是恢复目录需要加入 -R 参数,代表递归,如需查看过程,还需要加入 -v
restorecon -Rv /etc,这样就可以了,之后可以使用setenforce命令手动将运行模式更改为强制模式,或者直接重启都可以。
二.init=/bin/bash
如果在开机时,指定init指向一个shell,那么系统会在PID一号进程处为我们启动一个shell,同样可以修改密码。
按下开机键后,在内核引导语句后加入 init=/bin/bash,然后按下ctrl+x引导启动。之后的操作见下图
同样的,我们需要挂载根节点为可读写,否则无法修改,但是请注意这里挂载点是/,不是/sysroot
接下来是修改密码和创建relabel,但之后的启动需要注意,这里不能使用reboot或者init来重启或者继续启动,如果直接使用命令,返回的提示命令未找到,这是因为此时我们使用的是一个默认的bash环境,环境变量的缺失导致大量命令无法被系统找到。
此时能够使用的启动方法就是 exec 命令,通过指定命令的绝对路径来执行,但同样存在问题,经测试发现,exec 只能通过指定init命令的绝对路径继续启动,但无法执行reboot重启,以下是我的猜测:在bash下,进入到/sbin中,执行ls -al | grep "init" 以及替换成reboot 会发现,init指向的是/lib/systemd/systemd,而reboot指向的则是/bin/systemctl
在默认bash的环境下,因为环境变量的缺少,无法通过systemctl管理服务,导致reboot无法执行,因为reboot命令是systemctl的链接。但可以执行init,通过systemd来继续启动系统,正常开机验证一下,发现开机后是可以通过reboot的路径,使用exec正常执行的。这是我的猜测,是否正确需要以后通过了解linux启动过程和服务管理在确定。
但这里还存在一个问题。如果之前为了节省时间,没有创建.autorelabel的话,此时就需要再次手动重启并添加enforcing=0来启动,否则也无法使用新密码开机,所以如果使用指定bash方式修改密码,最好还是添加relabel。
杂谈
一.SElinux在initramfs时,是未运行的。
SElinux是linux内核中的一些模块,但此时内核也没有加载启动,自然就不会启动SElinux的相关进程,这时候使用命令修改SElinux的状态等操作,也是无意义的。
二.禁用或者许可模式下修改密码,不会出现上下文问题。
前面说了,密码失效是因为开机时强制执行SElinux策略,但是如果SElinux已经被调整为禁用或者许可模式,那自然不会有任何阻拦,但redhat不建议彻底禁用SElinux,因为许可模式同样满足我们的需求,而且会把一些异常行为记录下来,方便以后恢复强制模式时排错。
三.为什么不使用GRUB菜单中提供的紧急模式
紧急模式同样需要密码才可以进入
四.在修改开机启动项目时,如果将此行中“ro”改为“rw”,再次引导重启时,根节点直接就会是可读写,不需要再挂载一次
涉及到的命令
1. rd.break:我没有查到这样一条命令,但是有人说这里rd部分表示初始化虚拟内存盘,就是把initramfs解压到内存中并挂载为rootfs/这部分操作。没错,这部分还没有到硬盘,文件系统也是运行在内存中的,就是指的虚拟内存硬盘(ramdisk)。在这里给出一条break指令,那自然就是挂载后中断,不在继续进行。
2. mount:用于挂载文件系统。
格式:mount [-lhV]mount -a [-fFnrsvw] [-t vfstype ] [-O optlist ]mount [-fnrsvw] [-o options [,...]] device | dir mount [-fnrsvw] [-t vfstype ] [-o options ] device dir
描述:Unix 系统中,所有可以存取的文件都被组织为一个很大的树形结构,称为文件层次结构 (file hierarchy),以 /为根。这些文件可以分布在多个设备上。 mount命令用作将设备上的文件系统挂接到这个树形结构上。
用到的选项:-o: 用来指定后面跟着的挂载项。
remount:试图重新挂载一个已经挂载的文件系统。这个选项通常用于改变文件系统的挂载标志,尤其是使一个只读文件系统变为可读写。它不会改变设备或者挂载点。
rw:挂载文件系统为可读/写。
这里使用了逗号连接 -o 后面的两个挂载项 remount以及rw
3. chroot:run command or interactive shell with special root directory,用特殊的根运行命令或者运行交互式shell,切换跟。
格式:chroot [OPTION] NEWROOT [COMMAND [ARG]...]
chroot OPTION
描述:在根目录或者切换到新的根运行命令.
用到的选项:无
4. touch:主要有两个作用,这里主要用到的是创建文件的功能,但是原文这里是:touch - change file timestamps(修改文件时间戳)
格式:touch [OPTION]... FILE...
描述:将每个文件的访问和修改时间更新为当前时间,不存在的文件参数将被创建为空,除非提供了-c(--no-create 不创建任何文件)或-h。(--no-dereference 只影响符号链接本身,而非符号链接所指示的目的地(当系统支持更改符号链接的所有者时,此选项才有用))
用到的选项:无
5. echo:输出一行文本
格式:echo [SHORT-OPTION]... [STRING]...
echo LONG-OPTION
描述:将字符串回显到标准输出。
用到的选项:无,直接文本,这里直接手动输入新密码,当作标准输出,通过管道符 | 传递给后面的passwd
6. passwd:更新用户身份认证令牌,就是改密码
格式:passwd [-k] [-l] [-u [-f]] [-d] [-e] [-n mindays] [-x maxdays] [-w warndays] [-i inactivedays] [-S] [--stdin] [username]
描述:passwd实用程序用于更新用户的身份验证令牌。这个任务是通过调用linuxpam和Libuser API来实现的。本质上,它使用Linux PAM将自身初始化为“passwd”服务,并利用配置的密码模块进行身份验证,然后更新用户的密码。
用到的选项:--stdin:此选项用于指示passwd应该从标准输入(可以是管道)读取新密码。
7. restorecon:还原文件的默认SELinux安全上下文。就是修复
格式:restorecon [-R] [-n] [-p] [-v] [-e directory] pathname...
restorecon -f infilename [-e directory] [-R] [-n] [-p] [-v] [-F]
描述:此程序主要用于设置一个或多个文件的安全上下文(扩展属性)。它还可以在任何其他时间运行以更正不一致的标签,添加对新安装的策略的支持,或者使用-n选项被动地检查文件上下文是否都已设置由活动策略指定(默认行为)。
用到的选项:修复文件夹则至少添加-R
8. setenforce:修改SELinux正在运行的模式。
格式:setenforce [Enforcing|Permissive|1|0]
描述:使用强制或1将SELinux置于强制模式。使用Permissive或0将SELinux置于Permissive模式。
用到的选项:自己选择合适的。
9. getenforce:获取SELinux的当前模式
格式:getenforce
描述:报告SELinux是强制、许可还是禁用。
用到的选项:无
10.exec:调用并执行指定命令
格式:exec (选项) (参数)
描述:用于调用并执行指令的命令,通常用于shell脚本中调用其他命令,如果在当前终端中执行,则当前指定的命令执行完毕后会立即退出终端
用到的选项:无
参考文档
Redhat官方文档修改密码介绍: Chapter 9. Changing and resetting the root passwor(redhat.com)
Redhat官方文档修改SElinux状态介绍:在启动时更改 SELinux 模式 (redhat.com)
知乎文章-重置密码:Reset RHEL root密码 - 知乎(zhihu.com)
评论区有人讲了下shadow文件相关的问题:RHEL7: Interrupt the boot process in order to gain access to a system. - CertDepot
大概说了下switch_root模式:switch_root 命令_lbaihao的专栏-CSDN博客
切换跟的方式的简介: chroot,pivot_root和switch_root 区别_u012385733的专栏-CSDN博客
为何不使用系统提供的紧急模式:[QUESTION] rd.break vs emergency.target : redhat (reddit.com)
简述linux终端、虚拟终端、控制台:linux下 console ,tty 和tty0有什么区别!_骠姚校尉的博客-CSDN博客
console=tty*的含义:uboot传递参数'console=ttyXXX'的作用 - _小百 - 博客园 (cnblogs.com)
使用单用户模式修改密码的方式: CentOS7单用户模式修改密码_从入门到放弃-CSDN博客
另一篇rd.break流程,大同小异: How To Reset Root User Password In CentOS/RHEL 7 (rootusers.com)