Freeopen

linux 服务器自用手册

以 centos 系统为主,兼容大部分 linux 系统的管理手册。

软件安装

RPMFusion 镜像

安装基础包

tsinghua mirror:

yum localinstall --nogpgcheck https://mirrors.tuna.tsinghua.edu.cn/rpmfusion/free/el/rpmfusion-free-release-9.noarch.rpm https://mirrors.tuna.tsinghua.edu.cn/rpmfusion/nonfree/el/rpmfusion-nonfree-release-9.noarch.rpm

aliyun miiror:

dnf install https://mirrors.aliyun.com/rpmfusion/free/fedora/rpmfusion-free-release-stable.noarch.rpm https://mirrors.aliyun.com/rpmfusion/nonfree/fedora/rpmfusion-nonfree-release-stable.noarch.rpm --skip-broken

修改链接指向镜像站

修改 /etc/yum.repos.d/ 目录下以 rpmfusion 开头,以 .repo 结尾的文件

具体而言,需要将文件中的 baseurl= 开头的行等号后面链接中的 http://download1.rpmfusion.org/ 替换为 https://mirrors.tuna.tsinghua.edu.cn/rpmfusion/

安装高版本软件包

yum install -y http://rpms.famillecollet.com/enterprise/remi-release-7.rpm
yum --enablerepo=remi list redis --showduplicates | sort -r  # version search
yum --enablerepo=remi install redis-6.2.5 -y                 # select version installed

本地安装

yum localinstall *.rpm --disablerepo=*

新增安装源

  1. Add EPEL that Fedora project provides packages which are specially built for RHEL
    dnf -y install epel-release
    e.g.  dnf —enablerepo=epel install [package]
  1. Add ELRepo that provides mainly drivers for RHEL
    dnf -y install elrepo-release
  1. Add Remi's RPM repository that provides useful packages like latest PHP and so on
    dnf -y install https://rpms.remirepo.net/enterprise/remi-release-8.rpm

修改默认安装源

cd /etc/yum.repos.d/
wget http://mirrors.aliyun.com/repo/Centos-7.repo
wget -O /etc/yum.repos.d/epel-7.repo http://mirrors.aliyun.com/repo/epel-7.repo

yum clean all
yum makecache

yum repolist enabled
yum repolist all

批量文本换源

 sed -e 's|^mirrorlist=|#mirrorlist=|g' \
         -e 's|^#baseurl=http://mirror.centos.org/$contentdir|baseurl=https://mirrors.ustc.edu.cn/centos|g' \
         -i.bak \
         /etc/yum.repos.d/CentOS-Stream-AppStream.repo \
         /etc/yum.repos.d/CentOS-Stream-BaseOS.repo \
         /etc/yum.repos.d/CentOS-Stream-Debuginfo.repo \
         /etc/yum.repos.d/CentOS-Stream-Extras.repo \
         /etc/yum.repos.d/CentOS-Stream-HighAvailability.repo \
         /etc/yum.repos.d/CentOS-Stream-Media.repo \
         /etc/yum.repos.d/CentOS-Stream-PowerTools.repo \
         /etc/yum.repos.d/CentOS-Stream-RealTime.repo

系统服务

服务查看

列出所有已经加载的systemd units

systemctl

列出所有service

systemctl list-units --type=service

列出所有enabled状态的服务

systemctl list-unit-files --state=enabled

列出所有正在运行或failed状态的服务

systemctl list-units --type service --state running,failed

列出所有active状态(运行或退出)的服务

systemctl list-units --type=service --state=active

服务新增加载

systemctl daemon-reload

自定义自动重启

chmod +x /opt/script/autostart.sh
added  /opt/script/autostart.sh to  rc.lcoal
chmod +x /etc/rc.d/rc.local

# systemctl 服务主体存储目录
/usr/lib/systemd/system/
# systemctl 执行时产生的脚本位置
/run/systemd/system/
# systemctl 开机启动链接存储目录
/etc/systemed/system/basic.target.wants/

服务开机自启、重启等

systemctl enable frpc  # 开机自启
systemctl disable frpc # 关闭开机自启
systemctl restart frpc
systemctl stop frpc

静态 ip 地址配置

centos stream 8

/etc/sysconfig/network-scripts

BOOTPROTO=static
IPADDR=192.168.3.165
PREFIX=24
GATEWAY=192.168.3.1
DNS1=192.168.1.1
DNS2=61.128.128.68

命令行设置 静态 ip 的方法

nmcli con mod ens18 ipv4.addresses 192.168.1.91/24;
nmcli con mod ens18 ipv4.gateway 192.168.1.1;
nmcli con mod ens18 ipv4.method manual;
nmcli con mod ens18 ipv4.dns "8.8.8.8";

其中: con = connection, mod = modify

centos stream 9

/etc/NetworkManager/system-connections/

[ipv4]
address1=192.168.64.170/24,192.168.64.2     # static ip/24, gateway ip addr
dns=114.114.114.114
method=manual

加载网络配置

nmcli c reload                         # 重新加载配置文件
nmcli c up ens33                       # 重启ens33网卡

其他网络相关

路由

查看双网卡路由优先级

ip route show  # 值越小优先级越高

路由配置修改

/etc/sysconfig/network-scripts/ifcfg-ens37

IPV4_ROUTE_METRIC=90        # 新增, 修改路由优先级
DEFROUTE=no                 # 关闭内网的默认路由(可能 centos 9 不兼容):

网关

查看网关信息

route -n  或者 netstat -rn

临时删除默认网关

route del default gw 192.168.100.1

抓包

tcpdump -i eth1 -nnn

国内 DNS 及 监控 ip

DNS

114.114.114.114;114.114.114.115
233.5.5.5;233.6.6.6  # 阿里 AliDNS

阿里云盾监控ip

140.205.201.0/28,140.205.201.16/29,140.205.201.32/28,140.205.225.192/29,140.205.225.200/30,140.205.225.184/29,140.205.225.183/32,140.205.225.206/32,140.205.225.205/32,140.205.225.195/32,140.205.225.204/32,106.11.224.0/26,106.11.224.64/26,106.11.224.128/26,106.11.224.192/26,106.11.222.64/26,106.11.222.128/26,106.11.222.192/26,106.11.223.0/26,140.205.201.0/24,140.205.225.0/24

阿里云监测IP

112.124.127.224,112.124.127.44,112.124.127.64,112.124.127.53,120.26.216.168,120.26.64.126,121.43.107.174,121.43.105.176,121.41.117.242,121.40.130.38,121.41.112.148,115.29.112.222,115.28.203.70,42.96.189.63,115.29.113.101,120.27.40.113,115.28.171.22,115.28.189.208,121.42.196.232,115.28.26.13,120.27.47.144,120.27.47.33,112.126.74.55,182.92.148.207,182.92.1.233,112.126.73.56,123.56.138.37,123.57.10.133,112.126.75.174,182.92.157.118,112.126.75.221,182.92.69.212,10.153.174.11,10.153.175.147,10.153.175.146,110.75.0.0/16,42.120.0.0/16

防火墙

start firewall

systemctl start firewalld.service

open service

firewall-cmd --add-service=samba --zone=public --permanent
firewall-cmd --reload

open port

firewall-cmd --permanent --add-port=8080/tcp  # --permanent 表示永久生效,必须 reload

使mysql服务的3306端口只允许192.168.1.1/24网段的服务器能访问

firewall-cmd --permanent --add-rich-rule="rule family="ipv4" source address="192.168.1.1/24" port protocol="tcp" port="3306" accept"

open docker network

firewall-cmd --zone=trusted --change-interface=docker0 --permanent
firewall-cmd --reload

zone查看及设置

firewall-cmd --get-default-zone
firewall-cmd --set-default-zone=public

remove rich rules

firewall-cmd --list-rich-rules
firewall-cmd --permanent --remove-rich-rule '规则列表'

切换网卡到其他zone,即激活该zone

firewall-cmd --permanent --zone=internal --change-interface=xxx

zone 不信任 --> 信任

丢弃 drop

任何流入网络的包都被丢弃,不作出任何响应。只允许流出的网络连接。

阻塞 block

任何进入的网络连接都被拒绝,并返回 IPv4 的 icmp-host-prohibited 报文或者 IPv6 的 icmp6-adm-prohibited 报文。只允许由该系统初始化的网络连接。

公开 public

用以可以公开的部分。你认为网络中其他的计算机不可信并且可能伤害你的计算机。只允许选中的连接接入。

外部 external

用在路由器等启用伪装的外部网络。你认为网络中其他的计算机不可信并且可能伤害你的计算机。只允许选中的连接接入。

隔离区dmz

用以允许隔离区(dmz)中的电脑有限地被外界网络访问。只接受被选中的连接。

工作 work

用在工作网络。你信任网络中的大多数计算机不会影响你的计算机。只接受被选中的连接。

家庭 home

用在家庭网络。你信任网络中的大多数计算机不会影响你的计算机。只接受被选中的连接。

内部 internal

用在内部网络。你信任网络中的大多数计算机不会影响你的计算机。只接受被选中的连接。

受信任的 trusted

允许所有网络连接。

配置文件说明

firewalld 存放配置文件有两个目录,/usr/lib/firewalld 和 /etc/firewalld,前者存放了一些默认的文件,后者主要是存放用户自定义的数据,所以我们添加的service或者rule都在后者下面进行。

server 文件夹存储服务数据,就是一组定义好的规则。

zones 存储区域规则

firewalld.conf 默认配置文件,可以设置默认使用的区域,默认区域为 public,对应 zones目录下的 public.xml

1.重载防火墙配置

firewall-cmd --reload

2.查看防火墙运行状态

firewall-cmd --state

3.查看默认区域的设置

firewall-cmd --list-all

4.应急命令

firewall-cmd --panic-on  # 拒绝所有流量,远程连接会立即断开,只有本地能登陆
firewall-cmd --panic-off  # 取消应急模式,但需要重启firewalld后才可以远程ssh
firewall-cmd --query-panic  # 查看是否为应急模式

5.服务

firewall-cmd --add-service=<service name> #添加服务
firewall-cmd --remove-service=<service name> #移除服务

6.端口

firewall-cmd --add-port=<port>/<protocol> #添加端口/协议(TCP/UDP)
firewall-cmd --remove-port=<port>/<protocol> #移除端口/协议(TCP/UDP)
firewall-cmd --list-ports #查看开放的端口

7.协议

firewall-cmd --add-protocol=<protocol> # 允许协议 (例:icmp,即允许ping)
firewall-cmd --remove-protocol=<protocol> # 取消协议
firewall-cmd --list-protocols # 查看允许的协议

8.允许指定ip的所有流量

firewall-cmd --add-rich-rule="rule family="ipv4" source address="<ip>" accept"

e.g.

firewall-cmd --add-rich-rule="rule family="ipv4" source address="192.168.2.1" accept" # 表示允许来自192.168.2.1的所有流量

9.允许指定ip的指定协议

firewall-cmd --add-rich-rule="rule family="ipv4" source address="<ip>" protocol value="<protocol>" accept"

例:

firewall-cmd --add-rich-rule="rule family="ipv4" source address="192.168.2.208" protocol value="icmp" accept" # 允许192.168.2.208主机的icmp协议,即允许192.168.2.208主机ping

10.允许指定ip访问指定服务

firewall-cmd --add-rich-rule="rule family="ipv4" source address="<ip>" service name="<service name>" accept"

例:

firewall-cmd --add-rich-rule="rule family="ipv4" source address="192.168.2.208" service name="ssh" accept" # 允许192.168.2.208主机访问ssh服务

11.允许指定ip访问指定端口

firewall-cmd --add-rich-rule="rule family="ipv4" source address="<ip>" port protocol="<port protocol>" port="<port>" accept"

例:

firewall-cmd --add-rich-rule="rule family="ipv4" source address="192.168.2.1" port protocol="tcp" port="22" accept" # 允许192.168.2.1主机访问22端口

12.将指定ip改为网段

8-11 的各个命令都支持 source address 设置为网段,即这个网段的ip都是适配这个规则:

例如:

firewall-cmd --zone=drop --add-rich-rule="rule family="ipv4" source address="192.168.2.0/24" port protocol="tcp" port="22" accept"

表示允许192.168.2.0/24网段的主机访问22端口 。

13.禁止指定ip/网段

8-12 各个命令中,将 accept 设置为 reject 表示拒绝,设置为 drop表示直接丢弃(会返回timeout连接超时)

例如:

firewall-cmd --zone=drop --add-rich-rule="rule family="ipv4" source address="192.168.2.0/24" port protocol="tcp" port="22" reject"

表示禁止192.168.2.0/24网段的主机访问22端口 。

ipset

firewalld 默认一条规则处理一个ip地址, 而ipset可以在一条规则中处理多个ip地址

位置: /etc/firewalld/ipsets/

新建与删除 ipset

hash:ip 仅ip, 不允许重复

hash:net ip 或 ip段,不允许重复

firewall-cmd --permanent --new-ipset=sshblock --type=hash:ip
firewall-cmd --permanent --delete-ipset=sshblock

在set中增删ip

firewall-cmd --permanent --ipset=sshblock --add-entry=121.122.123.105
firewall-cmd --permanent --ipset=sshblock --remove-entry=121.122.123.105

firewall-cmd --permanent --ipset=test --add-entries-from-file=iplist.txt # 导入ip文件列表

# iplist.txt
192.168.0.2
192.168.0.3
192.168.1.0/24
192.168.2.254

打印一个set的内容

firewall-cmd --permanent --info-ipset=sshblock
firewall-cmd --permanent --ipset=sshblock --get-entries

把一个ipset加入到禁止的规则

firewall-cmd --permanent --add-rich-rule 'rule family="ipv4" source ipset="sshblock" drop'

前后台任务

nohup command args &

查看后台任务(任务编号, 状态)

jobs

调后台任务到前台

fg job_id

把前台任务切到后台

ctrl + z  # 注意,切换后,任务暂停,要运行暂停后台任务

任务重新开始运行

jobs
bg  job_id

各种查看

查看分区的uuid

blkid

操作系统版本

cat /proc/version
uname -r
uname -a
lsb_release -a
cat /etc/issue

查内存占用

cat /proc/meminfo

CPU总核数 = 物理CPU个数 * 每颗物理CPU的核数

查看cpu信息

cat /proc/cpuinfo | grep name | cut -f2 -d: | uniq -c

查看物理CPU个数

cat /proc/cpuinfo| grep "physical id"| sort| uniq| wc -l

查看每个物理CPU中core的个数(即核数)

cat /proc/cpuinfo| grep "cpu cores"| uniq

总逻辑CPU数 = 物理CPU个数 * 每颗物理CPU的核数 * 超线程数

cat /proc/cpuinfo| grep "processor"| wc -l

查看硬件

lshw -short

查看网卡

lshw -c network

查看分区

cat /proc/partitions

查看软件详细信息

dpkg -s nano

查看某个文件的归宿包

dpkg -S nano

查看属于某软件包的文件

dpkg-query -L nano

查看当前目录大小

du -sh

查看当前和子文件夹大小

du -sh *

查看该目录下所有文件夹所占空间的大小

du -hl

查看文件数量

ls -l | grep '^-' | wc -l

查看文件数量, 含子目录

ls -lR | grep '^-' | wc -l

端口查看

netstat -lnpt
lsof -i

查看可登录的用户名

cat /etc/passwd | grep -v /sbin/nologin | cut -d : -f 1

查看进程

ps auxZ | grep -v grep | grep nginx

e.g. system_u:system_r:httpd_t:s0 root 7997 0.0 0.0 122784 2156 ? Ss 14:31 0:00 nginx: master process /usr/sbin/nginx
e.g. system_u:system_r:httpd_t:s0 nginx 7998 0.0 0.0 125332 7560 ? S 14:31 0:00 nginx: worker process

带标题的查看

ps aux | head -1; ps aux | grep nginx
ps aux | grep -E "USER|nginx"

查看某进程详细内存占用

    more /proc/PID/smaps

    e.g.  output --->

    08048000-080bc000 r-xp 00000000 03:02 13130      /bin/bash

    Size:               1084 kB
    Rss:                 892 kB
    Pss:                 374 kB
    Shared_Clean:        892 kB
    Shared_Dirty:          0 kB
    Private_Clean:         0 kB
    Private_Dirty:         0 kB
    Referenced:          892 kB
    Anonymous:             0 kB
    ShmemPmdMapped:        0 kB
    Shared_Hugetlb:        0 kB
    Private_Hugetlb:       0 kB
    Swap:                  0 kB
    SwapPss:               0 kB
    KernelPageSize:        4 kB
    MMUPageSize:           4 kB
    Locked:                0 kB
    THPeligible:           0
    VmFlags: rd ex mr mw me dw

输出字段的含义

rd  - readable
wr  - writeable
ex  - executable
sh  - shared
mr  - may read
mw  - may write
me  - may execute
ms  - may share
gd  - stack segment growns down
pf  - pure PFN range
dw  - disabled write to the mapped file
lo  - pages are locked in memory
io  - memory mapped I/O area
sr  - sequential read advise provided
rr  - random read advise provided
dc  - do not copy area on fork
de  - do not expand area on remapping
ac  - area is accountable
nr  - swap space is not reserved for the area
ht  - area uses huge tlb pages
ar  - architecture specific flag
dd  - do not include area into core dump
sd  - soft-dirty flag
mm  - mixed map area
hg  - huge page advise flag
nh  - no-huge page advise flag
mg  - mergable advise flag

文件及权限

特殊的文件权限

i: 不可修改权限

e.g. 如果对/etc/shadow添加了i权限,那么就没有办法添加删除和修改linux账号了,可以作为安全加固的一个小方法

a: 只可追加权限

e.g. 这个权限让日志文件只能追加,不能删除,而且不能通过编辑器追加

t: 除了自己,别人无法删除权限

查看文件权限, 如果存在 ia, root也不能修改

lsattr filename

锁定文件权限

chattr  +ia filename

解锁

chattr -ia filename

文件恢复 (不一定有效)

debugfs
open /dev/sda1
ls -d /someDir
logdump -i <someNo>

显示

Inode 399405 is at group 48, block 1573089, offset 1536
Inode 138815 is at group 16, block 524561, offset 3840
block 2621985, offset 2432

dd if=/dev/vda1 of=/opt/logs/fi-business/2022-11-15.0.log.gz bs=4096 count=1 skip=1573089

from ext4 to xfs

sudo fstransform /dev/mapper/vg0-lv0 xfs

安全上下文

一个「进程安全上下文」一般对应多个「文件安全上下文」, 只有两者的安全上下文对应上了,进程才能访问文件。

文件安全上下文由文件创建的位置和创建文件的进程所决定。需要注意的是,单纯的移动文件操作并不会改变文件的安全上下文。

安全上下文有四个字段,分别用冒号隔开。形如:

system_u:object_r:admin_home_t:s0
viz. user:role:type:range

查看文件的安全上下文

ls -Z
e.g.  -rw-r--r--. root root system_u:object_r:net_conf_t:s0 /etc/hosts

启用

vim /etc/sysconfig/selinux

SELINUX=enforcing        # options: permissive, disabled
  1. enforcing:强制模式。违反 SELinux 规则的行为将被阻止并记录到日志中。

  2. permissive:宽容模式。违反 SELinux 规则的行为只会记录到日志中。一般为调试用。

  3. disabled:关闭 SELinux。

修改文件或目录的安全上下文

chcon -u aaa_u -r bbb_r -t ccc_t test
e.g. chcon -t samba_share_t -P /mldata
     chown -R sherlock:samba /mldata

恢复文件或目录默认的安全上下文

restorecon -rv /usr/local/bin/

添加目录的默认安全上下文

e.g. 为 Nginx 新增一个网站目录 /usr/share/nginx/html2 之后,需要为其设置与原目录相同的默认安全上下文

semanage fcontext -a -t httpd_sys_content_t "/usr/share/nginx/html2(/.*)?"

添加某类进程允许访问的端口

e.g. 为 Nginx 需要使用 10080 的端口用于 HTTP 服务

semanage port -a -t http_port_t -p tcp 10080

查询某个命令或进程的布尔型规则及其状态

getsebool -a | grep httpd

e.g. httpd_anon_write --> off
    httpd_builtin_scripting --> on
    httpd_can_check_spam --> off
    httpd_can_connect_ftp --> off

开关一个布尔型规则

setsebool -P httpd_anon_write on

违规日志分析

SELinux 违规日志保存在 /var/log/audit/audit.log

sealert -a /var/log/audit/audit.log

ACL 权限

以用户和用户组为主体给用户和用户组加权限让他们去有访问文件的权限

目录后有+号 表示 ACL 权限管理

查看权限

sudo getfacl dir

修改权限

sudo setfacl -m u:username:rwx dir
sudo setfacl -m g:grpname:rwx dir

移除用户

sudo setfacl -x u:username dir

擦除所有

sudo setfacl -b dir

时间修改及超多数量拷贝

修改文件时间

zc ubuntugege.txt

拷贝超多文件

可以把当前目录下的所有文件移到 "../matchres"下

ls | xargs -t -I {} mv {} ../matchres/

add sudo user

visudo

创建不可登陆的,无HOME目录的 用户名

useradd -s /bin/false -M nginx

默认会创建一个与用户名一样的用户组

随机密码生成

LC_ALL=C </dev/urandom tr -dc A-Za-z0-9 | head -c 64 && echo

strings /dev/urandom |tr -dc A-Za-z0-9 | head -c20; echo

openssl rand -base64 10

dd if=/dev/urandom bs=1 count=15 2>/dev/null|base64 -w 0

压缩与解压

下面的参数-f是必须的 -f: 使用档案名字,切记,这个参数是最后一个参数,后面只能接档案名。

压缩

tar -cJf my_archieve.tar.xz  /some_dir    # 压缩率最高

tar –cvf jpg.tar *.jpg //将目录里所有jpg文件打包成jpg.tar

tar –czf jpg.tar.gz *.jpg   //将目录里所有jpg文件打包成jpg.tar后,并且将其用gzip压缩,生成一个gzip压缩过的包,命名为jpg.tar.gz

tar –cjf jpg.tar.bz2 *.jpg //将目录里所有jpg文件打包成jpg.tar后,并且将其用bzip2压缩,生成一个bzip2压缩过的包,命名为jpg.tar.bz2

tar –cZf jpg.tar.Z *.jpg   //将目录里所有jpg文件打包成jpg.tar后,并且将其用compress压缩,生成一个umcompress压缩过的包,命名为jpg.tar.Z

rar a jpg.rar *.jpg //rar格式的压缩,需要先下载rar for linux

zip jpg.zip *.jpg //zip格式的压缩,需要先下载zip for linux

解压

tar –xvf file.tar

tar -xJf archieve.tar.xz

tar -xzvf file.tar.gz

tar -xjvf file.tar.bz2  

tar –xZvf file.tar.Z  

unrar e file.rar

unzip file.zip

解压小结

  1. *.tar 用 tar –xvf 解压
  2. *.gz 用 gzip -d或者gunzip 解压
  3. .tar.gz和.tgz 用 tar –xzf 解压
  4. *.bz2 用 bzip2 -d或者用bunzip2 解压
  5. *.tar.bz2用tar –xjf 解压
  6. *.Z 用 uncompress 解压
  7. *.tar.Z 用tar –xZf 解压
  8. *.rar 用 unrar e解压
  9. *.zip 用 unzip 解压

加密压缩/解密解压

tar cvf - src_file_or_dir | openssl des3 -salt -k password -out target_file.tar
openssl des3 -d -k password -salt -in src.tar | tar xvf -

清除痕迹

history -w
history -c

echo > /var/log/wtmp    //清除用户登录记录
echo > /var/log/btmp    //清除尝试登录记录
echo > /var/log/lastlog //清除最近登录信息
echo > /var/log/secure  //登录信息
echo > /var/log/syslog  //记录系统日志的服务

echo > /var/log/messages
echo > /var/log/xferlog
echo > /var/log/auth.log
echo > /var/log/user.log
cat /dev/null > /var/adm/sylog
cat /dev/null > /var/log/maillog
cat /dev/null > /var/log/openwebmail.log
cat /dev/null > /var/log/mail.info
echo>/var/run/utmp

echo > .bash_history //清除保存的用户操作历史记录
history -cw //清除所有历史

U 盘操作

U 盘格式化

sudo fdisk -l
sudo mkfs.exfat -n LABEL /dev/sdx
sudo fsck.exfat /dev/sdx

U 盘初始化

U 盘做了引导盘后,有时不能重新格式化时,需要重做初始化

sudo fdisk -l     查看U盘是哪个设备,假设是/def/sda1
sudo umount /dev/sda1
sudo mkfs.vfat -F 32 /dev/sda1

这时,初始化已经完成,但U盘容量仍然为引导盘时的容量 需要再进行一次格式化后,即可恢复原状

Categories: #速查手册 Tags: #linux #handbook


Comments

你有问题需要解决,想要分享反馈,或者讨论更多的想法吗?请随时在这里留下评论! 这个讨论将与 discussion on GitHub 直接连接,所以你也可以直接在那边发表评论