用Google Authenticator加强系统的安全性

安全攻防 gongyj 1606℃ 已收录 0评论

传统地用 SSH 登录 Linux 的时候,靠的只是一串字符密码,如果密码被泄露、被猜解、被暴力枚举成功,那么 lLinux 主机就完全暴露在坏人手中了。安全意识高一些的用户会使用公私钥代替字符来登录,但是这样的缺点是如果在陌生的电脑上想要临时登录一下,由于没有私钥,也就没办法了。传统地登录 WordPress 的时候,更是只有一个密码,如果被泄露、猜解、暴力枚举,辛辛苦苦经营的博客就完蛋了。本文介绍如何通过 Google Authenticator 构建“物理屏障”,最大限度地阻断来自网络的密码攻击。

google-authentication.jpg

google authenticator 是基于 TOTP(Time-based One-time Password,基于时间的一次性密码),TOTP(基于时间的一次性密码算法)是支持时间作为动态因素基于HMAC一次性密码算法的扩展。服务器端使用保存在数据库中的密钥和时间戳通过同一种算法生成一个6位数字的一次性密码。大家都懂控制变量法,如果算法相同、密钥相同,又是同一个时间(时间戳相同),那么客户端和服务器计算出的一次性密码是一样的。服务器验证时如果一样,就登录成功了,不用联网也可以,属于离线式验证。

http://tools.ietf.org/html/rfc6238

条件:

(1)Prover与Verifier之间必须时钟同步;

(2)Prover与Verifier之间必须共享密钥;

(3)Prover与Verifier之间必须使用相同的时间步长

算法:

K 共享密钥

T 时间

T0 开始计数的时间步长

X 时间步长

TOTP = Truncate(HMAC-SHA-1(K, (T – T0) / X))

使用 Google Authenticator 的步骤:

1.服务器端对每个用户生成一个唯一的密钥SecretKey。

2.用户在手机上安装 Google Authenticator。首次使用需要将密钥输入到Google Authenticator。可通过手动和扫描系统生成的二维码等两种方式输入密钥信息,并会被进行base32加密。

3.Google Authenticator 每隔一段时间(默认为30秒)为系统自动生成一个新的密码。用户在输入口令前需先输入生成的动态验证码。

一、用 Google Authenticator 加强 SSH 登录安全性

基础组件安装:

1、安装qrencode

在Linux上,有一个名为 QrenCode 的命令行工具可以很容易帮我们生成二维码,google authenticator 命令行生成二维码就是调用它。

wget -P /usr/local/src http://fukuchi.org/works/qrencode/qrencode-3.4.4.tar.gz
cd /usr/local/src
yum install -y gcc make libpng-devel
tar xf qrencode-3.4.4.tar.gz
cd qrencode-3.4.4
./configure --prefix=/usr/local/qrencode
make && make install
ln -s /usr/local/qrencode/lib/libqrencode.so.3 /usr/lib64/
cd ..

2、安装google authenticator PAM插件

wget -P /usr/local/src https://github.com/google/google-authenticator/archive/master.zip
cd /usr/local/src
yum install -y gcc autoconf automake libtool pam-devel
unzip master.zip
cd google-authenticator-master/libpam/
./bootstrap.sh
./configure
make && make install
ln -s /usr/local/lib/security/pam_google_authenticator.so /lib64/security/
#看日志/var/log/secure,库文件要到这个位置加载,所以要做个软连接

以前的版本在安装的时候,直接用 make 命令编译就行了,现在需要重新生成一下 configure 文件,然后在编译。

3、配置google authenticator

Google Authenticator 其实是一套开源的解决方案,所以不仅在 Google 的网站上能用,在其他地方也能用的。然而,在 Google 的网站上,会直接给你一个 QR 码让你扫的,而自己配置的 Google Authenticator 则要自己生成了。

首先需要切换到对应的用户,如果服务器上只有一个用户的话,自然是可以省略这一步的,但是多用户的服务器需要先切换到对应的用户,再运行 google-authenticator 命令,程序会问你Do you want authentication tokens to be time-based (y/n),大意是基于时间生成验证码(及TOTP),这里选择y。如果没有安装 qrencode 也可以使用,但屏幕不会输出二维码,只能通过 Google 的 uri 地址去看二维码。

结果类似这样:

[root@localhost ~]# google-authenticator

Do you want authentication tokens to be time-based (y/n) y
https://www.google.com/chart?chs=200x200&chld=M|0&cht=qr&chl=otpauth://totp/root@localhost%3Fsecret%3DCALFYQOHCJKO5P4V%26issuer%3Dlocalhost
Your new secret key is: CALFYQOHCJKO5P4V
Your verification code is 677598
Your emergency scratch codes are:
  23440409
  76361981
  51369574
  24159138
  13764502

如果安装过 qrencode工具后,终端中不但会有上述信息,还有有一个二维码信息输出:

QrenCode.png

这个 QR 码自然是给 Google Authenticator 应用程序来扫描的,也可以访问上面的那个链接,用 Google Chart API 生成的 QR 码来扫描。还可以照着 QR 码下面的文字密钥手工输入。当 Google Authenticator 识别了这个账号之后,验证器就配置好了。在文字密钥下面还提供了几个应急码,为手机丢了等情况下所用的,每个码只可以使用此次,用过后会被系统删除,一定要妥善保管。

注意:这个二维码不是图片,是在终端命令行输出的字符,有些远程工具显示这个二维码会有问题,secureCRT 与 Xshell 都可以正常显示。

这时 Google Authenticator 虽然运行了,但是相关设置还没有保存,程序会问你 :

Do you want me to update your “/root/.google_authenticator” file (y/n)

(是否将配置写入家目录的配置文件),当然是回答 y 了。又会问:

Do you want to disallow multiple uses of the same authentication
token? This restricts you to one login about every 30s, but it increases
your chances to notice or even prevent man-in-the-middle attacks (y/n) y

大意是说是否禁止一个口令多用,自然也是答 y。下一个问题是:

By default, tokens are good for 30 seconds and in order to compensate for
possible time-skew between the client and the server, we allow an extra
token before and after the current time. If you experience problems with poor
time synchronization, you can increase the window from its default
size of 1:30min to about 4min. Do you want to do so (y/n) y

是否打开时间容错以防止客户端与服务器时间相差太大导致认证失败。这个可以根据实际情况来。我的ipad时间很准(与网络同步的),所以答 n,如果一些平板电脑不怎么连网的,可以答 y 以防止时间误差错误导致认证失败。再一个问题是:

If the computer that you are logging into isn't hardened against brute-force
login attempts, you can enable rate-limiting for the authentication module.
By default, this limits attackers to no more than 3 login attempts every 30s.
Do you want to enable rate-limiting (y/n) y

选择是否打开尝试次数限制(防止暴力攻击),自然答 y。

问题答完了,家目录中多出一个 .google_authenticator 文件(默认权限为 400),这时客户端与服务端已经配套起来了,以后不用再运行 google-authenticator 命令了,否则会重新生成一组密码。

4、配置SSH验证

此时虽然 Google Authenticator 已经配置好了,但是并没有任何程序会去调用它。所以需要设置 SSH 登录的时候去通过它验证。

编辑 /etc/pam.d/sshd 文件,在首行添加

vim /etc/pam.d/sshd
auth           required     pam_google_authenticator.so

在修改SSH配置文件

vim /etc/ssh/sshd_config

ChallengeResponseAuthentication yes
UsePAM yes

最后重启 SSH 服务以支持 OTP 和密码认证:

/etc/init.d/sshd restart

然后就可以在手机上安装google-authenticator客户端,通过输入私钥即可实时显示密码

以后再远程登录的时候就会弹出验证码,而要输入客户端上实时产生的动态验证码才能进行登录。

这时候再用 Linux 主机之间 SSH 登录的话就会这样了:

Verification code: 
Password:

先输入动态密码,然后在输入用户密码,就可以登录系统了。

如果是使用远程工具,就要先选择“Keyboard Interactive”,使用键盘输入用户身份验证,然后在输入密码,才可以登录系统。

Xshell.png

5、错误解决

5.1 selinux需设定为disabled,不然SSH就无法登陆了。(不关闭的方法还没找到,继续探索中…)

5.2 google authenticator命令行下不生成二维码问题解决方法,开启日志跟踪

strace -o log google-authenticator

log信息如下:

execve("/usr/local/bin/google-authenticator", ["google-authenticator"], [/* 27 vars */]) = 0
brk(0)                                  = 0x1a75000
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fcc5131b000
access("/etc/ld.so.preload", R_OK)      = -1 ENOENT (No such file or directory)
open("/etc/ld.so.cache", O_RDONLY)      = 3
fstat(3, {st_mode=S_IFREG|0644, st_size=58970, ...}) = 0
mmap(NULL, 58970, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7fcc5130c000
close(3)                                = 0
open("/lib64/libdl.so.2", O_RDONLY)     = 3
read(3, "\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\340\r@C3\0\0\0"..., 832) = 832
fstat(3, {st_mode=S_IFREG|0755, st_size=22536, ...}) = 0
mmap(0x3343400000, 2109696, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x3343400000
mprotect(0x3343402000, 2097152, PROT_NONE) = 0
mmap(0x3343602000, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x2000) = 0x3343602000
close(3)                                = 0
open("/lib64/libc.so.6", O_RDONLY)      = 3
read(3, "\177ELF\2\1\1\3\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\360\355\201C3\0\0\0"..., 832) = 832
fstat(3, {st_mode=S_IFREG|0755, st_size=1922152, ...}) = 0
mmap(0x3343800000, 3745960, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x3343800000
mprotect(0x334398a000, 2093056, PROT_NONE) = 0
mmap(0x3343b89000, 20480, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x189000) = 0x3343b89000
mmap(0x3343b8e000, 18600, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x3343b8e000
close(3)                                = 0
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fcc5130b000
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fcc5130a000
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fcc51309000
arch_prctl(ARCH_SET_FS, 0x7fcc5130a700) = 0
mprotect(0x3343b89000, 16384, PROT_READ) = 0
mprotect(0x3343602000, 4096, PROT_READ) = 0
mprotect(0x334321f000, 4096, PROT_READ) = 0
munmap(0x7fcc5130c000, 58970)           = 0
getuid()                                = 0
brk(0)                                  = 0x1a75000
brk(0x1a96000)                          = 0x1a96000
socket(PF_FILE, SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0) = 3
connect(3, {sa_family=AF_FILE, path="/var/run/nscd/socket"}, 110) = -1 ENOENT (No such file or directory)
close(3)                                = 0
socket(PF_FILE, SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0) = 3
connect(3, {sa_family=AF_FILE, path="/var/run/nscd/socket"}, 110) = -1 ENOENT (No such file or directory)
close(3)                                = 0
open("/etc/nsswitch.conf", O_RDONLY)    = 3
fstat(3, {st_mode=S_IFREG|0644, st_size=1688, ...}) = 0
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fcc5131a000
read(3, "#\n# /etc/nsswitch.conf\n#\n# An ex"..., 4096) = 1688
read(3, "", 4096)                       = 0
close(3)                                = 0
munmap(0x7fcc5131a000, 4096)            = 0
open("/etc/ld.so.cache", O_RDONLY)      = 3
fstat(3, {st_mode=S_IFREG|0644, st_size=58970, ...}) = 0
mmap(NULL, 58970, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7fcc5130c000
close(3)                                = 0
open("/lib64/libnss_files.so.2", O_RDONLY) = 3
read(3, "\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\360!\0\0\0\0\0\0"..., 832) = 832
fstat(3, {st_mode=S_IFREG|0755, st_size=65928, ...}) = 0
mmap(NULL, 2151824, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7fcc510fb000
mprotect(0x7fcc51107000, 2097152, PROT_NONE) = 0
mmap(0x7fcc51307000, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0xc000) = 0x7fcc51307000
close(3)                                = 0
mprotect(0x7fcc51307000, 4096, PROT_READ) = 0
munmap(0x7fcc5130c000, 58970)           = 0
open("/etc/passwd", O_RDONLY|O_CLOEXEC) = 3
fcntl(3, F_GETFD)                       = 0x1 (flags FD_CLOEXEC)
fstat(3, {st_mode=S_IFREG|0644, st_size=1578, ...}) = 0
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fcc5131a000
read(3, "root:x:0:0:root:/root:/bin/bash\n"..., 4096) = 1578
close(3)                                = 0
munmap(0x7fcc5131a000, 4096)            = 0
uname({sys="Linux", node="fh21.desktop", ...}) = 0
uname({sys="Linux", node="fh21.desktop", ...}) = 0
open("/dev/urandom", O_RDONLY)          = 3
read(3, "v\340\231\277\261\"\23\250U\322\353l\275\270\20076\252\36\23\324\322\27\221\243~B>-)", 30) = 30
fstat(1, {st_mode=S_IFCHR|0620, st_rdev=makedev(136, 0), ...}) = 0
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fcc5131a000
write(1, "\n", 1)                       = 1
write(1, "Do you want authentication token"..., 57) = 57
fstat(0, {st_mode=S_IFCHR|0620, st_rdev=makedev(136, 0), ...}) = 0
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fcc51319000
read(0, "y\n", 1024)                    = 2
write(1, "https://www.google.com/chart?chs"..., 146) = 146
ioctl(1, SNDCTL_TMR_TIMEBASE or TCGETS, {B38400 opost isig icanon echo ...}) = 0
open("/etc/ld.so.cache", O_RDONLY)      = 4
fstat(4, {st_mode=S_IFREG|0644, st_size=58970, ...}) = 0
mmap(NULL, 58970, PROT_READ, MAP_PRIVATE, 4, 0) = 0x7fcc510ec000
close(4)                                = 0
open("/lib64/tls/x86_64/libqrencode.so.2", O_RDONLY) = -1 ENOENT (No such file or directory)
stat("/lib64/tls/x86_64", 0x7fffe3e65380) = -1 ENOENT (No such file or directory)
open("/lib64/tls/libqrencode.so.2", O_RDONLY) = -1 ENOENT (No such file or directory)
stat("/lib64/tls", {st_mode=S_IFDIR|0555, st_size=4096, ...}) = 0
open("/lib64/x86_64/libqrencode.so.2", O_RDONLY) = -1 ENOENT (No such file or directory)
stat("/lib64/x86_64", 0x7fffe3e65380)   = -1 ENOENT (No such file or directory)
open("/lib64/libqrencode.so.2", O_RDONLY) = -1 ENOENT (No such file or directory)
stat("/lib64", {st_mode=S_IFDIR|0555, st_size=12288, ...}) = 0
open("/usr/lib64/tls/x86_64/libqrencode.so.2", O_RDONLY) = -1 ENOENT (No such file or directory)
stat("/usr/lib64/tls/x86_64", 0x7fffe3e65380) = -1 ENOENT (No such file or directory)
open("/usr/lib64/tls/libqrencode.so.2", O_RDONLY) = -1 ENOENT (No such file or directory)
stat("/usr/lib64/tls", {st_mode=S_IFDIR|0555, st_size=4096, ...}) = 0
open("/usr/lib64/x86_64/libqrencode.so.2", O_RDONLY) = -1 ENOENT (No such file or directory)
stat("/usr/lib64/x86_64", 0x7fffe3e65380) = -1 ENOENT (No such file or directory)
open("/usr/lib64/libqrencode.so.2", O_RDONLY) = -1 ENOENT (No such file or directory)
stat("/usr/lib64", {st_mode=S_IFDIR|0555, st_size=61440, ...}) = 0
munmap(0x7fcc510ec000, 58970)           = 0
open("/etc/ld.so.cache", O_RDONLY)      = 4
fstat(4, {st_mode=S_IFREG|0644, st_size=58970, ...}) = 0
mmap(NULL, 58970, PROT_READ, MAP_PRIVATE, 4, 0) = 0x7fcc510ec000
close(4)                                = 0
open("/lib64/tls/libqrencode.so.3", O_RDONLY) = -1 ENOENT (No such file or directory)
open("/lib64/libqrencode.so.3", O_RDONLY) = -1 ENOENT (No such file or directory)
open("/usr/lib64/tls/libqrencode.so.3", O_RDONLY) = -1 ENOENT (No such file or directory)
open("/usr/lib64/libqrencode.so.3", O_RDONLY) = -1 ENOENT (No such file or directory)
munmap(0x7fcc510ec000, 58970)           = 0
write(1, "Your new secret key is: O3QJTP5R"..., 41) = 41
write(1, "Your verification code is 970552"..., 33) = 33
write(1, "Your emergency scratch codes are"..., 34) = 34
read(3, "b\26M&", 4)                    = 4
write(1, "  45628710\n", 11)            = 11
read(3, "\235\256p\355", 4)             = 4
write(1, "  97971437\n", 11)            = 11
read(3, "\25L\6T", 4)                   = 4
write(1, "  57303892\n", 11)            = 11
write(1, "  95420542\n", 11)            = 11
write(1, "  11371049\n", 11)            = 11
close(3)                                = 0
write(1, "\n", 1)                       = 1
write(1, "Do you want me to update your \"/"..., 71) = 71
read(0, "y\n", 1024)                    = 2
write(1, "\n", 1)                       = 1
write(1, "Do you want to disallow multiple"..., 138) = 138
write(1, "your chances to notice or even p"..., 71) = 71
read(0, "y\n", 1024)                    = 2
write(1, "\n", 1)                       = 1
write(1, "By default, tokens are good for "..., 291) = 291
write(1, "size of 1:30min to about 4min. D"..., 58) = 58
read(0, "y\n", 1024)                    = 2
write(1, "\n", 1)                       = 1
write(1, "If the computer that you are log"..., 231) = 231
write(1, "Do you want to enable rate-limit"..., 42) = 42
read(0, "y\n", 1024)                    = 2
open("/root/.google_authenticator~", O_WRONLY|O_CREAT|O_EXCL|O_TRUNC|O_NOFOLLOW, 0400) = 3
write(3, "O3QJTP5REIJ2QVOS\n\" RATE_LIMIT 3 "..., 126) = 126
rename("/root/.google_authenticator~", "/root/.google_authenticator") = 0
close(3)                                = 0
exit_group(0)                                           = ?

问题出在google-authenticator调用libqrencode.so.3时,在相应路径查询不到,解决方法很简单。

①编译qrencode时指定路径即可及。./configure –prefix=/usr。(默认编译安装至/usr/local)

这样libqrencode.so.3就会安装到/usr/lib目录下。(32位系统时有效)

②还有种方法就是默认编译安装,然后做个软连接,64位系统要放到/usr/lib64下。

二、用 Google Authenticator 增强 WordPress 安全性

 1、在 WordPress 后台搜索并安装 Google Authenticator 这个插件,然后启用。


wp-google-authentication1.png

wp-Google-Authenticator2.png

2、在 WordPress 后台个人用户页面“Google Authenticator 设置”选项下,选中“启用”,“宽松模式”建议选上,防止手机时钟与服务器时钟允许有一定范围的误差,不会因为误差原因而被拒绝登录,然后填好“描述”(只是在手机上显示的名字而已),再点击“显示/隐藏QR码”可以显示出二维码图片,“启用应用专用密码”没什么用处,可以不选。

wp-Google-Authenticator3.png

最后保存一下,更新个人资料就可以使用,密钥也可以随时更换。如果因为手机遗失等问题,造成不能登录,不用怕,登录服务器后进入插件目录,把Google Authenticator插件目录改个名字或直接删除,验证就取消了。

3、在手机中安装的 Google Authenticator 应用程序中扫描这个“QR 码”以添加账号,或者手工输入“密钥”也行。

配置成功以后,再登录 WordPress 后台的时候就会是这样的:

wp-Google-Authenticator4.png

wp-Google-Authenticator5.png

Google Authenticator验证码要输入正确才能登录,否则系统就会拒绝登录。

三、用 Google Authenticator 增强桌面电脑的安全性

本文第一节讲了如何把 Gooogle Authenticator 用在增强 SSH 登录远程主机时的安全性。自然,Gooogle Authenticator 也是能用在装有 GNU/Linux 的桌面电脑上的。安装模块的方法和在上边是一样的,手机上配置也是一样,但是调用的时候是不同的。

在 /etc/pam.d/ 目录下,可以看到一些文件:

[root@publish ~]# ls /etc/pam.d/
atd             crond                gdm-autologin      newrole           ppp        smartcard-auth     sudo                          system-config-keyboard
authconfig      cups                 gdm-fingerprint    other             reboot     smartcard-auth-ac  sudo-i                        system-config-network
authconfig-gtk  cvs                  gdm-password       passwd            remote     smtp               su-l                          system-config-network-cmd
authconfig-tui  eject                gnome-screensaver  password-auth     run_init   smtp.postfix       system-auth                   system-config-users
chfn            fingerprint-auth     halt               password-auth-ac  runuser    sshd               system-auth-ac                xserver
chsh            fingerprint-auth-ac  ksu                polkit-1          runuser-l  ssh-keycat         system-config-authentication
config-util     gdm                  login              poweroff          setup      su                 system-config-date

这些文件从文件名就能看出它们是干嘛的:控制一些重要操作的认证。我们在 sshd 中添加了 auth required pam_google_authenticator.so 这一行,于是在 SSH 登录远程主机的时候就会调用 Gooogle Authenticator 来认证。在别的文件中加入 auth required pam_google_authenticator.so 这一行的话,就会在相应的操作中调用 Gooogle Authenticator 来认证了。下面是几个重要的:

gdm-password:在 CentOS6 图形界面登录的时候要求先输入验证码,然后在输入密码,效果如下:

xwimdows.png

注意:auth required pam_google_authenticator.so 要放在第二行,否则root无法登录。lightdm 是 Ubuntu 的默认图形管理,屏保解锁的时候也要输入验证码和密码,其他的发行版的配置文件可能是 gdm、kdm 等,请自行修改。

login:在字符界面下(如 tty)登录的时候也会要求输入验证码。

terminal.png

sudo:使普通用户试图提权执行系统操作的时候要求输入验证码,很适合给多人共享电脑用。

相关功能就介绍到这里,做了这些设置,别人想进入的系统那是很有难度了,安全水平又高了一个等级。用Google Authenticator加强系统的安全性

本站文章如未注明,均为原创丨本网站采用BY-NC-SA协议进行授权,转载请注明转自:http://blog.chopmoon.com/favorites/203.html
喜欢 (0)
发表我的评论
取消评论

表情 代码 贴图 加粗 链接 私信 删除线 签到

Hi,请填写昵称和邮箱!

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址
正在加载中……