修改 Linux 默认 locale 使得非 ASCII 字符在 ssh 下可以正常显示

一般来说,如果没有进行过特殊配置的话(比如说我们用 debootstrap 安装的系统),其默认 locale 会是 POSIX,我们使用 locale 命令可以看到这个情况。

# locale
LANG=
LANGUAGE=
LC_CTYPE="POSIX"
LC_NUMERIC="POSIX"
LC_TIME="POSIX"
LC_COLLATE="POSIX"
LC_MONETARY="POSIX"
LC_MESSAGES="POSIX"
LC_PAPER="POSIX"
LC_NAME="POSIX"
LC_ADDRESS="POSIX"
LC_TELEPHONE="POSIX"
LC_MEASUREMENT="POSIX"
LC_IDENTIFICATION="POSIX"
LC_ALL=

C 是系统默认的 locale,而 POSIX 是 C 的别名,因此这里看到的情况就是默认的 C Locale。它所指定的属性和行为由 ISO C 标准所指定。当我们新安装完一个系统时,默认的 locale 就是 C 或 POSIX。我们这里说的 C 其实就是 ASCII 编码。

但是我们同样也知道,标准的 ASCII 字符集中是不包含中文、日文等字符的,因此如果 POSIX 作为系统默认的 locale,那么会遇到在 SSH 下无法正确显示这些字符的问题,会直接按字节解析为 ASCII 转义字符,因此要想更正这个情况只有修改系统默认的 locale。

通过 locale -a 可以查看当前系统可用的所有 locale:

# locale -a
C
C.UTF-8
POSIX
en_US.utf8

显然,en_US.utf8 是一个好选择。

下面贴过来一段对上文中提及的 LC_* 环境变量的接释:

  • LANG
    LANG的优先级是最低的,它是所有 LC_* 变量的默认值。下方所有以 LC_ 开头变量(不包括LC_ALL)中,如果存在没有设置变量值的变量,那么系统将会使用 LANG 的变量值来给这个变量进行赋值。如果变量有值,则保持不变,不受影响。可以看到,我们上面示例中的输出中的 LC_*变量的值其实就是 LANG 变量决定的
  • LC_CTYPE
    用于字符分类和字符串处理,控制所有字符的处理方式,包括字符编码,字符是单字节还是多字节,如何打印等,这个变量是最重要的。
  • LC_NUMERIC
    用于格式化非货币的数字显示。
  • LC_TIME
    用于格式化时间和日期。
  • LC_COLLATE
    用于比较和排序。
  • LC_MONETORY
    用于格式化货币单位。
  • LC_MESSAGES
    用于控制程序输出时所使用的语言,主要是提示信息,错误信息,状态信息, 标题,标签, 按钮和菜单等。
  • LC_PAPER
    默认纸张尺寸大小
  • LC_NAME
    姓名书写方式
  • LC_ADDRESS
    地址书写方式
  • LC_TELEPHONE
    电话号码书写方式
  • LC_MEASUREMENT
    度量衡表达方式
  • LC_IDENTIFICATION
    locale对自身包含信息的概述
  • LC_ALL
    它不是环境变量,它是一个宏,可通过该变量的设置覆盖所有的 LC_* 变量。这个变量设置之后,可以废除 LC_* 的设置值,使得这些变量的设置值与 LC_ALL 的值一致,注意,LANG 变量不受影响。在这里,这个宏操作就是用 LC_ALL 的值去覆盖 LC_* 的变量值

从描述中可以看出,优先级级别:LC_ALL > LC_* > LANG

注意:定义这么多变量在某些情况下是很有用的,例如,当我需要一个能够输入中文的英文环境,我可以把 LC_CTYPE 设定成 zh_CN.GB18030,而其他所有的项都是 en_US.UTF-8。

如果我们要修改这个默认的变量,可以在自己的 ~/.bashrc 中进行 export,比如我加入了这两行:

export LANG=en_US.utf8
export LC_CTYPE=en_US.utf8

之后重新登录一下就可以正常显示 Unicode 字符了。

Ubuntu 18.04 配置 Online IPv6

Ubuntu 的 netplan.io 实在是太烂了

Online 的 IPv6,之前写过一篇,是通过 /etc/network/interfaces 文件中的 pre-up 字段运行 dhclient 宣告 DUID,然而在 Ubuntu 18.04 中,ifupdown 换成了 netplan.io,原来的方法就不能用了。此外,dhclient 在每次运行的时候会清空当前 iface 上的 IPv6 地址,这个也给 netplan 造成了麻烦。在折腾之后有了一种解决办法:

  • 在 netplan 配置文件中写入静态 IPv6 地址(同 /etc/network/interfaces)
  • 使用 systemd 运行 dhclient
  • 在运行 dhclient 之后再次执行 netplan apply

需要写入如下文件:

/etc/dhcp/dhclient6.conf

interface "<iface>" {
  send dhcp6.client-id <DUID>;
  request;
}

/etc/systemd/system/dhclient.service

[Unit]
Description=dhclient for sending DUID IPv6
Wants=network.target
Before=network.target

[Service]
Type=forking
ExecStart=/sbin/dhclient -cf /etc/dhcp/dhclient6.conf -6 -P -v <iface>

[Install]
WantedBy=multi-user.target

/etc/systemd/system/dhclient-netplan.service

[Unit]
Description=redo netplan apply after dhclient
Wants=dhclient.service
After=dhclient.service
Before=network.target

[Service]
Type=oneshot
ExecStart=/usr/sbin/netplan apply

[Install]
WantedBy=dhclient.service

该文件在 dhclient.service 改变状态之后执行,注意该 oneshot 不能使用 RemainAfterExit=True,不然在 restart dhclient 以后,不能再次执行 netplan apply。

/etc/netplan/01-netcfg.yaml

# This file describes the network interfaces available on your system
# For more information, see netplan(5).
network:
  version: 2
  renderer: networkd
  ethernets:
    <iface>:
      dhcp4: yes
      dhcp6: no
      accept-ra: yes
      addresses:
      - <IPv6>/56

即可。

参考资料:

https://documentation.online.net/en/dedicated-server/network/network-configuration-with-netplan
https://documentation.online.net/en/dedicated-server/network/ipv6/prefix
https://askubuntu.com/questions/1031853/how-to-set-up-ipv6-with-netplan-on-ubuntu-18-04?rq=1
https://lafibre.info/scaleway/configurer-une-ipv6-online-net-sur-ubuntu-18-04/
https://unix.stackexchange.com/questions/219940/getting-a-service-started-automatically-when-another-gets-started

Online Dedibox IPv6 配置

Ubuntu 18.04 配置 HE Tunnelbroker

Ubuntu 18.04 配置 HE Tunnelbroker

我无比怀念 Ubuntu 还在用 ifupdown 的年代,netplan.io 什么垃圾玩意。

起因是这样的,看阿里云打折,所以买了一个轻量 HK 来玩。阿里云的机器嘛,拿到手第一件事肯定是跑脚本重装系统,然后就默认装了最新的 Ubuntu 18.04。接下来发现,这机器没有 IPv6,于是就打算配置一下 HE Tunnelbroker,这时候问题就出现了,18.04 默认已经没有 ifupdown 而改用 netplan.io,HE 也没有提供 netplan 的样例配置。考虑到用新不用旧的原则(类比 systemd 和 Ubuntu upstart/update-rc.d),于是想折腾一下 netplan。

netplan 官网上宣称,它可以配置 6in4 Tunnel,还在官方示例(https://netplan.io/examples)最下面给出了对应的语法,然而在最新的 Ubuntu 内置版本上,tunnels 这个关键字根本不识别。查询 bug 发现在 https://bugs.launchpad.net/netplan/+bug/1799487 这里有人提到 netplan 无法使用 6in4 tunnel 的问题,也提出了一个使用 systemd-networkd 的解决方案。netplan 的 tunnel 支持根据这个 bug report 的说法,已经在一月初 commit,可能还没合并到软件包的版本里面吧。

阿里云的镜像,默认的网络配置是在 /etc/netplan/01-netcfg.yaml 中,默认是 DHCPv4 的方式获取内网 IP。我们先通过这里看到 v4 interface 的名称,比如 ens3。之后创建如下文件:

/etc/systemd/network/he-ipv6.netdev

[NetDev]
Name=he-ipv6
Kind=sit
MTUBytes=1480

[Tunnel]
Local=<local IPv4 address> # should be the internal IPv4 address on Aliyun instead of the public IPv4 address
Remote=<tunnel server IPv4 address>
TTL=64

/etc/systemd/network/he-ipv6.network

[Match]
Name=he-ipv6

[Network]
Address=<Client IPv6 Address>/64
Gateway=<Server IPv6 Address>

/etc/systemd/network/10-netplan-<iface name>.network.d/tunnel.conf
e.g. /etc/systemd/network/10-netplan-ens3.network.d/tunnel.conf

[Network]
Tunnel=he-ipv6

表示在默认的 v4 interface 上创建 tunnel。之后 systemctl restart systemd-networkd 即可

或者也可以使用 ip 命令在一个 systemd service 中完成添加:

[Unit]
Description=he.net IPv6 tunnel
After=network.target

[Service]
Type=oneshot
RemainAfterExit=yes
ExecStart=/sbin/ip tunnel add he-ipv6 mode sit remote <server IPv4 address> local <client IPv4 address> ttl 255
ExecStart=/sbin/ip link set he-ipv6 up mtu 1480
ExecStart=/sbin/ip addr add <client IPv6 address> dev he-ipv6
ExecStart=/sbin/ip -6 route add ::/0 dev he-ipv6
ExecStop=/sbin/ip -6 route del ::/0 dev he-ipv6
ExecStop=/sbin/ip link set he-ipv6 down
ExecStop=/sbin/ip tunnel del he-ipv6

[Install]
WantedBy=multi-user.target

 

关于 qb 有可能遇到 ICE default IO error handler doing an exit() 的问题

在一些性能比较弱的机器上,比如 N2800,运行 qb 时可能会报错 ICE default IO error handler doing an exit()。

说实话这是一个比较莫名其妙的错误,ICE 是一个 X client 之间通信的协议,可能是 Qt 在后台有什么操作吧。StackOverflow 上对于这个错误的解决方法是删除 ~/.ICEauthority 文件,然后就好了。我猜可能是 qb 启动如果卡太久 GUI 出不来的话会把这个文件搞坏,然后再启动的时候读取这个文件就炸了,删除这个文件以后 Qt 可能会报一个 warning,authentication failure 之类的,这个不影响运行,而且 qb 也不会炸了。

这个文件在每次启动桌面时会自动生成,因此已加入 qb 自动重启脚本中的错误检测豪华套餐。

关于 VSCode 在 VNC 下可能无法正常使用的问题

code –verbose 后报错:

[20447:0201/143519.959342:ERROR:browser_main_parts.cc(139)] X IO error received (X server probably went away)

这个似乎是一个 Electron 里面的 bug,一个可行的解决方案为:

# make a copy of the relevant library
mkdir ~/lib
cp /usr/lib/x86_64-linux-gnu/libxcb.so.1 ~/lib
sed -i 's/BIG-REQUESTS/_IG-REQUESTS/' ~/lib/libxcb.so.1
# set the dynamic loader path to put your library first before executing VS Code
LD_LIBRARY_PATH=$HOME/lib code

即修改一下 libxcb.so.1 之后再执行

对于 .desktop 文件启动的 VSCode(桌面的菜单项),将上述修改的 .so 文件复制到比如 /opt/code_lib 下,将 /usr/share/applications/code.desktop 中的 Exec 项修改为:

Exec=sh -c "env LD_LIBRARY_PATH\=/opt/code_lib /usr/share/code/code" %U

即可

关于在 VNC 中可能无法使用 Tab 键的问题

仅作为记录,似乎这是个 Ubuntu 16.04 的 bug,在 18.04 中已经被修正

在 ~/.config/xfce4/xfconf/xfce-perchannel-xml/xfce4-keyboard-shortcuts.xml 中找到这样一行:

<property name="&lt;Super&gt;Tab" type="string" value="switch_window_key"/>

<property name=”&lt;Super&gt;Tab” type=”string” value=”switch_window_key”/>

改成

<property name="&lt;Super&gt;Tab" type="empty"/>

<property name=”&lt;Super&gt;Tab” type=”empty”/>

就行了

Kimsufi KS-4 在 Rescue 模式下安装 Ubuntu 18.04

这两天 KS-4 搞特价,4T 硬盘只要 9o,是公网保种机的绝佳配置,于是买买买。下机以后发现默认的系统模板沙雕了,/ 分区不能使用 SoftRaid0 方式挂载,虽然可以曲线救国把 /root 分一大块出来做 R0,但是我不喜欢这种分区方式,于是就想办法重装这个系统。

一开始我测试了 Airium 修改的 Vicer 重装脚本,这个脚本可以把 iKoula 的机器重装成 Raid0,然而不知道为什么一重装就 SSH 不通,遂放弃。在考察了 KS 的后台分区以后,发现 / 分区只能做成 Raid1,并且无法在上面配置 LVM,看来走官方模板的路子是走不通了。于是就剩下一个方法,在 Rescue 模式下手动安装系统。

KS 的机器在 Rescue 下安装系统主要有两个方法,一是使用 dd 安装 Windows,这不是我想要的,而且我也没有 Ubuntu 的 dd 镜像;二是下载系统镜像的 iso 安装包,使用 QEMU 将硬盘挂载以后在虚拟环境中完成安装。奈何 N2800 这个 CPU 过于古老,没有 VT-x 指令集,因而无法运行 QEMU,只好另辟蹊径。

在寻找如何在 Linux 下安装一个 ISO 文件的过程中,偶然发现了 Debian 系的 debootstrap 脚本,这个脚本可以在任何 Linux 环境下安装 Debian 系的 Linux。作为 Debian 的下游,Ubuntu 也有这个脚本,在一些文档的帮助下,我感觉这条路是可行的。

从一个比较 high-level 的角度来看,使用 debootstrap 安装系统需要如下几步:

  • 备份原系统的网络配置(不然等下 SSH 连不上)
  • 硬盘分区,配置 SoftwareRAID
  • 挂载硬盘
  • 在挂载文件夹下运行 debootstrap
  • chroot 进入新系统
  • 在 debootstrap 完成的基础系统中完成硬盘挂载、SSH、网络和引导的配置
  • 将启动方式还原为 local disk,重启

接下来是详细的步骤

备份网络配置

由于我要安装的是 Ubuntu 18.04,因此知道 OVH 的网络配置的最佳方法就是用官方模板装一遍,然后把配置弄出来。在安装之后,发现 OVH 并没使用 netplan,也没有使用之前的 ifupdown 文件,而是直接写了 systemd-networkd 的配置文件。需要备份的是这两个文件:

  • /etc/systemd/network/50-default.network
  • /etc/systemd/network/50-public-interface.link

文件名可能不同,但是基本就是在这个目录下。对于老版本的系统,备份 /etc/network/interfaces。

硬盘分区

理想的分区情况是这样的,两块盘 /dev/sda 和 /dev/sdb,分一个 512M 的区做 R1 挂载在 /boot,剩下的挂载在 /,swap 这里不考虑,装好了以后写一个文件作为 swap 是很方便的事情。

分区使用命令 fdisk,比如 fdisk /dev/sda,之后进入命令行交互环境。一个典型的配置情况如下:

root@rescue:/mnt# fdisk /dev/sda

Welcome to fdisk (util-linux 2.25.2).
Changes will remain in memory only, until you decide to write them.
Be careful before using the write command.

Device does not contain a recognized partition table.
Created a new DOS disklabel with disk identifier 0xc1d78def.

Command (m for help): n
Partition type
   p   primary (0 primary, 0 extended, 4 free)
   e   extended (container for logical partitions)
Select (default p): p
Partition number (1-4, default 1): 1
First sector (2048-3907029167, default 2048):
Last sector, +sectors or +size{K,M,G,T,P} (2048-3907029167, default 3907029167): +512M

Created a new partition 1 of type 'Linux' and of size 512 MiB.

Command (m for help): n
Partition type
   p   primary (1 primary, 0 extended, 3 free)
   e   extended (container for logical partitions)
Select (default p): p
Partition number (2-4, default 2):
First sector (1050624-3907029167, default 1050624):
Last sector, +sectors or +size{K,M,G,T,P} (1050624-3907029167, default 3907029167):

Created a new partition 2 of type 'Linux' and of size 1.8 TiB.

按 n 增加分区,然后输入 p 作为 primary 分区,然后指定大小,默认是全部,因此第一个分区输入 +512M,第二个保持默认就好。

之后用 fdisk -l 可以看到当前的分区情况:

root@rescue:/mnt# fdisk -l /dev/sda

Disk /dev/sda: 1.8 TiB, 2000398934016 bytes, 3907029168 sectors
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disklabel type: dos
Disk identifier: 0xc1d78def

Device     Boot   Start        End    Sectors  Size Id Type
/dev/sda1          2048    1050623    1048576  512M 83 Linux
/dev/sda2       1050624 3907029167 3905978544  1.8T 83 Linux

/dev/sdb 如法炮制即可。

创建 RAID 设备

使用命令 mdadm,先删除之前所有创建的 md,之后再创建新的,命令如下:

mdadm --stop /dev/mdX
mdadm --remove /dev/mdX
mdadm --create --verbose /dev/md0 --level=mirror --raid-devices=2 /dev/sda1 /dev/sdb1
mdadm --create --verbose /dev/md1 --level=stripe  --raid-devices=2 /dev/sda2 /dev/sdb2

前两行多运行几遍直到所有的都被删除,之后创建 /dev/md0 RAID1 作为 /boot,/dev/md1 RAID0 作为 /。

使用 lsblk 可以看到当前的分区情况:

root@rescue:/mnt# lsblk
NAME    MAJ:MIN RM   SIZE RO TYPE  MOUNTPOINT
sdb       8:16   0   1.8T  0 disk
├─sdb2    8:18   0   1.8T  0 part
│ └─md1   9:1    0   3.7T  0 raid0
└─sdb1    8:17   0   512M  0 part
  └─md0   9:0    0 511.4M  0 raid1
sda       8:0    0   1.8T  0 disk
├─sda2    8:2    0   1.8T  0 part
│ └─md1   9:1    0   3.7T  0 raid0
└─sda1    8:1    0   512M  0 part
  └─md0   9:0    0 511.4M  0 raid1

然后格式化成 ext4 文件系统:

mkfs.ext4 /dev/md0
mkfs.ext4 /dev/md1

安装 debootstrap

这里下载 Ubuntu 的 debootstrap 脚本,应该会获得一个类似于 debootstrap_1.0.112ubuntu1_all.deb 的 deb 文件,使用 ar 释放后再解压至根目录下。ar 后会看到一个 data.tar.gz,释放到根目录下。

ar -x debootstrap_1.0.112ubuntu1_all.deb
cd /
zcat /pat/to/data.tar.gz|tar xv

运行 debootstrap

接下来才是重头戏,挂载之前分好区的硬盘并安装系统。我们假定 /dev/md1 挂载在 /mnt/ubuntu 下,然后对 /mnt/ubuntu 运行 debootstrap 安装系统。

mkdir /mnt/ubuntu
mount /dev/md1 /mnt/ubuntu
debootstrap --arch amd64 bionic /mnt/ubuntu http://fr.archive.ubuntu.com/ubuntu/

debootstrap 的命令中,要求安装 amd64 架构的 Ubuntu bionic,也就是 18.04,系统在 /mnt/ubuntu 下,需要的包从 http://fr.archive.ubuntu.com/ubuntu/ 获得。

稍等一会儿, 看到 I: Base system installed successfully. 就安装完成了。

chroot 准备

debootstrap 完成以后,在 /mnt/ubuntu 下就可以看到熟悉的 Linux 根目录结构了,然后我们做一些准备以方便 chroot 运行。

mount /dev/md0 /mnt/ubuntu/boot
mount --bind /dev /mnt/ubuntu/dev
mount --bind /dev/pts /mnt/ubuntu/dev/pts
mount -t proc proc /mnt/ubuntu/proc
mount -t sysfs sys /mnt/ubuntu/sys

第一行是将 /dev/md0 挂载在 /boot 下,这是和我们的分区意图相一致,2 3 两行是将当前系统的 /dev 映射进去,方便之后配置 fstab 文件,4 5 两行是将 proc 和 sysfs 挂载进去,方便配置时进行交互。

之后 chroot 进入新的环境:

LANG=C.UTF-8 chroot /mnt/ubuntu /bin/bash
locale-gen en_US.UTF-8
export TERM=xterm-color

如果进去以后报错 “bash: warning: setlocale: LC_ALL: cannot change locale (en_US.UTF-8)” 则运行第二行,如果不报错就不用管了,第三行是为了 Terminal 好看点。

系统配置

首先配置一下软件包的源,编辑 /etc/apt/sources.list 写入如下几行:

deb http://fr.archive.ubuntu.com/ubuntu bionic main restricted
deb http://fr.archive.ubuntu.com/ubuntu bionic universe
deb http://fr.archive.ubuntu.com/ubuntu bionic multiverse
deb http://fr.archive.ubuntu.com/ubuntu bionic-backports main restricted universe multiverse
deb http://security.ubuntu.com/ubuntu/ bionic-security main restricted
deb http://security.ubuntu.com/ubuntu/ bionic-security universe
deb http://security.ubuntu.com/ubuntu/ bionic-security multiverse

并且 apt update。完成后先安装 mdadm 和 lvm2,方便之后挂载。

apt-get install mdadm lvm2

编辑 /etc/fstab 写入挂载方式:

# file system    mount point   type    options                  dump pass
/dev/md0        /boot   ext4    defaults        0       2
/dev/md1        /       ext4    defaults        0       1

运行 mount -a 自动挂载,此时使用 df 命令应该可以看到正确的硬盘占用了。

运行 passwd 命令配置 root 用户密码,如果需要加用户,此时使用 adduser 即可。

之后安装内核和 OpenSSH Server:

apt-get install openssh-server linux-generic linux-tools-generic

正常情况下,在安装时会弹出 grub 的配置窗口,选择将 GRUB 安装在 /dev/sda 和 /dev/sdb,如果没有询问的话,安装完成以后运行如下两行:

grub-install /dev/sda
grub-install /dev/sdb

之后写回最开始备份的网络配置文件,并且启动相关服务:

vi /etc/systemd/network/50-default.network
vi /etc/systemd/network/50-public-interface.link

systemctl enable systemd-networkd
systemctl enable systemd-resolved

注意如果只有 root 用户的话,去 /etc/ssh/sshd_config 中修改 PermitRootLogin 为 yes,不然可能无法登录。

这时候就安装完成了,去 KS 后台用本地硬盘重启即可。在登录后记得配置 Public Key,然后将 PermitRootLogin 改为 PermitRootLogin prohibit-password。

最后在安装好的系统中使用 df 和 lsblk 命令查看分区情况:

root@rescue:~# df -h
Filesystem      Size  Used Avail Use% Mounted on
udev            1.9G     0  1.9G   0% /dev
tmpfs           393M  676K  393M   1% /run
/dev/md1        3.6T  1.3G  3.4T   1% /
tmpfs           2.0G     0  2.0G   0% /dev/shm
tmpfs           5.0M     0  5.0M   0% /run/lock
tmpfs           2.0G     0  2.0G   0% /sys/fs/cgroup
/dev/md0        488M   73M  386M  16% /boot
tmpfs           393M     0  393M   0% /run/user/0
root@rescue:~# lsblk
NAME    MAJ:MIN RM   SIZE RO TYPE  MOUNTPOINT
sda       8:0    0   1.8T  0 disk
|-sda1    8:1    0   512M  0 part
| `-md0   9:0    0 511.4M  0 raid1 /boot
`-sda2    8:2    0   1.8T  0 part
  `-md1   9:1    0   3.7T  0 raid0 /
sdb       8:16   0   1.8T  0 disk
|-sdb1    8:17   0   512M  0 part
| `-md0   9:0    0 511.4M  0 raid1 /boot
`-sdb2    8:18   0   1.8T  0 part
  `-md1   9:1    0   3.7T  0 raid0 /

参考资料

系统安装相关:

https://www.codejam.info/2015/12/installing-nixos-on-a-kimsufi.html
https://help.ubuntu.com/community/Installation/MinimalCD
https://www.pozzo-balbi.com/help/Remote_install_OS_-_Qemu
https://help.ubuntu.com/community/Installation/FromLinux
https://help.ubuntu.com/lts/installation-guide/powerpc/apds04.html
http://ports.ubuntu.com/ubuntu-ports/pool/main/d/debootstrap/

分区相关:

https://wiki.archlinux.org/index.php/Fdisk_(%E7%AE%80%E4%BD%93%E4%B8%AD%E6%96%87)
https://raid.wiki.kernel.org/index.php/RAID_setup#Create_RAID_device
https://wiki.archlinux.org/index.php/Fstab_(%E7%AE%80%E4%BD%93%E4%B8%AD%E6%96%87)

配置相关:

https://help.ubuntu.com/community/Installation/SoftwareRAID
https://wiki.archlinux.org/index.php/systemd-networkd#Basic_usage
https://unix.stackexchange.com/questions/158400/etc-shadow-how-to-generate-6-s-encrypted-password

nginx 规则匹配与 acme.sh 自动证书续签

这事情是这样的,我半个月前发现之前配置的 Syncthing 代理和 Nextcloud 页面的 Let’s Encrypt 证书都过期了。按道理讲,oneinstack 的一键包会添加一个 crontab 任务自动续签证书,但是就是没 work,手动执行 acme.sh 以后,发现是域名认证无法通过,请求 .well-known/acme-challenge下的认证文件时返回 403 错误。

Nextcloud 使用了一键包中提供的 rewrite rule,Syncthing 是自己改的 nginx 配置文件,而同服务器上的 h5ai 却续签正常。在这种情况下,首先肯定是怀疑自己改错了。

在查看了 Syncthing 的 nginx 配置文件以后,发现默认将所有请求转发给 localhost 处理,那么问题就显而易见了,Syncthing 不知道那个 .well-known 是什么东西,自然返回错误,因此加上了一条 location 规则:

location /.well-known {
}

嗯,可以续签了。

但是同样的方法在 Nextcloud 上却行不通,加入了这个规则以后,curl 仍然返回 403 错误。查看了 oneinstack 的 rewrite rule 以后发现,似乎本来就对这个目录做了处理。考虑到一堆正则看得头大,想着直接 override 掉好了,在查找了 location 的用法以后,加上了这么一行:

location ^~ /.well-known/acme-challenge {
}

然后就能用了。

最后再转载一些对于 location 用法的解释:

location  = / {
  # 精确匹配 / ,主机名后面不能带任何字符串
  [ configuration A ]
}

location  / {
  # 因为所有的地址都以 / 开头,所以这条规则将匹配到所有请求
  # 但是正则和最长字符串会优先匹配
  [ configuration B ]
}

location /documents/ {
  # 匹配任何以 /documents/ 开头的地址,匹配符合以后,还要继续往下搜索
  # 只有后面的正则表达式没有匹配到时,这一条才会采用这一条
  [ configuration C ]
}

location ~ /documents/Abc {
  # 匹配任何以 /documents/Abc 开头的地址,匹配符合以后,还要继续往下搜索
  # 只有后面的正则表达式没有匹配到时,这一条才会采用这一条
  [ configuration CC ]
}

location ^~ /images/ {
  # 匹配任何以 /images/ 开头的地址,匹配符合以后,停止往下搜索正则,采用这一条。
  [ configuration D ]
}

location ~* \.(gif|jpg|jpeg)$ {
  # 匹配所有以 gif,jpg或jpeg 结尾的请求
  # 然而,所有请求 /images/ 下的图片会被 config D 处理,因为 ^~ 到达不了这一条正则
  [ configuration E ]
}

location /images/ {
  # 字符匹配到 /images/,继续往下,会发现 ^~ 存在
  [ configuration F ]
}

location /images/abc {
  # 最长字符匹配到 /images/abc,继续往下,会发现 ^~ 存在
  # F与G的放置顺序是没有关系的
  [ configuration G ]
}

location ~ /images/abc/ {
  # 只有去掉 config D 才有效:先最长匹配 config G 开头的地址,继续往下搜索,匹配到这一条正则,采用
    [ configuration H ]
}

location ~* /js/.*/\.js

已=开头表示精确匹配
如 A 中只匹配根目录结尾的请求,后面不能带任何字符串。
^~ 开头表示uri以某个常规字符串开头,不是正则匹配
~ 开头表示区分大小写的正则匹配;
~* 开头表示不区分大小写的正则匹配
/ 通用匹配, 如果没有其它匹配,任何请求都会匹配到
顺序 no优先级:
(location =) > (location 完整路径) > (location ^~ 路径) > (location ~,~* 正则顺序) > (location 部分起始路径) > (/)

上面的匹配结果
按照上面的location写法,以下的匹配示例成立:

/ -> config A
精确完全匹配,即使/index.html也匹配不了
/downloads/download.html -> config B
匹配B以后,往下没有任何匹配,采用B
/images/1.gif -> configuration D
匹配到F,往下匹配到D,停止往下
/images/abc/def -> config D
最长匹配到G,往下匹配D,停止往下
你可以看到 任何以/images/开头的都会匹配到D并停止,FG写在这里是没有任何意义的,H是永远轮不到的,这里只是为了说明匹配顺序
/documents/document.html -> config C
匹配到C,往下没有任何匹配,采用C
/documents/1.jpg -> configuration E
匹配到C,往下正则匹配到E
/documents/Abc.jpg -> config CC
最长匹配到C,往下正则顺序匹配到CC,不会往下到E

 

qBittorrent 自动重启

因为 libtorrent 全是 bug,所以用了它的 qBittorrent 经常会莫名其妙出现一些错误,然后自己挂掉。虽然每次的 backtrace 都是在 libtorrent 里面出现的错误,但是同样用了 lt 的 Deluge 就一点都没有问题,所以估计这俩的代码都写得太差吧。

因此,写了一个简单的脚本去监控 qb 的运行状态,在它挂掉的时候自动重启,在漏内存的时候自动 kill 掉重开一个。

具体代码可以看 Github。默认行为是在 Xserver 存在的情况下启动 GUI 版的 qb,如果没有 Xserver 就启动 qbittorrent-nox,一分钟检测一次运行状态,挂了就重开。

如果在 VNC 下面使用 GUI 版的 qb,且想要随着桌面自动启动的话,需要在 ~/.config/autostart 下放置一个 desktop 文件

如果想看到一个 Terminal,并且监控一下重启的原因,用这个版本:

[Desktop Entry]
Encoding=UTF-8
Version=0.9.4
Type=Application
Name=qBittorrent-daemon
Comment=
Exec=bash -c '/usr/bin/python3 <path>/launch.py'
OnlyShowIn=XFCE;
StartupNotify=false
Terminal=true
Hidden=false

如果不需要看到那个 Terminal,用这个版本:

[Desktop Entry]
Encoding=UTF-8
Version=0.9.4
Type=Application
Name=qBittorrent-daemon
Comment=
Exec=/usr/bin/python3 <path>/launch.py
OnlyShowIn=XFCE;
StartupNotify=false
Terminal=false
Hidden=false