linux基础学习(1)- 用户和用户组管理

简介

本手册的目的

本手册主要用来学习linux的,对于linux的学习开始于我的大学时代,刚开始的目的仅仅是为了不玩游戏而想要抛弃windows阵营,但在office、学校管理系统、CAD尤其是腾讯系软件等在linux系统下的糟糕体验而最终没有坚持下来。
这一次重新检起linux的学习,希望在学习的同时将其整理成手册以便以后查阅以及督促自己的学习。

发行版的选择

首先我们给出一个发行版的时间线:

发行版时间线

发行版时间线图表网站

发行版介绍

从上面的图表中可以看到,发行版实在太多了。你可以在DistroWatch.com网站上查询到几百个不同的发行版。
但是我们还可以发现linux发行版可以分为三个主要的分支:RedHat、Slackware和Debian。每一个分支下都拥有一个最具代表性的商业服务器级的发行版。分别对应的是Red Hat Enterprise Linux简称RHEL,SUSE Linux Enterprise简称SUSE,以及Ubuntu。接下来我们来分别介绍下这几个发行版:

  1. RedHat: 其本身是一个收费的发行版,当然也可以免费使用,但是你将得不到任何系统升级服务,也得不到任何技术支持。其包管理方式采用基于RPM包的YUM包管理方式。其重点在稳定性而非持续跟新上,所以其软件仓库中的软件比较少而且都相对比较老。通常被用于服务器端。
    1. CentOS:该发行版是利用完全免费的RHEL的源代码重新编译而成的,免费提供给大家使用,所以本质上与RHEL没有什么区别。唯一的区别就是更新的频率没有RHEL快。
    2. Fedora:可以说是RHEL的试验田,RedHat将一些新的技术应用到该发行版中,以便观察是否稳定,当该技术稳定后会加入到下一版的RHEL中,所以通常fedora稳定性会比较差,最好只用于桌面系统。
  2. Debian: 是社区Linux的典范,是迄今为止最遵循GNU规范的Linux发行版。其包管理采用apt/dpkg的方式,采用DEB包形式。其分为三个版本分支:stable为最稳定最安全版本通常用于服务器,unstable为最新的测试版本,包含最新的软件包,相对的也具有最多的bug。testing版本都经过unstable中的测试,相对比较稳定也支持不少新技术,一般用于桌面。
    1. Ubuntu: 基于Debian的unstable版本加强而来。继承了Debian的优点,并且拥有自己独特的优势。可以说是拥有最多使用人群的linux发行版本。
      1. Elementary OS: 基于Ubuntu而来,被誉为最美linux发行版。其桌面环境称为Pantheon基于Gnome构建。
      2. Deepin: 基于Ubuntu而来,专为国人打造的Linux发行版。由深度科技开发,其中集成了深度音乐、深度截图、深度视频等程序。并且其应用程序仓库中包含QQ、阿里旺旺等国人必不可少的程序。
      3. Linux Mint: 提供了经典桌面配置的现代发行版,如果是从windows转换过来的新手,可以使用这个发行版本,他提供了普通家庭日常所需的几乎所有的应用。
  3. Slackware: 与其他开发版不同,它坚持KISS(Keep It Simple Stupid)原则,也就是说没有任何配置系统的图形界面工具。在一开始使用的时候会相对困难些,但是会更加的灵活和透明。Slackware没有如RPM DEB之类的软件包管理方式。其软件包都是通常的tgz格式文件再加上安装脚本来完成的。
    1. openSUSE: 由德国一家公司开发的发行版。以Slackware为基础,并提供了完整的桌面环境和图形程序支持。
  4. Arch Linux: 该发行版本采用滚动升级的策略,其尽力保持软件处于最新的稳定版本,只要不出现系统软件包破损都尽量使用最新的版本。所以他的软件仓库也包含最全的软件。其仓库中即提供开源、自由的软件,也包含闭源软件。其宗旨是实用性大于意识形态。Arch采用Pacman包管理系统。而且其官方Wiki以及社区我感觉是最好的。
    1. manjaro: 该发行版基于Arch创建,其提供了一个更简单的方法来安装和使用基于Arch的发行版。Arch虽然是一个前瞻性很强的Linxu发行版,但对于新手并不太友好,而Manjaro就是为了解决这个问题而出现的。

本手册使用的发行版

看似很难抉择,但实际上没有什么,不管是谁家的发行版,其本质都是一样的。使用的都是linux内核。所谓的发行版只不过是给这个内核加上一堆应用程序而组成一个操作系统而已。而这些应用程序很大一部分都是来源于GNU社区,代码都是一样的,所以其内在并没有多大的差别。
其主要的差别体现在管理工具的选用上。不同的发行版可能对某种特性有不同的偏好。尤其是包管理的不同。目前比较主流的包管理有YUM红帽系,APT debian系以及pacman Arch系。
所以针对于桌面系统:yum系使用fedora,apt系使用ubuntu、elementary OS、Deepin等,pacman系使用manjaro。而针对与服务器可以使用centos ubuntu server等。
本手册采用manjaro作为学习机,具体为manjaro18.1.5版本。之所以采用该发行版纯粹是因为好看,并且pacman实在太好用了,也不能说pacman好用而是社区的力量是强大的,提供了诸多软件供pacman使用。

安装系统

参看我其他的博文

图形界面

linux内核本身是没有图形界面的。实际上linux只是一个内核。而我们之所以能通过图形用户界面来使用linux,是因为一个软件提供了该功能。
其中图形界面有几个概念经常混淆: X/X11 XFree86/Xorg KDE/GNOME。我们通过对这三个概念的解释来说明linux的图形界面是什么。

X不是程序而是协议

X是一个协议,就像HTTP协议、IP协议一样。一个基于X协议的应用程序需要运行并显示内容时他就连接到X服务器,开始用X协议与服务器交谈。比如一个X应用程序要在屏幕上输出一个圆,他就用X协议告诉X服务器。
而X11 代表的是X协议的第十一个版本。类似于HTTP 1.1

XFree86是一个实现了X协议的服务器软件

XFree86是一个软件,他具体实现了X协议。就相当于Apache实现了HTTP协议一样。起初XFree86是以GPL许可发行的,后来开发商更改了许可协议,这就引起了GNU社区的不满,于是从XFree86 4.4 RC2衍生出了xorg。目前几乎所有开源的类Unix系统都使用xorg。

有了服务器还需要有X客户端程序

类似于Apache实现了HTTP协议的服务器软件,而浏览器就是一个客户端程序。同理xorg是一个实现了X协议的服务端软件,也就是需要一个客户端软件通常我们称之为窗口管理器(window manager WM)。其显见的作用就是绘制鼠标位置、应用程序的移动、最小化、最大化等。

图形界面操作环境是一系列软件的集和

KDE/GNOME是linux的图形界面操作环境。他们不仅有一个窗口管理器(KDE的WM是KWin,GNOME的WM是Metacity),还有很多配套的应用软件和方便使用的桌面环境,比如任务栏、开始菜单、桌面图标等。

DM又是什么鬼

对于那些默认使用图形用户界面的Linux系统,还有一个十分重要的X客户端需要启动,就是显示管理器(Display Manager DM)。这个是专门负责图形界面的用户登陆问题的。也就是说,系统启动之后第一个要启动的X客户端程序就应该是DM,而且没有人可以关闭它。

linux用户和用户组管理

多用户多任务分时操作系统

  • 多用户:可以让多个人使用同一台电脑而且不能互相窥探对方的秘密。
  • 多任务:当你使用电脑的时候可以一边听音乐一边玩游戏。
  • 分时:将电脑的时间资源适当分配给所有使用者身上,让所有使用者有独占机器的感觉。
    通常我们说一个操作系统会说他支持多用户多任务。这是因为一个分时系统,支持多任务是其与生俱来的本质。

用户的身份

Linux从诞生之日起就是多用户的,而对多用户的管理简单来说就是管理用户的等级以及用户对文件的访问权限。
Linux下用户等级分为两个等级:root和非root。root用户在linux下拥有至高无上的权力,所以一个系统只能有一个root用户其用户名就是root。而非root用户的权力是受限的,只能访问由root规定的文件或者自己的文件。

理解用户角色

linux是一个多用户操作系统,所以可以在linux中添加n多的用户,具体n的数字为2的32次方个。
在linux系统中还有一些用户是用来完成特定任务的,比如nobody、admin、ftp等。需要注意的是在Linux系统中无论这些用户多么厉害多么特殊,主要不是root,他就一定是普通用户,权力大小都是相同的。
虽然用户角色不能更权限靠上关系,但是不同的角色还是有待遇区别的,所谓的待遇就是是否拥有密码、home目录以及shell这些资源。例如nobody用户可以用于Nginx的工作进程。对于这类用户一般是不分配密码和shell的。甚至连home目录都没有。究其原因就是很多服务程序默认使用这些用户,如果设置了密码,程序就无法自动运行了。其次因为不会有人使用这个用户登陆系统也就没有必要分配shell以及设置home目录了。

/etc/passwd文件查看用户

该文件是系统用户配置文件,存储了系统中所有用户的基本信息。并且所有用户都可以对此文件执行读操作。

[hncjygd@hncjygd-pc ~]$ cat /etc/passwd
root:x:0:0::/root:/bin/bash
nobody:x:65534:65534:Nobody:/:/usr/bin/nologin
dbus:x:81:81:System Message Bus:/:/usr/bin/nologin
bin:x:1:1::/:/usr/bin/nologin
daemon:x:2:2::/:/usr/bin/nologin
mail:x:8:12::/var/spool/mail:/usr/bin/nologin
ftp:x:14:11::/srv/ftp:/usr/bin/nologin
http:x:33:33::/srv/http:/usr/bin/nologin
systemd-journal-remote:x:982:982:systemd Journal Remote:/:/usr/bin/nologin
systemd-network:x:981:981:systemd Network Management:/:/usr/bin/nologin
systemd-resolve:x:980:980:systemd Resolver:/:/usr/bin/nologin
...
tss:x:967:967:tss user for tpm2:/:/usr/bin/nologin
usbmux:x:140:140:usbmux user:/:/usr/bin/nologin
hncjygd:x:1000:1000:hncjygd:/home/hncjygd:/bin/bash

可以看到,passwd文件中的内容非常规律,每一行对应一个用户。默认情况下Linux系统有非常多的用户,这些用户中的绝大多数是系统或服务正常运行所必须的用户通常被称为系统用户或伪用户。这些用户无法登陆也不能删除,一旦删除依赖于这些用户的服务或程序就不能正常运行了。
每一行表示一个用户其具体含义如下:

用户名:密码:UID:GID:描述性信息:家目录:默认shell

  • 用户名:一串代表用户身份的字符串
  • 密码:全部都是x表示,并不是真正的密码,真正的密码保存在/etc/shadow文件中。虽然x并不代表真正的密码,但是也不能删除,如果删除了x那么系统会认为这个用户没有密码,从而导致只输入用户名而不用输入密码就可以登陆。
  • UID:这是用户ID,每个用户都有唯一的UID,Linux系统通过UID来识别不同的用户。实际上UID就是一个0~65535之间的数。其中有一些约定俗成的规定:
    • 0:超级用户即root的UID
    • 1~999:系统用户,此范围的UID保留给系统使用。每个发行版可能不一样
    • 1000~65535:普通用户,例如你新建的第一个用户的UID就是1000.
  • GID:用户组ID。其也是一个0~65535之间的数。其规定与UID差不多。但是还有一些不同的概念:
    • 初始组:在建立一个新用户的时候通常用该用户的用户名创建一个以该用户名相同的组名作为该用户的初始组。每个用户的初始组只能有一个
    • 附加组:指用户可以加入多个其他的用户组,附加组可以有多个
    • passwd文件中的GID是用户初始组的GID
  • 描述性信息:这个字段没有什么重要的用途,默认与用户名相同。
  • 家目录:默认情况下为/home/[userName]
  • 默认的Shell:shell就是Linux的命令解释器,是用户和Linux内核之间沟通的桥梁。通常你新建的用户使用linux系统默认使用的命令解释器bash(/bin/bash),当然也可以使用sh、csh等。我们也可以将这个字段更改为其他程序:
    • /bin/bash 表示该用户可以使用普通用户的所有权限
    • /sbin/nologin 改为此表示用户不能登陆
    • /usr/bin/passwd 表示用户可以登陆,但登陆后只能修改自己的密码
    • 这里并不能随便写入和登陆没有关系的命令例如写成 /bin/ls ,系统并不能识别这个命令,同时也就意味着这个用户不能登陆

/etc/shadow文件

/etc/shawdow文件,用于存储linux系统中用户的密码信息,又称为影子文件。
在以前密码是存放在/etc/passwd文件,由于该文件允许所有用户读取,容易导致用户密码泄露。因此Linux系统将用户的密码信息从passwd文件中分离出来,并单独放到了此文件中。而/etc/shadow文件只有root用户才拥有读权限。

[hncjygd@hncjygd-pc ~]$ sudo cat /etc/shadow
root:$6$2sICgKsL37jQyg5b$Kz.Np6BqqUjMlY60tPsdxkCMxUpmCGbArH4nr/eueEJ053XpdCI3q4zTj6.1h6d31rjAIv4JthcVoeUVjXheS/:18277::::::
nobody:!!:18259::::::
dbus:!!:18259::::::
bin:!!:18259::::::
daemon:!!:18259::::::
mail:!!:18259::::::
ftp:!!:18259::::::
...
tss:!!:18259::::::
usbmux:!!:18259::::::
hncjygd:$6$KBiD/hKgfEtp33Ak$Dtu6nnp0X87ysZfA6CSVHL5RyK/VAYfjLnul7Mhh/xXtS0JxWPaM8R0RhFhmy90fs9/zH1LeB3G7ydwTiIgm01:18277:0:99999:7:::

其每行也代表一个用户,其具体信息为:

用户名:加密密码:最后一次修改时间:最小修改时间间隔:密码有效期:密码需要变更前的警告天数:密码过期后的宽限时间:帐号失效时间:保留字段

  • 用户名:与/etc/passwd文件相同
  • 加密密码:目前linux使用SHA512加密密码。一些系统用户的密码是!!或*,这代表没有密码。如果你在创建用户时不设置密码的情况下该字段也为!!
  • 最后一次修改时间:表示最后一次修改密码的时间。在linux中计算日期的时间是以1970年1月1日作为1不断累计得到的时间。到了1971年1月1日则为366。
  • 最小修改时间间隔:该字段规定从最后一次修改时间算起,多长时间不能修改密码,如果是0就表示密码随时可以修改,如果是10则表示10天后才能修改密码。
  • 密码有效期: 表示在最后一次修改时间算起多长时间内需要再次变更密码。默认为999999也就是273年,可以认为永久生效。管理服务器时,通过这个字段强制用户定期修改密码。
  • 密码需要变更前的警告天数:当帐号密码有效期快到时,系统会发出警告信息告诉该账户需要修改密码
  • 密码过期后的宽限天数:如果密码过期后没有被修改,允许宽限的天数。如果还没有修改系统将不再让此账户登陆。
  • 帐号失效时间:同第三个字段相同,使用自1970年1月1日以来的总天数作为帐号的失效时间,该字段表示,帐号在此字段规定的时间之外,不论你的密码是否过期,都将无法使用。该字段通常被使用在具有收费服务的系统中。
  • 保留:这个字段目前没有使用

/etc/group

该文件时用户组配置文件。对应于/etc/passwd文件中每行用户的GID。

[hncjygd@hncjygd-pc ~]$ cat /etc/group
root:x:0:root
adm:x:999:daemon
wheel:x:998:hncjygd
kmem:x:997:
tty:x:5:
utmp:x:996:
audio:x:995:
...
sddm:x:968:
tss:x:967:
usbmux:x:140:
hncjygd:x:1000:

可以看到,每一行代表一个用户组。其含义为:

组名:密码:GID:该用户组中的用户列表

  • 组名:用户组的名称
  • 组密码:x仅仅是作为表示,具体密码位于/etc/gshadow文件中。这个密码主要用来指定组管理员的,由于系统中的帐号非常多,root用户可能没有时间进行用户的组调整,这时可以给用户组指定管理员,如果有用户需要加入或退出某个用户组,可以由该组管理员替代root进行管理。这个功能目前很少使用了,也就很少在设置组密码了。如果需要赋予某个用户调整某个用户组的权限,可以使用sudo命令替代。
  • GID:组的ID.Linux系统是通过GID识别组的,而组名只是为了便于管理员记忆。
  • 组中的用户:此字段列出了每个组中的用户。需要注意的是如果该用户是这个用户的初始组,则该用户不会写入这个字段。多个用户是用逗号隔开的。

/etc/gshadow文件

该文件存储了组用户的密码信息。

[hncjygd@hncjygd-pc ~]$ sudo cat /etc/gshadow
root:::root
adm:!!::daemon
wheel:!!::hncjygd
kmem:!!::
tty:!!::
...
sddm:!!::
tss:!!::
usbmux:!!::
hncjygd:!::

其每行代表一个组用户的密码信息:

组名:加密密码:组管理员:组附加用户列表

  • 组名:同/etc/group
  • 加密密码:通常不是组密码,该这段通常为空。有时也为!指该群组没有组密码也不设置管理员。
  • 组管理员:从系统管理员的角度来说,该文件最大的功能就是创建群组管理员。群组管理员的作用就是分担root任务,当某个用户想要加入某群组的时候,root来不及回应的时候可以让群组管理员来管理。在目前由于sudo的使用,该功能已经很少被提及了。
  • 组中的附加用户:与/etc/group相同

管理用户和组

linux系统为用户和组的增删改查提供了一些基本的命令。这些命令的作用机制就是对/etc/passwd /etc/group则两个文件进行增删改查来完成的。外加一个/etc/shadow /etc/gshadow两个文件来专门保存相关的密码。

管理用户

useradd添加用户

adduser/useradd 这两个命令都可以。通常我们使用useradd
基本格式: useradd [选项] 用户名

选项 含义
-u 手动指定用户的UID
-d 手动指定用户的主目录
-c 手动指定用户说明信息
-g 手动指定初始组
-G 指定用户的附加组
-s 手动指定用户的shell,默认为/bin/bash
-e 指定用户的失效日期,格式为YYYY-MM-DD
-m 建立用户时强制建立用户的家目录,在建立系统用户时,该选项时默认的
-r 创建系统用户,也就是UID在1~999之间,公系统程序使用,该参数默认不会创建家目录

useradd usertest #以默认设置创建用户
useradd -u 1100 -g usertest -c "test user" usertest1 #设置相应的参数来创建一个用户

默认参数由useradd的两个配置文件中定义:/etc/default/useradd文件以及/etc/login.defs文件

/etc/default/useradd文件

其中

# useradd defaults file for ArchLinux
# original changes by TomK
GROUP=users
HOME=/home
INACTIVE=-1
EXPIRE=
SHELL=/bin/bash
SKEL=/etc/skel
CREATE_MAIL_SPOOL=no

我们来逐一介绍:

  • GROUP=users:这个选项用于建立用户的默认组。也就是说,在添加每个用户时,用户的创建与用户名相同的组来作为用户的初始组(Linux中默认用户组有两种机制:一种私有用户组机制,即manjaro这种,另一种就是公共用户组机制例如设置GRPUP=200,那么新建的用户的初始组ID都是200)
  • HOME=/home:指定用户主目录的默认位置,所有新建用户的主目录默认都在/home下
  • INACTIVE=-1:指的是密码过期后的宽限天数,也就是/etc/shadow文件的第七个字段,默认-1,代表所有新建的用户密码永远不会失效
  • EXPIRE= :表示密码失效时间,也就是/etc/shadow文件的第八个字段,默认为空,代表永久有效
  • SHELL=/bin/bash:表示新建的用户默认的Shell
  • SKEL=/etc/skel:在创建一个新用户后,你会发现,该用户主目录并不是空目录,而是有.bash_profile .bashrc等文件,这些文件都是从/etc/skel目录中自动复制过来的
  • CREATE_MAIL_SPOOL=no:指的是给新建用户建立邮箱,默认是不创建。如果是yes对于所有新建用户系统都会在/var/spool/mail/目录下创建和用户名相同的邮箱。

要想更改这个文件的内容,除了直接使用vim来改写这个文件,linux也为用户提供了useradd -D参数来修改这个文件中的内容。

选项 含义
-b 设置HOME=的内容
-e 设置EXPIRE=的内容,参数格式为YYYY-MM-DD
-f 设置INACTIVE=的内容,参数为整数
-g 设置GROUP=可以是UID也可以是组名
-s 设置SHELL= 的内容
/etc/login.defs文件

该文件是shadow密码套件配置。也是创建用户时对一些基本属性作默认设置。

#
# /etc/login.defs - Configuration control definitions for the login package.
#
# Three items must be defined:  MAIL_DIR, ENV_SUPATH, and ENV_PATH.
# If unspecified, some arbitrary (and possibly incorrect) value will
# be assumed.  All other items are optional - if not specified then
# the described action or option will be inhibited.
#
# Comment lines (lines beginning with "#") and blank lines are ignored.
#
# Modified for Linux.  --marekm

#
# Delay in seconds before being allowed another attempt after a login failure
#
FAIL_DELAY              3

#
# Enable display of unknown usernames when login failures are recorded.
#
LOG_UNKFAIL_ENAB        no

#
# Enable logging of successful logins
#
LOG_OK_LOGINS           no

#
# Enable "syslog" logging of su activity - in addition to sulog file logging.
# SYSLOG_SG_ENAB does the same for newgrp and sg.
#
SYSLOG_SU_ENAB          yes
SYSLOG_SG_ENAB          yes

#
# If defined, either full pathname of a file containing device names or
# a ":" delimited list of device names.  Root logins will be allowed only
# upon these devices.
#
CONSOLE         /etc/securetty
#CONSOLE        console:tty01:tty02:tty03:tty04

#
# If defined, all su activity is logged to this file.
#
#SULOG_FILE     /var/log/sulog

#
# If defined, file which maps tty line to TERM environment parameter.
# Each line of the file is in a format something like "vt100  tty01".
#
#TTYTYPE_FILE   /etc/ttytype

#
# If defined, the command name to display when running "su -".  For
# example, if this is defined as "su" then a "ps" will display the
# command is "-su".  If not defined, then "ps" would display the
# name of the shell actually being run, e.g. something like "-sh".
#
SU_NAME         su

#
# *REQUIRED*
#   Directory where mailboxes reside, _or_ name of file, relative to the
#   home directory.  If you _do_ define both, MAIL_DIR takes precedence.
#   QMAIL_DIR is for Qmail
#
#QMAIL_DIR      Maildir
MAIL_DIR        /var/spool/mail

#
# If defined, file which inhibits all the usual chatter during the login
# sequence.  If a full pathname, then hushed mode will be enabled if the
# user's name or shell are found in the file.  If not a full pathname, then
# hushed mode will be enabled if the file exists in the user's home directory.
#
HUSHLOGIN_FILE  .hushlogin
#HUSHLOGIN_FILE /etc/hushlogins

#
# *REQUIRED*  The default PATH settings, for superuser and normal users.
#
# (they are minimal, add the rest in the shell startup files)
ENV_SUPATH      PATH=/usr/local/sbin:/usr/local/bin:/usr/bin
ENV_PATH        PATH=/usr/local/sbin:/usr/local/bin:/usr/bin

#
# Terminal permissions
#
#       TTYGROUP        Login tty will be assigned this group ownership.
#       TTYPERM         Login tty will be set to this permission.
#
# If you have a "write" program which is "setgid" to a special group
# which owns the terminals, define TTYGROUP to the group number and
# TTYPERM to 0620.  Otherwise leave TTYGROUP commented out and assign
# TTYPERM to either 622 or 600.
#
TTYGROUP        tty
TTYPERM         0600

#
# Login configuration initializations:
#
#       ERASECHAR       Terminal ERASE character ('\010' = backspace).
#       KILLCHAR        Terminal KILL character ('\025' = CTRL/U).
#       UMASK           Default "umask" value.
#
# The ERASECHAR and KILLCHAR are used only on System V machines.
# The ULIMIT is used only if the system supports it.
# (now it works with setrlimit too; ulimit is in 512-byte units)
#
# Prefix these values with "0" to get octal, "0x" to get hexadecimal.
#
ERASECHAR       0177
KILLCHAR        025
UMASK           077

#
# Password aging controls:
#
#       PASS_MAX_DAYS   Maximum number of days a password may be used.
#       PASS_MIN_DAYS   Minimum number of days allowed between password changes.
#       PASS_WARN_AGE   Number of days warning given before a password expires.
#
PASS_MAX_DAYS   99999
PASS_MIN_DAYS   0
PASS_WARN_AGE   7

#
# Min/max values for automatic uid selection in useradd
#
UID_MIN                  1000
UID_MAX                 60000
# System accounts
SYS_UID_MIN               500
SYS_UID_MAX               999

#
# Min/max values for automatic gid selection in groupadd
#
GID_MIN                  1000
GID_MAX                 60000
# System accounts
SYS_GID_MIN               500
SYS_GID_MAX               999

#
# Max number of login retries if password is bad
#
LOGIN_RETRIES           5

#
# Max time in seconds for login
#
LOGIN_TIMEOUT           60

#
# Which fields may be changed by regular users using chfn - use
# any combination of letters "frwh" (full name, room number, work
# phone, home phone).  If not defined, no changes are allowed.
# For backward compatibility, "yes" = "rwh" and "no" = "frwh".
# 
CHFN_RESTRICT           rwh

#
# List of groups to add to the user's supplementary group set
# when logging in on the console (as determined by the CONSOLE
# setting).  Default is none.
#
# Use with caution - it is possible for users to gain permanent
# access to these groups, even when not logged in on the console.
# How to do it is left as an exercise for the reader...
#
#CONSOLE_GROUPS         floppy:audio:cdrom

#
# Should login be allowed if we can't cd to the home directory?
# Default in no.
#
DEFAULT_HOME    yes

#
# If defined, this command is run when removing a user.
# It should remove any at/cron/print jobs etc. owned by
# the user to be removed (passed as the first argument).
#
#USERDEL_CMD    /usr/sbin/userdel_local

#
# Enable setting of the umask group bits to be the same as owner bits
# (examples: 022 -> 002, 077 -> 007) for non-root users, if the uid is
# the same as gid, and username is the same as the primary group name.
#
# This also enables userdel to remove user groups if no members exist.
#
USERGROUPS_ENAB yes

#
# Controls display of the motd file. This is better handled by pam_motd.so
# so the declaration here is empty is suppress display by readers of this
# file.
#
MOTD_FILE

#
# Hash shadow passwords with SHA512.
#
ENCRYPT_METHOD  SHA512

设置项 含义
MAIL_DIR /var/spool/mail 创建用户邮箱的位置
PASS_MAX_DAYS 99999 密码有效期,99999默认273年即永久有效
PASS_MIN_DAYS 0 自上次修改密码以来最少相隔多少天才能再次修改密码
PASS_MIN_LEN 5 指定密码的最小长度,默认为5位,但是现在用户登陆时验证已经被PAM模块取代,所以该选项并不生效
PASS_WARN_AGE 7 指定密码到期前多少天,系统开始通知用户密码将到期
UID_MIN 1000 指定最小UID为1000,也就是说添加用户时默认UID从1000开始。注意:如果手动指定一个用户UID时1100,那么下一个创建的用户的UID会从1101开始,哪怕1000~1099是空的。
UID_MAX 60000 指定用户最大的UID为60000
GID_MIN 1000 指定GID最小值为500
GID_MAX 60000 指定GID最大值为60000
CREATE_HOME yes 指定在创建用户时,是否同时创建用户的主目录,yes表示创建,no表示不创建
USERGROUPS_ENAB yes 指定删除用户的时候是否同时删除由该用户初始化的用户组
ENCRYPT_METHOD SHA512 指定用户密码的加密规则,默认采用SHA512

当我们执行了useradd命令后系统到底执行了什么?
useradd usertest
1.在/etc/passwd 文件中创建一行数据(这也是为什么非root用户无法使用useradd的原因其对passwd文件没有写权限):
usertest:x:1001:1001::/home/usertest:/bin/bash
2.在/etc/shadow文件中创建一行有关usertest的数据:
usertest:!!:18202:0:99999:7:::
3.在/etc/group文件中创建一行关于usertest的用户组数据:
usertest:x:1001:
4.在/etc/gshadow文件增加一行关于group密码的数据:
usertest:!::
5.创建用户的主目录和邮箱(如果要求):
/home/usertest/
/var/spool/mail/usertest
6.将/etc/skel目录中的配置文件复制到新用户的主目录中
以上就时useradd命令的全过程,如果你不想使用useradd命令,完全可以根据这个过程手动创建用户。

passwd修改用户密码

通过useradd命令创建了linux用户后。并没有设置用户的密码,表现就在/etc/shadow中密码列为!!,此时无法用该用户登陆系统,需要使用passwd来设置密码后才可以登陆。

passwd [选项] 用户名
从下面的选项可以看出,每个选项对应的都是对/etc/shadow中各个字段的修改,其实直接使用vim修改shadow文件可能会跟简单写但也相对危险一些
普通用户只能使用passwd修改自己的密码,而不能修改其他用户的密码
还要注意如果使用普通用户修改自己的密码的时候时需要被验证的即123这样的密码时无法被认可的。但是如果使用root用户修改密码,虽然依然无法通过验证但是可以强制修改为123这样的弱密码。

选项 意义
-S 查询用户的密码状态,其实就是查询shadow文件中此用户的密码内容
-l 暂时锁定用户,该选项会在shadow文件中指定用户的加密密码前添加!,使密码失效
-u 解锁用户,与-l选项相对应
--stdin 可以通过管道符输出的数据作为用户的密码,主要在批量添加用户时使用
-n 设置该用户修改密码后,多长时间不能再次修改密码
-x 设置该用户的密码有效期
-w 设置用户密码过期前的警告天数
-i 设置用户密码失效日期

如果忘记密码了怎么办?
对于非root用户,如果忘记密码了,直接可以让root用户通过passwd命令来更改该用户密码。
而对于root用户丢失了密码就需要一些特殊的方法了。

usermod修改用户信息

通过useradd命令添加用户后,如果不小心添加错用户信息了,或者对默认的用户信息不满意时,后期可以通过usermod命令来修改这些信息。

usermod [选项] 用户名
根据下面的选项可以看到基本上与useradd的参数设定差不多,但是一个是创建时设置,一个是创建后修改,其本质都是对/etc/passwd /etc/shadow文件的修改

选项 说明
-c 修改用户说明信息
-d 修改用户主目录
-e 修改用户的失效日期 YYYY-MM-DD
-g 修改用户的初始组
-u 修改用户的UID
-G 修改用户的附加组
-l 修改用户的用户名
-L 临时锁定用户
-U 解锁用户与-L相对
-s 修改用户的登陆shell

chage修改用户密码状态

chage [选项] 用户名

选项 含义
-l 列出用户的详细密码状态
-d 修改 /etc/shadow 文件中指定用户密码信息的第 3 个字段,也就是最后一次修改密码的日期,格式为 YYYY-MM-DD
-m 修改密码最短保留的天数,也就是 /etc/shadow 文件中的第 4 个字段
-M 修改密码的有效期,也就是 /etc/shadow 文件中的第 5 个字段
-W 修改密码到期前的警告天数,也就是 /etc/shadow 文件中的第 6 个字段
-i 修改密码过期后的宽限天数,也就是 /etc/shadow 文件中的第 7 个字段
-E 修改账号失效日期,格式为 YYYY-MM-DD,也就是 /etc/shadow 文件中的第 8 个字段

在实际应用中,如果你需要修改用户的密码信息,直接修改/etc/shadow文件可能更加方便。但是chage也是有其使用环境的。尤其是通过修改最后一次修改密码的日期即-d选项来强制让用户在首次登陆帐号时先修改密码。例如:chage -d 0 usertest #通过chage命令设置此帐号的密码创建的日期为1970年1月1日,这样用户在登陆后就必须先修改密码才能干别的。
实际使用:如果一些学校要给学生发放一批用户,以学号作为用户名以及密码交给学生,并且设置change -d 0 学号 来让每个学生在第一次登陆时更改密码。

userdel删除用户

通过上面的学习我们知道,用户的相关数据包含在如下文件中:

  • 用户基本信息:/etc/passwd
  • 用户密码信息:/etc/shadow
  • 用户组基本信息:/etc/group
  • 用户组密码信息:/etc/gshadow
  • 用户家目录:/home/[username]
  • 用户邮箱:/var/spool/mail/[username]

而对于userdel命令的作用就是从以上文件中删除指定用户的数据信息。

userdel -r 用户名
-r选项表示在删除用户的同时删除用户的家目录以及邮箱
你也可以手动删除指定用户,即通过对上面文件的修改来删除用户,但是这样作没什么意义错非你为了加深对userdel命令的理解。
如果要删除的用户已经使用系统一段时间了,那么用户可能在系统的其他位置留有其他文件,仅仅使用userdel命令时无法删除该用户的使用痕迹的,这是你可以在使用userdel命令前,通过find -user 用户名 命令来查出那些文件属于该用户一并删除后在使用userdel命令删除用户。(find 默认搜索当前目录下,所以要搜索整个电脑可以切换到根目录搜索)

管理组

管理组与管理用户差不多,只是其中的user改为group即可。其本质也是对/etc/group /etc/gshadow文件的修改。

  • groupadd [选项] 组名 添加用户组
    • -g 指定组ID
  • groupmod [选项] 组名 修改用户组
    • -g 修改组ID
    • -n 修改组名
  • groupdel 组名 删除组
    • 其本质就是删除/etc/group /etc/gshadow中组相应的行。此命令仅仅适用于删除那些不是任何用户初始组的群组,否则无法成功。除非你使用usermod -g更改用户的初始组,或者删除用户。

gpasswd将用户加入群组或从组中删除

使用gpasswd命令可以为群组添加密码,设置群组管理员以及将用户加入群组或从组内删除。

gpasswd [选项] 组名

选项 功能
选项为空时,表示给群组设置密码,仅 root 用户可用。
-A user1,... 将群组的控制权交给 user1,... 等用户管理,也就是说,设置 user1,... 等用户为群组的管理员,仅 root 用户可用。
-M user1,... 将 user1,... 加入到此群组中,仅 root 用户可用。
-r 移除群组的密码,仅 root 用户可用。
-R 让群组的密码失效,仅 root 用户可用。
-a user 将 user 用户加入到群组中。
-d user 将 user 用户从群组中移除。

注意:我们可以使用usermod -G命令将用户加入一个群组,但是同时会产生一个副作用,即使用该命令后会将用户以前加入的群组都清空(初始组除外)
而使用gpasswd命令则不会出现问题,所以将用户加入某个群组最好使用gpasswd命令

newgrp切换用户的有效组

用户在创建文件的时候,该文件默认会有组权限,而这个文件所属的组由用户的有效组决定。而用户的有效组默认情况下就是用户的初始组。而newgrp命令可以从用户的附加组中选择一个群组作为用户新的有效组。这种切换是临时的,在关闭终端后重新开启终端其有效组还是初始组。

su root #切换到root用户
groupadd grouptest #新建组
gpasswd -a usertest grouptest #将usertest添加到grouptest组中
exit #退出root
touch test1 #新建一个test1文件
ls -al
-rw-rw-r--. 1 usertest usertest   0 11月  2 22:09 test1
newgrp grouptest  #切换有效组
touch test2 
ls -al
-rw-rw-r--. 1 usertest usertest   0 11月  2 22:09 test1
-rw-rw-r--. 1 usertest grouptest   0 11月  2 22:10 test2
exit #关闭终端
touch test3
-rw-rw-r--. 1 usertest usertest   0 11月  2 22:09 test1
-rw-rw-r--. 1 usertest grouptest   0 11月  2 22:10 test2
-rw-rw-r--. 1 usertest usertest   0 11月  2 22:13 test3
# 可见切换是临时的

总结:

  • 默认情况下有效组就是初始组
  • 通过newgrp可以临时切换有效组,前提是只能选择附加组中的组
  • 要想永久切换有效组,可是使用 usermod -g 修改用户的初始组

其他有关命令

id查询用户的UID和GID

id 用户名
uid=1001(usertest) gid=1001(usertest) groups=1001(usertest),1002(grouptest)
uid表示用户id,gid是初始组id,groups是附加组列表

su临时切换用户命令

su是最简单的用户切换命令,通过该命令可以实现任何身份的切换。
注意:普通用户到任何用户切换都需要知晓对方的密码,而root用户到任何用户的切换不需要对方密码直接切换成功。

su [选项] 用户名

选项 含义
- 当前用户不仅切换为指定用户的身份,同时所用的工作环境也切换为此用户的环境(包括 PATH 变量、MAIL 变量等),使用 - 选项可省略用户名,默认会切换为 root 用户
-l 同 - 的使用类似,也就是在切换用户身份的同时,完整切换工作环境,但后面需要添加欲切换的使用者账号
-p 表示切换为指定用户的身份,但不改变当前的工作环境(不使用切换用户的配置文件)
-m 和 -p 一样
-c 仅切换用户执行一次命令,执行后自动切换回来,该选项后通常会带有要执行的命令

除了使用-c选项外,其他切换用户的方式不会自动切换回来,需要exit命令来退出。

注意:
su和su - 区别是非常大的
如果不切换用户环境例如使用su切换,可能导致很多命令无法执行。例如虽然普通用户可以使用su切换到root,但是使用的配置文件依然是普通用户的,其环境变量中不包含/sbin /usr/sbin等root用户命令的保存路径,这就导致你无法使用这里的命令除非全路径调用。
简单的理解就是su切换的不彻底,而su - 切换的彻底就好了。

whoami和who am i命令用于打印用户名时的区别

whoami:用于打印当前执行操作的用户名
who am i:用于打印登陆当前linux系统的用户名

加入我们使用usertest登陆了linux系统,在终端中使用su切换到root命令。此时执行whoami显示的就是root。而使用who am i显示的则是:usertest pts/0 2019-11-02 22:17 (:0)

对于:whoami等价于 id -un 命令 而who am i 等价于 who -m 命令。

sudo命令详解

linux的用户除了root就是普通用户,而普通用户的权限非常的低,甚至连安装软件的权力都没有。很多时候系统管理员为了能让普通用户具备一些root用户的特权,就需要赋予他们相应的权限。使用su命令是一个方法,但是该命令需要告诉使用人管理员密码,并且root账户太过强大。因此赋予权限的就交给了一个叫做sudo的命令了。
但是并非任何普通用户都拥有sudo特权。对于一些面向桌面的发行版,比如manjaro Ubuntu Fedora等,他们的设计理念就是日常应用,比较偏向赋予普通用户相对较为宽泛的权力,所以在系统的配置中,会给一类基于普通用户的管理员角色,这类角色被赋予了sudo特权。而对于CentOS这样用于服务器的,强调的是安全可靠,所以在系统的配置中,除非root用户指定,否则不会让普通用户具有sudo特权。
而给某个用户赋予sudo特权,实际上就是更改/etc/sudoers文件中的内容。

sudo [-b] [-u 新使用者帐号] 要执行的命令
-b: 将后续的命令放到背景中让系统自动运行,不对但前的shell环境产生影响
-u: 后面可以接需要切换的用户名,若无此项则代表切换身份为root
-l:用于显示当前用户可以用sudo执行那些命令

/etc/sudoers文件

首先让我们看看该文件中包含那些内容:

## sudoers file.
##
## This file MUST be edited with the 'visudo' command as root.
## Failure to use 'visudo' may result in syntax or file permission errors
## that prevent sudo from running.
##
## See the sudoers man page for the details on how to write a sudoers file.
##

##
## Host alias specification
##
## Groups of machines. These may include host names (optionally with wildcards),
## IP addresses, network numbers or netgroups.
# Host_Alias    WEBSERVERS = www1, www2, www3

##
## User alias specification
##
## Groups of users.  These may consist of user names, uids, Unix groups,
## or netgroups.
# User_Alias    ADMINS = millert, dowdy, mikef

##
## Cmnd alias specification
##
## Groups of commands.  Often used to group related commands together.
# Cmnd_Alias    PROCESSES = /usr/bin/nice, /bin/kill, /usr/bin/renice, \
#                           /usr/bin/pkill, /usr/bin/top
# Cmnd_Alias    REBOOT = /sbin/halt, /sbin/reboot, /sbin/poweroff

##
## Defaults specification
##
## You may wish to keep some of the following environment variables
## when running commands via sudo.
##
## Locale settings
# Defaults env_keep += "LANG LANGUAGE LINGUAS LC_* _XKB_CHARSET"
##
## Run X applications through sudo; HOME is used to find the
## .Xauthority file.  Note that other programs use HOME to find   
## configuration files and this may lead to privilege escalation!
# Defaults env_keep += "HOME"
##
## X11 resource path settings
# Defaults env_keep += "XAPPLRESDIR XFILESEARCHPATH XUSERFILESEARCHPATH"
##
## Desktop path settings
# Defaults env_keep += "QTDIR KDEDIR"
##
## Allow sudo-run commands to inherit the callers' ConsoleKit session
# Defaults env_keep += "XDG_SESSION_COOKIE"
##
## Uncomment to enable special input methods.  Care should be taken as
## this may allow users to subvert the command being run via sudo.
# Defaults env_keep += "XMODIFIERS GTK_IM_MODULE QT_IM_MODULE QT_IM_SWITCHER"
##
## Uncomment to use a hard-coded PATH instead of the user's to find commands
# Defaults secure_path="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
##
## Uncomment to send mail if the user does not enter the correct password.
# Defaults mail_badpass
##
## Uncomment to enable logging of a command's output, except for
## sudoreplay and reboot.  Use sudoreplay to play back logged sessions.
# Defaults log_output
# Defaults!/usr/bin/sudoreplay !log_output
# Defaults!/usr/local/bin/sudoreplay !log_output
# Defaults!REBOOT !log_output

##
## Runas alias specification
##

##
## User privilege specification
##
root ALL=(ALL) ALL

## Uncomment to allow members of group wheel to execute any command
# %wheel ALL=(ALL) ALL

## Same thing without a password
# %wheel ALL=(ALL) NOPASSWD: ALL

## Uncomment to allow members of group sudo to execute any command
# %sudo ALL=(ALL) ALL

## Uncomment to allow any user to run sudo if they know the password
## of the user they are running the command as (root by default).
# Defaults targetpw  # Ask for the password of the target user
# ALL ALL=(ALL) ALL  # WARNING: only use this together with 'Defaults targetpw'

## Read drop-in files from /etc/sudoers.d
## (the '#' here does not indicate a comment)
#includedir /etc/sudoers.d

要修改/etc/sudoers中的内容,不建议直接使用vim,包括在/etc/sudoers中也有明确要求使用visudo命令来修改sudoers文件,其实visudo的实际操作与vim一样,使用visudo的好处在于当修改完毕后在离开修改后的页面时系统会自动检验/etc/sudoers文件的语法正确性。

其中有两条没有被注释,我们来分析:

root ALL=(ALL) ALL 和 %wheel ALL=(ALL) ALL
用户名/群组名 被管理主机的地址(可使用的身份) 授权的命令
用户名/群组名:表示系统中的那个用户或群组可以使用sudo,群组前要加百分号以与用户名区别
被管理主机的地址:用户可以指定IP地址的服务器。ALL表示可以管理任何主机。如果写固定IP表示用户可以管理指定的服务器。如果写本机的IP代表指定用户可以从任何IP地址来管理当前服务器。
可使用的身份:就是来源用户可以切换成什么身份也就是sudo -u后面可以接谁,ALL指代任意
授权命令:表示将什么命令授权给用户ALL表示所有命令,要写命令的全路径

我们使用几个具体的例子来解释如何编写sudoers文件:

# 赋予usertest sudo的所有权限
usertest ALL=(ALL) ALL

# 赋予usertest 组 sudo的所有权限
%usertest ALL=(ALL) ALL

# 赋予usertest1 sudo的所有权限并且在执行sudo时不需要密码
usertest1 ALL=(ALL)  NOPASSWD:ALL

# 赋予usertest1 组 sudo的部分权限
# 即usertest1群组成员可以执行
#sudo mount /mnt/cdrom
#sudo unmount /mnt/cdrom
#其余命令不可以执行
%usertest1 ALL=/sbin/mount /mnt/cdrom, /sbin/umount /mnt/cdrom 

# 赋予usertest2组sudo除了adduser useradd权限外的所有权限
%usertest2 ALL=(ALL) ALL,!/user/sbin/adduser, !/user/sbin/useradd

使用/etc/sudoers.d文件夹

sudo可用解析/etc/sudoers.d/目录中的文件,这样就不需要编辑单一的/etc/sudoers文件了,可用单独修改一个设置然后放入这个目录中,目录中的配置格式与/etc/sudoers是一样的,这样的优点就是如果配置出现问题可以直接删除而不用编辑sudoers这个文件。
/etc/sudoers.d/目录中的文件是按字母顺序排序加载的,点或者~开头的文件被跳过,让我们看看manjaro是如何处理的,在/etc/sudoers.d/下有一个10-installer文件,其中写入赋予wheel组sudo权限,而你在安装时使用的用户名就被加入的该组所以具备sudo权限。

[hncjygd-pc sudoers.d]# ls
10-installer
[hncjygd-pc sudoers.d]# cat 10-installer 
%wheel ALL=(ALL) ALL

禁止root用户登录

类似Ubuntu这样的发行版其禁止root用户登录,而是赋予普通用户sudo的ALL权限来实现系统管理,要想在manjaro中禁止root用户有以下方法:

  1. 使用passwd命令锁住root用户:

passwd -l root

要想恢复则使用-u解锁

sudo passwd -u root

  1. 直接编辑/etc/shadow文件,将root的加密口令替换为!

root:!:12345::::::

此时要再次启用root重新为其设置密码就可以了

sudo passwd root

总结:实际上Ubuntu就是用的这种方法禁用的root,要想恢复:

sudo passwd -u root
sudo passwd root

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 211,948评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,371评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,490评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,521评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,627评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,842评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,997评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,741评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,203评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,534评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,673评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,339评论 4 330
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,955评论 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,770评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,000评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,394评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,562评论 2 349

推荐阅读更多精彩内容