<?xml version="1.0" encoding="utf-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" version="2.0"><channel><title>愚蠢的地球人</title><link>https://blog.fairysoft.net/</link><description>Hello, Earth!</description><item><title>在不同Linux系统下用脚本实现SSL证书的自动更新</title><link>https://blog.fairysoft.net/post/68.html</link><description>&lt;p&gt;好像是从去年开始，各大ssl证书服务商统一把免费证书的有效期从 1 年缩短到了 90 天，以前一年更新一次，手动操作还可以忍，现在每年要操作四到五次，就有点麻烦了。于是我在 Windows Server 云服务器上部署了 Win-acms，解决了证书更新的问题。但是我还有几台其他的 Linux 设备，也需要同步更新，于是我想到了通过计划任务定时执行脚本来实现证书自动更新。&lt;/p&gt;&lt;p&gt;首先，把 Win-acms 自动下载的证书挂到 ftp 下，直接用 IIS 内置的的 ftp 服务就行，注意开启 SSL。&lt;/p&gt;&lt;p&gt;原理很简单，写一个 sh 脚本检查证书的有效期，如果临近过期，则连接到远程 ftp 服务器，下载最新的证书并替换原来快过期的证书，然后重启 Web 服务器。最后把这个脚本添加到系统的计划任务中，每天定时执行就行了。&lt;/p&gt;&lt;p&gt;这一切在 Ubuntu 和 Debian 系统下都非常容易实现，直接贴代码，/etc/ssl/update_ssl.sh：&lt;/p&gt;&lt;pre class=&quot;prettyprint&quot;&gt;#!/usr/bin/bash
#&amp;nbsp;自动获取当前脚本所在的绝对路径
SCRIPT_DIR=$(cd&amp;nbsp;&amp;quot;$(dirname&amp;nbsp;&amp;quot;$0&amp;quot;)&amp;quot;;&amp;nbsp;pwd)

#&amp;nbsp;如果不是在终端运行（说明是&amp;nbsp;crontab&amp;nbsp;触发），则启用静默和错误日志模式
if&amp;nbsp;[&amp;nbsp;!&amp;nbsp;-t&amp;nbsp;1&amp;nbsp;];&amp;nbsp;then
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;exec&amp;nbsp;2&amp;gt;&amp;gt;&amp;nbsp;&amp;quot;${SCRIPT_DIR}/update_ssl_err.log&amp;quot;&amp;nbsp;&amp;gt;/dev/null
fi

#&amp;nbsp;====================&amp;nbsp;配置区域&amp;nbsp;====================
FTP_HOST=&amp;quot;ftp.your_host&amp;quot;
FTP_USER=&amp;quot;ftp_user&amp;quot;
FTP_PASS=&amp;quot;password&amp;quot;
FTP_DIR=&amp;quot;/ssl&amp;quot;
FTP_URL=&amp;quot;ftp://${FTP_HOST}${FTP_DIR}&amp;quot;
CURL_OPTS=&amp;quot;--ssl-reqd&amp;nbsp;-k&amp;nbsp;-sS&amp;nbsp;-u&amp;nbsp;${FTP_USER}:${FTP_PASS}&amp;quot;

REMOTE_CERT_FILE=&amp;quot;fairysoft.net-chain.pem&amp;quot;&amp;nbsp;
REMOTE_KEY_FILE=&amp;quot;fairysoft.net-key.pem&amp;quot;&amp;nbsp;&amp;nbsp;

LOCAL_CERT_FILE=&amp;quot;/etc/ssl/fairysoft.net-chain.pem&amp;quot;
LOCAL_KEY_FILE=&amp;quot;/etc/ssl/fairysoft.net-key.pem&amp;quot;

NGINX_RELOAD_CMD=&amp;quot;systemctl&amp;nbsp;reload&amp;nbsp;nginx&amp;quot;
EXPIRE_DAYS=28
#&amp;nbsp;=================================================

#&amp;nbsp;检查本地证书文件是否存在
if&amp;nbsp;[&amp;nbsp;!&amp;nbsp;-f&amp;nbsp;&amp;quot;$LOCAL_CERT_FILE&amp;quot;&amp;nbsp;];&amp;nbsp;then
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;echo&amp;nbsp;&amp;quot;本地证书文件不存在，将直接下载新证书。&amp;quot;
else
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;#&amp;nbsp;获取当前证书的过期时间并计算剩余天数
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;EXPIRE_DATE=$(openssl&amp;nbsp;x509&amp;nbsp;-in&amp;nbsp;&amp;quot;$LOCAL_CERT_FILE&amp;quot;&amp;nbsp;-enddate&amp;nbsp;-noout&amp;nbsp;|&amp;nbsp;cut&amp;nbsp;-d=&amp;nbsp;-f2)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;EXPIRE_SECS=$(date&amp;nbsp;-d&amp;nbsp;&amp;quot;$EXPIRE_DATE&amp;quot;&amp;nbsp;+%s)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;CURRENT_SECS=$(date&amp;nbsp;+%s)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;DAYS_LEFT=$((&amp;nbsp;($EXPIRE_SECS&amp;nbsp;-&amp;nbsp;$CURRENT_SECS)&amp;nbsp;/&amp;nbsp;86400&amp;nbsp;))
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;EXPIRE_DATE=$(date&amp;nbsp;-d&amp;nbsp;&amp;quot;$EXPIRE_DATE&amp;quot;&amp;nbsp;&amp;#39;+%Y-%m-%d&amp;#39;)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;#&amp;nbsp;判断是否需要更新
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if&amp;nbsp;[&amp;nbsp;&amp;quot;$DAYS_LEFT&amp;quot;&amp;nbsp;-ge&amp;nbsp;&amp;quot;$EXPIRE_DAYS&amp;quot;&amp;nbsp;];&amp;nbsp;then
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;echo&amp;nbsp;&amp;quot;当前证书有效期($EXPIRE_DATE)，将在&amp;nbsp;$DAYS_LEFT&amp;nbsp;天后过期&amp;nbsp;，无需更新。&amp;quot;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;exit&amp;nbsp;0
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;else
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;echo&amp;nbsp;&amp;quot;当前证书有效期($EXPIRE_DATE)，将在&amp;nbsp;$DAYS_LEFT&amp;nbsp;天后过期&amp;nbsp;，需要更新。&amp;quot;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;fi
fi

echo&amp;nbsp;&amp;quot;开始从远程&amp;nbsp;FTP&amp;nbsp;服务器下载新证书...&amp;quot;

#&amp;nbsp;通过&amp;nbsp;curl&amp;nbsp;从&amp;nbsp;FTP&amp;nbsp;服务器下载最新证书
echo&amp;nbsp;&amp;quot;正在下载证书...&amp;quot;
curl&amp;nbsp;$CURL_OPTS&amp;nbsp;&amp;quot;${FTP_URL}/${REMOTE_CERT_FILE}&amp;quot;&amp;nbsp;-o&amp;nbsp;&amp;quot;${LOCAL_CERT_FILE}.new&amp;quot;

echo&amp;nbsp;&amp;quot;正在下载私钥...&amp;quot;
curl&amp;nbsp;$CURL_OPTS&amp;nbsp;&amp;quot;${FTP_URL}/${REMOTE_KEY_FILE}&amp;quot;&amp;nbsp;-o&amp;nbsp;&amp;quot;${LOCAL_KEY_FILE}.new&amp;quot;

#&amp;nbsp;检查下载是否成功
if&amp;nbsp;[&amp;nbsp;-s&amp;nbsp;&amp;quot;${LOCAL_CERT_FILE}.new&amp;quot;&amp;nbsp;]&amp;nbsp;&amp;amp;&amp;amp;&amp;nbsp;[&amp;nbsp;-s&amp;nbsp;&amp;quot;${LOCAL_KEY_FILE}.new&amp;quot;&amp;nbsp;];&amp;nbsp;then
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;echo&amp;nbsp;&amp;quot;下载成功！正在替换旧证书...&amp;quot;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;#&amp;nbsp;备份旧证书
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;[&amp;nbsp;-f&amp;nbsp;&amp;quot;$LOCAL_CERT_FILE&amp;quot;&amp;nbsp;]&amp;nbsp;&amp;amp;&amp;amp;&amp;nbsp;mv&amp;nbsp;&amp;quot;$LOCAL_CERT_FILE&amp;quot;&amp;nbsp;&amp;quot;${LOCAL_CERT_FILE}.bak&amp;quot;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;[&amp;nbsp;-f&amp;nbsp;&amp;quot;$LOCAL_KEY_FILE&amp;quot;&amp;nbsp;]&amp;nbsp;&amp;amp;&amp;amp;&amp;nbsp;mv&amp;nbsp;&amp;quot;$LOCAL_KEY_FILE&amp;quot;&amp;nbsp;&amp;quot;${LOCAL_KEY_FILE}.bak&amp;quot;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;#&amp;nbsp;移动新证书到正式位置
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;mv&amp;nbsp;&amp;quot;${LOCAL_CERT_FILE}.new&amp;quot;&amp;nbsp;&amp;quot;$LOCAL_CERT_FILE&amp;quot;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;mv&amp;nbsp;&amp;quot;${LOCAL_KEY_FILE}.new&amp;quot;&amp;nbsp;&amp;quot;$LOCAL_KEY_FILE&amp;quot;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;#&amp;nbsp;设置正确的权限
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;chmod&amp;nbsp;644&amp;nbsp;&amp;quot;$LOCAL_CERT_FILE&amp;quot;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;chmod&amp;nbsp;600&amp;nbsp;&amp;quot;$LOCAL_KEY_FILE&amp;quot;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;#&amp;nbsp;重载&amp;nbsp;Nginx&amp;nbsp;使新证书生效
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;echo&amp;nbsp;&amp;quot;正在重载&amp;nbsp;Nginx&amp;nbsp;服务...&amp;quot;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if&amp;nbsp;eval&amp;nbsp;$NGINX_RELOAD_CMD;&amp;nbsp;then
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;echo&amp;nbsp;&amp;quot;SSL&amp;nbsp;证书更新并重载&amp;nbsp;Nginx&amp;nbsp;成功！&amp;quot;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;else
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;echo&amp;nbsp;&amp;quot;Nginx&amp;nbsp;重载失败，请检查配置文件！&amp;quot;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;fi
else
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;echo&amp;nbsp;&amp;quot;错误：从&amp;nbsp;FTP&amp;nbsp;下载证书失败或文件为空，更新终止。&amp;quot;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;rm&amp;nbsp;-f&amp;nbsp;&amp;quot;${LOCAL_CERT_FILE}.new&amp;quot;&amp;nbsp;&amp;quot;${LOCAL_KEY_FILE}.new&amp;quot;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;exit&amp;nbsp;1
fi

echo&amp;nbsp;&amp;quot;SSL&amp;nbsp;证书自动更新成功并已生效！&amp;quot;&lt;/pre&gt;&lt;p&gt;然后用 crontab -e 编辑计划任务，添加：&lt;/p&gt;&lt;pre class=&quot;prettyprint&quot;&gt;0&amp;nbsp;2&amp;nbsp;*&amp;nbsp;*&amp;nbsp;*&amp;nbsp;/usr/bin/bash&amp;nbsp;/etc/ssl/update-ssl.sh&lt;/pre&gt;&lt;p&gt;完成！就这么简单&lt;/p&gt;&lt;p&gt;在搞定了 Debian 服务器之后，我又开始研究威联通的 NAS，威联通的 QNAP 系统是深度定制的 Linux 系统，跟常见的 Ubuntu/Debian 系统区别很大。最大的区别，也是我踩过的最大的坑，就是证书的格式。&lt;/p&gt;&lt;p&gt;QNAP 虽然使用的是 Apache 作为 Web 服务器，但是证书管理却使用了 stunnel，配置文件如下：&lt;/p&gt;&lt;pre class=&quot;prettyprint&quot;&gt;SSLCertificateFile&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;quot;/etc/stunnel/stunnel.pem&amp;quot;&amp;nbsp;&amp;nbsp;#&amp;nbsp;这里面同时包含私钥和网站证书
SSLCertificateChainFile&amp;nbsp;&amp;quot;/etc/stunnel/uca.pem&amp;quot;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;#&amp;nbsp;中继证书&lt;/pre&gt;&lt;p&gt;从配置文件可以看出来，威联通使用的证书格式跟 Nginx 和 Apache 都不一样，私钥和网站证书合并成一个证书链，而中继证书却单独放。&lt;/p&gt;&lt;p&gt;经过几天时间的折腾，最后终于搞定了，/mnt/HDA_ROOT/.config/stunnel/update_ssl.sh：&lt;/p&gt;&lt;pre class=&quot;prettyprint&quot;&gt;#!/bin/bash
#&amp;nbsp;自动获取当前脚本所在的绝对路径
SCRIPT_DIR=$(cd&amp;nbsp;&amp;quot;$(dirname&amp;nbsp;&amp;quot;$0&amp;quot;)&amp;quot;;&amp;nbsp;pwd)

#&amp;nbsp;如果不是在终端运行（说明是&amp;nbsp;crontab&amp;nbsp;触发），则启用静默和错误日志模式
if&amp;nbsp;[&amp;nbsp;!&amp;nbsp;-t&amp;nbsp;1&amp;nbsp;];&amp;nbsp;then
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;exec&amp;nbsp;2&amp;gt;&amp;gt;&amp;nbsp;&amp;quot;${SCRIPT_DIR}/update_ssl_err.log&amp;quot;&amp;nbsp;&amp;gt;/dev/null
fi

#&amp;nbsp;====================&amp;nbsp;配置区域&amp;nbsp;====================
FTP_HOST=&amp;quot;ftp.your_host&amp;quot;
FTP_USER=&amp;quot;ftp_user&amp;quot;
FTP_PASS=&amp;quot;password&amp;quot;
FTP_DIR=&amp;quot;/ssl&amp;quot;
FTP_URL=&amp;quot;ftp://${FTP_HOST}${FTP_DIR}&amp;quot;
CURL_OPTS=&amp;quot;--ssl-reqd&amp;nbsp;-k&amp;nbsp;-sS&amp;nbsp;-u&amp;nbsp;${FTP_USER}:${FTP_PASS}&amp;quot;

REMOTE_CRT_FILE=&amp;quot;fairysoft.net-crt.pem&amp;quot;
REMOTE_KEY_FILE=&amp;quot;fairysoft.net-key.pem&amp;quot;
REMOTE_CHAIN_FILE=&amp;quot;fairysoft.net-chain-only.pem&amp;quot;

TMP_DIR=&amp;quot;/tmp/ssl_download&amp;quot;
STUNNEL_DIR=&amp;quot;/mnt/HDA_ROOT/.config/stunnel&amp;quot;
LOCAL_CERT_FILE=&amp;quot;${STUNNEL_DIR}/stunnel.pem&amp;quot;
LOCAL_UCA_FILE=&amp;quot;${STUNNEL_DIR}/uca.pem&amp;quot;
EXPIRE_DAYS=28
#&amp;nbsp;=================================================

#&amp;nbsp;检查本地证书文件是否存在
if&amp;nbsp;[&amp;nbsp;!&amp;nbsp;-f&amp;nbsp;&amp;quot;$LOCAL_CERT_FILE&amp;quot;&amp;nbsp;];&amp;nbsp;then
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;echo&amp;nbsp;&amp;quot;本地证书文件不存在，将直接下载新证书。&amp;quot;
else
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;#&amp;nbsp;获取当前证书的过期时间并计算剩余天数
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;EXPIRE_DATE=$(openssl&amp;nbsp;x509&amp;nbsp;-in&amp;nbsp;&amp;quot;$LOCAL_CERT_FILE&amp;quot;&amp;nbsp;-enddate&amp;nbsp;-noout&amp;nbsp;|&amp;nbsp;cut&amp;nbsp;-d=&amp;nbsp;-f2)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;EXPIRE_SECS=$(date&amp;nbsp;-d&amp;nbsp;&amp;quot;$EXPIRE_DATE&amp;quot;&amp;nbsp;+%s)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;CURRENT_SECS=$(date&amp;nbsp;+%s)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;DAYS_LEFT=$((&amp;nbsp;($EXPIRE_SECS&amp;nbsp;-&amp;nbsp;$CURRENT_SECS)&amp;nbsp;/&amp;nbsp;86400&amp;nbsp;))
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;EXPIRE_DATE=$(date&amp;nbsp;-d&amp;nbsp;&amp;quot;$EXPIRE_DATE&amp;quot;&amp;nbsp;&amp;#39;+%Y-%m-%d&amp;#39;)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;#&amp;nbsp;判断是否需要更新
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if&amp;nbsp;[&amp;nbsp;&amp;quot;$DAYS_LEFT&amp;quot;&amp;nbsp;-ge&amp;nbsp;&amp;quot;$EXPIRE_DAYS&amp;quot;&amp;nbsp;];&amp;nbsp;then
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;echo&amp;nbsp;&amp;quot;当前证书有效期($EXPIRE_DATE)，将在&amp;nbsp;$DAYS_LEFT&amp;nbsp;天后过期&amp;nbsp;，无需更新。&amp;quot;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;exit&amp;nbsp;0
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;else
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;echo&amp;nbsp;&amp;quot;当前证书有效期($EXPIRE_DATE)，将在&amp;nbsp;$DAYS_LEFT&amp;nbsp;天后过期&amp;nbsp;，需要更新。&amp;quot;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;fi
fi

echo&amp;nbsp;&amp;quot;开始从远程&amp;nbsp;FTP&amp;nbsp;服务器下载新证书...&amp;quot;

#&amp;nbsp;创建并清理临时下载目录
mkdir&amp;nbsp;-p&amp;nbsp;&amp;quot;$TMP_DIR&amp;quot;

#&amp;nbsp;通过&amp;nbsp;curl&amp;nbsp;从&amp;nbsp;FTP&amp;nbsp;服务器下载最新证书
echo&amp;nbsp;&amp;quot;正在下载&amp;nbsp;crt&amp;nbsp;文件...&amp;quot;
curl&amp;nbsp;$CURL_OPTS&amp;nbsp;&amp;quot;${FTP_URL}/${REMOTE_CRT_FILE}&amp;quot;&amp;nbsp;-o&amp;nbsp;&amp;quot;${TMP_DIR}/${REMOTE_CRT_FILE}&amp;quot;

echo&amp;nbsp;&amp;quot;正在下载&amp;nbsp;key&amp;nbsp;文件...&amp;quot;
curl&amp;nbsp;$CURL_OPTS&amp;nbsp;&amp;quot;${FTP_URL}/${REMOTE_KEY_FILE}&amp;quot;&amp;nbsp;-o&amp;nbsp;&amp;quot;${TMP_DIR}/${REMOTE_KEY_FILE}&amp;quot;

echo&amp;nbsp;&amp;quot;正在下载&amp;nbsp;chain&amp;nbsp;文件...&amp;quot;
curl&amp;nbsp;$CURL_OPTS&amp;nbsp;&amp;quot;${FTP_URL}/${REMOTE_CHAIN_FILE}&amp;quot;&amp;nbsp;-o&amp;nbsp;&amp;quot;${TMP_DIR}/${REMOTE_CHAIN_FILE}&amp;quot;

#&amp;nbsp;校验下载的文件大小，防止下载了空文件导致服务瘫痪
if&amp;nbsp;[&amp;nbsp;!&amp;nbsp;-s&amp;nbsp;&amp;quot;${TMP_DIR}/${REMOTE_CRT_FILE}&amp;quot;&amp;nbsp;]&amp;nbsp;||&amp;nbsp;[&amp;nbsp;!&amp;nbsp;-s&amp;nbsp;&amp;quot;${TMP_DIR}/${REMOTE_KEY_FILE}&amp;quot;&amp;nbsp;];&amp;nbsp;then
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;echo&amp;nbsp;&amp;quot;错误：从&amp;nbsp;FTP&amp;nbsp;下载证书失败或文件为空！更新终止。&amp;quot;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;rm&amp;nbsp;-rf&amp;nbsp;&amp;quot;$TMP_DIR&amp;quot;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;exit&amp;nbsp;1
fi

echo&amp;nbsp;&amp;quot;下载完成！开始更新旧证书...&amp;quot;
#&amp;nbsp;备份旧证书
[&amp;nbsp;-f&amp;nbsp;&amp;quot;$LOCAL_CERT_FILE&amp;quot;&amp;nbsp;]&amp;nbsp;&amp;amp;&amp;amp;&amp;nbsp;mv&amp;nbsp;&amp;quot;$LOCAL_CERT_FILE&amp;quot;&amp;nbsp;&amp;quot;${LOCAL_CERT_FILE}.bak&amp;quot;
[&amp;nbsp;-f&amp;nbsp;&amp;quot;$LOCAL_UCA_FILE&amp;quot;&amp;nbsp;]&amp;nbsp;&amp;amp;&amp;amp;&amp;nbsp;mv&amp;nbsp;&amp;quot;$LOCAL_UCA_FILE&amp;quot;&amp;nbsp;&amp;quot;${LOCAL_UCA_FILE}.bak&amp;quot;

#&amp;nbsp;合并私钥与域名证书到&amp;nbsp;stunnel.pem，私钥在前，证书在后&amp;nbsp;
cat&amp;nbsp;&amp;quot;${TMP_DIR}/${REMOTE_KEY_FILE}&amp;quot;&amp;nbsp;&amp;quot;${TMP_DIR}/${REMOTE_CRT_FILE}&amp;quot;&amp;nbsp;&amp;gt;&amp;nbsp;&amp;quot;${LOCAL_CERT_FILE}&amp;quot;

#&amp;nbsp;写入中间证书链
if&amp;nbsp;[&amp;nbsp;-s&amp;nbsp;&amp;quot;${TMP_DIR}/${REMOTE_CHAIN_FILE}&amp;quot;&amp;nbsp;];&amp;nbsp;then
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;cat&amp;nbsp;&amp;quot;${TMP_DIR}/${REMOTE_CHAIN_FILE}&amp;quot;&amp;nbsp;&amp;gt;&amp;nbsp;&amp;quot;${LOCAL_UCA_FILE}&amp;quot;
fi

#&amp;nbsp;覆盖两个&amp;nbsp;backup&amp;nbsp;文件
cat&amp;nbsp;&amp;quot;${TMP_DIR}/${REMOTE_CRT_FILE}&amp;quot;&amp;nbsp;&amp;gt;&amp;nbsp;&amp;quot;${STUNNEL_DIR}/backup.cert&amp;quot;
cat&amp;nbsp;&amp;quot;${TMP_DIR}/${REMOTE_KEY_FILE}&amp;quot;&amp;nbsp;&amp;gt;&amp;nbsp;&amp;quot;${STUNNEL_DIR}/backup.key&amp;quot;

#&amp;nbsp;chmod&amp;nbsp;600&amp;nbsp;&amp;quot;${LOCAL_CERT_FILE}&amp;quot;&amp;nbsp;&amp;quot;${LOCAL_UCA_FILE}&amp;quot;

#&amp;nbsp;清理临时文件
rm&amp;nbsp;-rf&amp;nbsp;&amp;quot;$TMP_DIR&amp;quot;

echo&amp;nbsp;&amp;quot;正在重启系统服务使证书生效...&amp;quot;

#&amp;nbsp;重启核心&amp;nbsp;stunnle、web&amp;nbsp;与反向代理服务
/etc/init.d/stunnel.sh&amp;nbsp;stop
sleep&amp;nbsp;3
/etc/init.d/Qthttpd.sh&amp;nbsp;restart&amp;nbsp;2&amp;gt;&amp;amp;1&amp;nbsp;|&amp;nbsp;grep&amp;nbsp;-v&amp;nbsp;&amp;quot;php_ext.ini&amp;quot;
sleep&amp;nbsp;3
/etc/init.d/thttpd.sh&amp;nbsp;restart
sleep&amp;nbsp;3
/etc/init.d/stunnel.sh&amp;nbsp;start
sleep&amp;nbsp;3
if&amp;nbsp;[&amp;nbsp;-f&amp;nbsp;&amp;quot;/etc/init.d/reverse_proxy.sh&amp;quot;&amp;nbsp;];&amp;nbsp;then
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;/etc/init.d/reverse_proxy.sh&amp;nbsp;reload&amp;nbsp;&amp;nbsp;2&amp;gt;&amp;amp;1&amp;nbsp;|&amp;nbsp;grep&amp;nbsp;-v&amp;nbsp;&amp;quot;AH00558&amp;quot;
fi

echo&amp;nbsp;&amp;quot;SSL&amp;nbsp;证书自动更新成功并已生效！&amp;quot;&lt;/pre&gt;&lt;p&gt;脚本跑通之后就是添加计划任务，威联通系统添加计划任务的方法跟其他 Linux 系统不一样，网上有很多教程，很容易就能搜到。需要先编辑 /etc/config/crontab，可以使用 vi 或者 nano，然后再执行指令：&lt;/p&gt;&lt;pre class=&quot;prettyprint&quot;&gt;crontab&amp;nbsp;/etc/config/crontab&amp;nbsp;&amp;amp;&amp;amp;&amp;nbsp;/etc/init.d/crond.sh&amp;nbsp;restart&lt;/pre&gt;&lt;p&gt;在搞定了威联通之后，我又把目光转到了我家里的一台 Home Assistant 服务器，HAOS 系统更加特殊，虽然底层是 Linux，但是默认并没有开启 SSH，需要先下载一个 SSH工具。&lt;/p&gt;&lt;p&gt;在 HA 插件商店里，搜索并安装 Advanced SSH &amp;amp; Web Terminal，开启&amp;nbsp;SSH，编写脚本测试，运行正常，/ssl/update_ssl.sh：&lt;/p&gt;&lt;pre class=&quot;prettyprint&quot;&gt;#!/bin/bash
#&amp;nbsp;自动获取当前脚本所在的绝对路径
SCRIPT_DIR=$(cd&amp;nbsp;&amp;quot;$(dirname&amp;nbsp;&amp;quot;$0&amp;quot;)&amp;quot;;&amp;nbsp;pwd)

#&amp;nbsp;如果不是在终端运行（说明是&amp;nbsp;crontab&amp;nbsp;触发），则启用静默和错误日志模式
if&amp;nbsp;[&amp;nbsp;!&amp;nbsp;-t&amp;nbsp;1&amp;nbsp;];&amp;nbsp;then
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;exec&amp;nbsp;2&amp;gt;&amp;gt;&amp;nbsp;&amp;quot;${SCRIPT_DIR}/update_ssl_err.log&amp;quot;&amp;nbsp;&amp;gt;/dev/null
fi
#&amp;nbsp;====================&amp;nbsp;HAOS&amp;nbsp;专用配置区域&amp;nbsp;====================
FTP_HOST=&amp;quot;ftp.your_host&amp;quot;
FTP_USER=&amp;quot;ftp_user&amp;quot;
FTP_PASS=&amp;quot;password&amp;quot;
FTP_DIR=&amp;quot;/ssl&amp;quot;
FTP_URL=&amp;quot;ftp://${FTP_HOST}${FTP_DIR}&amp;quot;
CURL_OPTS=&amp;quot;--ssl-reqd&amp;nbsp;-k&amp;nbsp;-sS&amp;nbsp;-u&amp;nbsp;${FTP_USER}:${FTP_PASS}&amp;quot;

REMOTE_CERT_FILE=&amp;quot;fairysoft.net-chain.pem&amp;quot;&amp;nbsp;
REMOTE_KEY_FILE=&amp;quot;fairysoft.net-key.pem&amp;quot;&amp;nbsp;&amp;nbsp;

LOCAL_CERT_FILE=&amp;quot;/ssl/fairysoft.net-chain.pem&amp;quot;
LOCAL_KEY_FILE=&amp;quot;/ssl/fairysoft.net-key.pem&amp;quot;

NGINX_RELOAD_CMD=&amp;quot;ha&amp;nbsp;apps&amp;nbsp;restart&amp;nbsp;core_nginx_proxy&amp;quot;
EXPIRE_DAYS=28
#&amp;nbsp;=========================================================


#&amp;nbsp;检查本地证书文件是否存在
if&amp;nbsp;[&amp;nbsp;!&amp;nbsp;-f&amp;nbsp;&amp;quot;$LOCAL_CERT_FILE&amp;quot;&amp;nbsp;];&amp;nbsp;then
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;echo&amp;nbsp;&amp;quot;本地证书文件不存在，将直接下载新证书。&amp;quot;
else
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;#&amp;nbsp;获取&amp;nbsp;OpenSSL&amp;nbsp;的原始英文时间
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;RAW_DATE=$(openssl&amp;nbsp;x509&amp;nbsp;-in&amp;nbsp;&amp;quot;$LOCAL_CERT_FILE&amp;quot;&amp;nbsp;-enddate&amp;nbsp;-noout&amp;nbsp;|&amp;nbsp;cut&amp;nbsp;-d=&amp;nbsp;-f2)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;#&amp;nbsp;示例格式:&amp;nbsp;May&amp;nbsp;30&amp;nbsp;23:59:59&amp;nbsp;2026&amp;nbsp;GMT
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;#&amp;nbsp;针对&amp;nbsp;HAOS&amp;nbsp;(Alpine)&amp;nbsp;环境，用&amp;nbsp;awk&amp;nbsp;将英文月份转换为纯数字格式&amp;nbsp;(YYYY-MM-DD&amp;nbsp;HH:MM:SS)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;CLEAN_DATE=$(echo&amp;nbsp;&amp;quot;$RAW_DATE&amp;quot;&amp;nbsp;|&amp;nbsp;awk&amp;nbsp;&amp;#39;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;BEGIN&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;split(&amp;quot;Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec&amp;quot;,&amp;nbsp;months,&amp;nbsp;&amp;quot;|&amp;quot;);
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;for&amp;nbsp;(i=1;&amp;nbsp;i&amp;lt;=12;&amp;nbsp;i++)&amp;nbsp;m_num[months[i]]&amp;nbsp;=&amp;nbsp;sprintf(&amp;quot;%02d&amp;quot;,&amp;nbsp;i);
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;print&amp;nbsp;$4&amp;nbsp;&amp;quot;-&amp;quot;&amp;nbsp;m_num[$1]&amp;nbsp;&amp;quot;-&amp;quot;&amp;nbsp;sprintf(&amp;quot;%02d&amp;quot;,&amp;nbsp;$2)&amp;nbsp;&amp;quot;&amp;nbsp;&amp;quot;&amp;nbsp;$3
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&amp;#39;)

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;EXPIRE_DATE=$(date&amp;nbsp;-d&amp;nbsp;&amp;quot;$CLEAN_DATE&amp;quot;&amp;nbsp;&amp;#39;+%Y-%m-%d&amp;#39;)

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;#&amp;nbsp;计算具体的剩余天数&amp;nbsp;$DAYS_LEFT
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;EXPIRE_SECS=$(date&amp;nbsp;-d&amp;nbsp;&amp;quot;$CLEAN_DATE&amp;quot;&amp;nbsp;+%s)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;CURRENT_SECS=$(date&amp;nbsp;+%s)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;DAYS_LEFT=$((&amp;nbsp;($EXPIRE_SECS&amp;nbsp;-&amp;nbsp;$CURRENT_SECS)&amp;nbsp;/&amp;nbsp;86400&amp;nbsp;))

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;#&amp;nbsp;检查证书是否在指定的&amp;nbsp;$EXPIRE_DAYS&amp;nbsp;天数内过期
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;CHECK_SECS=$((&amp;nbsp;EXPIRE_DAYS&amp;nbsp;*&amp;nbsp;86400&amp;nbsp;))
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if&amp;nbsp;openssl&amp;nbsp;x509&amp;nbsp;-in&amp;nbsp;&amp;quot;$LOCAL_CERT_FILE&amp;quot;&amp;nbsp;-checkend&amp;nbsp;$CHECK_SECS&amp;nbsp;&amp;gt;&amp;nbsp;/dev/null;&amp;nbsp;then
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;echo&amp;nbsp;&amp;quot;当前证书有效期($EXPIRE_DATE)，将在&amp;nbsp;$DAYS_LEFT&amp;nbsp;天后过期&amp;nbsp;，无需更新。&amp;quot;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;exit&amp;nbsp;0
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;else
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;echo&amp;nbsp;&amp;quot;当前证书有效期($EXPIRE_DATE)，将在&amp;nbsp;$DAYS_LEFT&amp;nbsp;天后过期&amp;nbsp;，需要更新。&amp;quot;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;fi
fi

echo&amp;nbsp;&amp;quot;开始从远程&amp;nbsp;FTP&amp;nbsp;服务器下载新证书...&amp;quot;

#&amp;nbsp;通过&amp;nbsp;curl&amp;nbsp;从&amp;nbsp;FTP&amp;nbsp;服务器下载最新证书
echo&amp;nbsp;&amp;quot;正在下载证书...&amp;quot;
curl&amp;nbsp;$CURL_OPTS&amp;nbsp;&amp;quot;${FTP_URL}/${REMOTE_CERT_FILE}&amp;quot;&amp;nbsp;-o&amp;nbsp;&amp;quot;${LOCAL_CERT_FILE}.new&amp;quot;

echo&amp;nbsp;&amp;quot;正在下载私钥...&amp;quot;
curl&amp;nbsp;$CURL_OPTS&amp;nbsp;&amp;quot;${FTP_URL}/${REMOTE_KEY_FILE}&amp;quot;&amp;nbsp;-o&amp;nbsp;&amp;quot;${LOCAL_KEY_FILE}.new&amp;quot;

#&amp;nbsp;检查下载是否成功
if&amp;nbsp;[&amp;nbsp;-s&amp;nbsp;&amp;quot;${LOCAL_CERT_FILE}.new&amp;quot;&amp;nbsp;]&amp;nbsp;&amp;amp;&amp;amp;&amp;nbsp;[&amp;nbsp;-s&amp;nbsp;&amp;quot;${LOCAL_KEY_FILE}.new&amp;quot;&amp;nbsp;]&amp;nbsp;&amp;amp;&amp;amp;&amp;nbsp;!&amp;nbsp;grep&amp;nbsp;-q&amp;nbsp;-E&amp;nbsp;&amp;quot;html|Error&amp;quot;&amp;nbsp;&amp;quot;${LOCAL_CERT_FILE}.new&amp;quot;;&amp;nbsp;then
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;echo&amp;nbsp;&amp;quot;下载成功！正在替换旧证书...&amp;quot;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;#&amp;nbsp;备份旧证书
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;[&amp;nbsp;-f&amp;nbsp;&amp;quot;$LOCAL_CERT_FILE&amp;quot;&amp;nbsp;]&amp;nbsp;&amp;amp;&amp;amp;&amp;nbsp;mv&amp;nbsp;&amp;quot;$LOCAL_CERT_FILE&amp;quot;&amp;nbsp;&amp;quot;${LOCAL_CERT_FILE}.bak&amp;quot;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;[&amp;nbsp;-f&amp;nbsp;&amp;quot;$LOCAL_KEY_FILE&amp;quot;&amp;nbsp;]&amp;nbsp;&amp;amp;&amp;amp;&amp;nbsp;mv&amp;nbsp;&amp;quot;$LOCAL_KEY_FILE&amp;quot;&amp;nbsp;&amp;quot;${LOCAL_KEY_FILE}.bak&amp;quot;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;#&amp;nbsp;移动新证书到正式位置
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;mv&amp;nbsp;&amp;quot;${LOCAL_CERT_FILE}.new&amp;quot;&amp;nbsp;&amp;quot;$LOCAL_CERT_FILE&amp;quot;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;mv&amp;nbsp;&amp;quot;${LOCAL_KEY_FILE}.new&amp;quot;&amp;nbsp;&amp;quot;$LOCAL_KEY_FILE&amp;quot;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;#&amp;nbsp;设置正确的权限
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;chmod&amp;nbsp;644&amp;nbsp;&amp;quot;$LOCAL_CERT_FILE&amp;quot;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;chmod&amp;nbsp;600&amp;nbsp;&amp;quot;$LOCAL_KEY_FILE&amp;quot;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;#&amp;nbsp;重载&amp;nbsp;Nginx
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;echo&amp;nbsp;&amp;quot;正在重载&amp;nbsp;Nginx&amp;nbsp;服务...&amp;quot;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if&amp;nbsp;eval&amp;nbsp;$NGINX_RELOAD_CMD;&amp;nbsp;then
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;echo&amp;nbsp;&amp;quot;SSL&amp;nbsp;证书更新并重启&amp;nbsp;Nginx&amp;nbsp;成功！&amp;quot;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;else
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;echo&amp;nbsp;&amp;quot;Nginx&amp;nbsp;重启失败，请检查命令行！&amp;quot;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;fi
else
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;echo&amp;nbsp;&amp;quot;错误：从&amp;nbsp;FTP&amp;nbsp;下载证书失败，更新终止。&amp;quot;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;rm&amp;nbsp;-f&amp;nbsp;&amp;quot;${LOCAL_CERT_FILE}.new&amp;quot;&amp;nbsp;&amp;quot;${LOCAL_KEY_FILE}.new&amp;quot;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;exit&amp;nbsp;1
fi

echo&amp;nbsp;&amp;quot;SSL&amp;nbsp;证书自动更新成功并已生效！&amp;quot;&lt;/pre&gt;&lt;p&gt;脚本跑通之后，下一步就是添加计划任务了，但这时候问题来了，计划任务却怎么都跑不起来。&lt;/p&gt;&lt;p&gt;问 AI，告诉我说 HAOS 不支持计划任务，让我使用系统自带的自动化功能配合 Shell Command 来实现，在 AI 的引导下，一顿操作猛如虎，折腾了一上午，脚本硬是没有执行起来。&lt;/p&gt;&lt;p&gt;只好自己想办法排查问题，经过一番排查，发现 Shell Command 根本没有 /ssl 目录的写入权限，然后我改目录，证书倒是能下载了，最后却又没有 ha apps restart core_nginx_proxy&amp;nbsp;的执行权限。&lt;/p&gt;&lt;p&gt;被一堆权限问题搞得焦头烂额之后，我只好换了一家 AI，请出 Gemini 大哥出场，他让我直接在&amp;nbsp;Advanced SSH &amp;amp; Web Terminal 的配置页面里编辑YAML：&lt;/p&gt;&lt;pre class=&quot;prettyprint&quot;&gt;init_commands:
&amp;nbsp;&amp;nbsp;-&amp;nbsp;echo&amp;nbsp;&amp;quot;00&amp;nbsp;03&amp;nbsp;*&amp;nbsp;*&amp;nbsp;*&amp;nbsp;/bin/bash&amp;nbsp;/ssl/update_ssl.sh&amp;quot;&amp;nbsp;&amp;gt;&amp;nbsp;/etc/crontabs/root
&amp;nbsp;&amp;nbsp;-&amp;nbsp;echo&amp;nbsp;&amp;quot;&amp;quot;&amp;nbsp;&amp;gt;&amp;gt;&amp;nbsp;/etc/crontabs/root
&amp;nbsp;&amp;nbsp;-&amp;nbsp;chmod&amp;nbsp;600&amp;nbsp;/etc/crontabs/root
&amp;nbsp;&amp;nbsp;-&amp;nbsp;crond&lt;/pre&gt;&lt;p&gt;改完之后点保存，系统会自动重启 Advanced SSH &amp;amp; Web Terminal，测试正常，还得是Gemini。&lt;/p&gt;</description><pubDate>Thu, 21 May 2026 13:18:00 +0800</pubDate></item><item><title>腾讯云 SSL 证书文件格式踩坑笔记</title><link>https://blog.fairysoft.net/post/67.html</link><description>&lt;p&gt;最近在给网站部署 SSL 证书，由于我的设备跨越了 Windows Server、Debian、QNAP 等多个平台，所以证书格式也要用到很多种，以下是我经过几天时间踩坑之后总结出来的干货。&lt;/p&gt;

&lt;p&gt;先说证书，IIS 的就不说了，只说说 Nginx 和 Apache 证书格式的区别，以及威联通 QNAP 系统独特的证书格式。&lt;/p&gt;

&lt;p&gt;腾讯云的 Nginx 证书包解压之后有四个文件，文件名分别为：&lt;/p&gt;

&lt;pre class=&quot;prettyprint&quot;&gt;
fairysoft.net_bundle.crt 网站证书跟中继证书合并的证书链
fairysoft.net_bundle.crt 这个文件跟上面那个文件内容完全一样，只是扩展名不同
fairysoft.net.csr        申请证书时生成的请求文件，部署时用不到，可删
fairysoft.net.key        私钥文件&lt;/pre&gt;

&lt;p&gt;腾讯云的 Apache 证书包解压之后也有四个文件，文件名分别是：&lt;/p&gt;

&lt;pre class=&quot;prettyprint&quot;&gt;
fairsoft.net.crt 网站证书
fairsoft.net.key 私钥
fairsoft.net.csr 申请证书时生成的请求文件，部署时用不到，可删
root_bundle.crt  中继证书&lt;/pre&gt;

&lt;p&gt;Nginx 的配置文件如下，需要指定两个文件，一个是网站证书跟中继证书合并成的证书链，另外一个是单独的私钥：&lt;/p&gt;

&lt;pre class=&quot;prettyprint&quot;&gt;
ssl_certificate     /etc/ssl/fairysoft.net_bundle.pem;
ssl_certificate_key /etc/ssl/fairysoft.net.key;&lt;/pre&gt;

&lt;p&gt;旧版 Apache（2.4.8 之前）配置文件中需要指定三个文件：&lt;/p&gt;

&lt;pre class=&quot;prettyprint&quot;&gt;
SSLCertificateFile      /etc/ssl/fairsoft.net.crt
SSLCertificateChainFile /etc/ssl/root_bundle.crt
SSLCertificateKeyFile   /etc/ssl/fairsoft.net.key&lt;/pre&gt;

&lt;p&gt;新版 Apache（2.4.8 及以后）配置文件中废弃了 SSLCertificateChainFile，强制要求把域名证书跟中继证书顺序拼接成一个证书链，私钥依旧单独文件，格式跟 Nginx 一致了。&lt;/p&gt;

&lt;pre class=&quot;prettyprint&quot;&gt;
SSLCertificateFile    /etc/ssl/fairysoft.net_bundle.pem
SSLCertificateKeyFile /etc/ssl/fairsoft.net.key&lt;/pre&gt;

&lt;p&gt;&lt;br /&gt;
总结一下，腾讯云 Apache 证书包是拆分文件：域名证书.crt + 中继.crt + 私钥.key，旧版专用，新版不能直接用。新版 Apache 2.4.8+ 如果要用旧版的 Apache 证书包，必须手动将：域名证书.crt + 中继.crt &amp;rarr; 合并成证书链文件，和 Nginx 格式一致，其实直接下载 Nginx 的证书包就可以直接用了，不需要手动合并。&lt;/p&gt;

&lt;p&gt;威联通 QNAP 系统则有些特殊，QNAP 虽然使用的是 Apache 作为 Web 服务器，但是证书管理却使用了&amp;nbsp;stunnel，配置文件如下：&lt;/p&gt;

&lt;pre class=&quot;prettyprint&quot;&gt;
SSLCertificateFile      &amp;quot;/etc/stunnel/stunnel.pem&amp;quot;  # 这里面同时包含私钥和网站证书
SSLCertificateChainFile &amp;quot;/etc/stunnel/uca.pem&amp;quot;      # 中继证书&lt;/pre&gt;

&lt;p&gt;从配置文件可以看出来，威联通使用的证书格式跟 Nginx 和 Apache 都不一样，私钥和网站证书合并成一个证书链，而中继证书却单独放。&lt;/p&gt;

&lt;p&gt;最近我还在 Windows Server 服务器上部署了 Win‑acme ，下载的证书文件格式如下：&lt;/p&gt;

&lt;pre class=&quot;prettyprint&quot;&gt;
fairsoft.net‑key.pem        私钥文件
fairsoft.net‑crt.pem        网站证书
fairsoft.net‑chain‑only.pem 仅中继证书
fairsoft.net‑chain.pem      网站证书和中级证书合并的证书链
&lt;/pre&gt;

&lt;p&gt;对比一下就会发现 Win‑acme 下载的证书跟 Nginx 格式的证书包完全互通，可直接通用于 Nginx 和新版 Apache。&lt;/p&gt;

&lt;p&gt;下一期我来讲一下如何在各种 Linux 系统上部署脚本，自动更新快过期的证书。&lt;/p&gt;

&lt;p&gt;&amp;nbsp;&lt;/p&gt;
</description><pubDate>Wed, 20 May 2026 23:15:53 +0800</pubDate></item><item><title>当飞机遇到GPS干扰</title><link>https://blog.fairysoft.net/post/66.html</link><description>&lt;p&gt;两年前我就想写一篇关于飞机GPS干扰的文章，一直没找到机会写，直到昨天我自己遭遇了一次严重的GPS干扰事件。&lt;/p&gt;

&lt;p&gt;昨天飞晚班回重庆，着陆前10分钟，在我们飞过沙坪坝上空时，飞机弹出一个&amp;ldquo;ADS-B故障&amp;rdquo;的信息，我们明白这是遇到了GPS干扰。但这时候我们还以为这只是一次普通的干扰，对这种由于GPS干扰导致的ADS-B故障我们最近几年经常遇到，早就习以为常了，一般这种干扰只会持续几秒钟就会消失，因为通常干扰范围是局部的，以我们飞机的速度很快就能脱离这个干扰区域，而且就算是持续受到干扰，对我们飞机影响也不大，因为我们飞机有三套高精度的惯性导航系统，它们可以完全不依赖外部的GPS信号来工作。&lt;/p&gt;

&lt;p&gt;可是随后我在无线电频率里听到一架在我前面的山东航空的飞机报告管制员说他们遭遇GPS干扰中止进近拉升了，这时候我才意识到这次的干扰可能跟之前不一样，于是我们开始评估风险，我们重点检查了飞机的导航系统，确认飞机的位置没有出现偏移，但我们还是通告了管制员要求帮我们通过地面雷达监控我们飞机的位置和高度，因为空管的一次雷达是使用无线电波反射原理来主动测量飞机的位置，而不依赖飞机的位置报告系统。&lt;/p&gt;

&lt;p&gt;没过多久，飞机又弹出一个&amp;ldquo;FMS/GPS位置不一致&amp;rdquo;的信息，这个信息意味着GPS受到的不是普通的干扰&amp;mdash;&amp;mdash;普通的干扰属于&amp;ldquo;压制式信号干扰&amp;rdquo;，只是用一个杂乱无章的无线电噪声淹没掉正常的GPS卫星信号，使得GPS接收机无法从这一堆噪声中提取出有用的信息，也就无法计算出经纬度位置数据。这种情况下，GPS只是暂时罢工但并不会添乱，失去了GPS定位，飞机的位置精度会略微下降一点点，但还是能满足除了RNP进近之外的大多数飞行程序的要求。但是这个&amp;ldquo;FMS/GPS位置不一致&amp;rdquo;的告警信息意味着GPS接收机接收到了一组精心伪造的无线电信号并解码计算出了一个错误的经纬度位置信息，这种升级版的干扰被称为&amp;ldquo;GPS欺骗&amp;rdquo;，这种情况下，GPS不再是罢工这么简单了，它会直接给你添乱。虽然GPS欺骗很难对飞机的导航系统产生严重的影响，因为飞机的FMGS计算机会识别出明显不合理的GPS位置信息并将其舍弃掉，但是错误的位置信息会对飞机的增强型近地警告系统（EGPWS）造成极大的影响，因为EGPWS系统的位置信号是直接来源于GPS接收机而不是FMGS计算机，EGPWS系统在发出告警之前并不会对GPS接收机提供的位置信息做有效性校验。&lt;/p&gt;

&lt;p&gt;果然这个信息弹出来没多久，驾驶舱里PULL UP警告就开始响了起来，当时我的内心也是慌得一批，倒不是担心会撞山&amp;mdash;&amp;mdash;因为我很清楚我飞机当时的位置根本没有任何撞山的可能。我在重庆飞了近20年，这条航路我太熟悉了：当时我们正在左转五边准备建立盲降，我左前方是鹅公岩长江大桥，江对岸是南岸区，虽然正前方是南山，但是南山海拔最高的地方也只有600米，而我们飞机当时的高度在1500米以上。&lt;/p&gt;

&lt;p&gt;那我慌啥呢？非业内人士可能不知道&amp;ldquo;PULL UP&amp;rdquo;警告有多严重，可以这么说，这个警告是飞机上最严重的警告，没有之一！99%的飞行员一辈子都不会在真飞机上听到这个警告，如果你触发了这个警告，在现在的处罚制度下，基本上就可以宣告你的职业生涯终结了。不过好在这是个假警告，所以我们三个人的饭碗暂时是保住了，算是虚惊一场。随后的重点是如何决策，当时我们有两个选择，一是直接中止进近拉升，二是忽略警告继续进近。&lt;/p&gt;

&lt;p&gt;原则上讲，PULL UP警告是最高优先级、立即动作级警告，根本没时间给你去思考判断和决策，你要做的只能是立即执行地形避让的记忆项目：迅速前推油门杆到底同时断开自动驾驶并向后拉杆到底。这个原则曾经是不容置疑的，关键时刻能保命。但问题是在GPS干扰越来越频发的当今，这种地形避让操作带来的衍生风险也不容忽视：首先，迅速带杆到底的操作对飞机的飞控系统是一个很大的挑战，虽说空客的正常法则会保护飞机不进入失速，但是飞行数据上必然会出现很多超限，每一个超限都够飞行员喝一壶了，如果造成旅客受伤你更是吃不了兜着走了；其次，警告持续响你就得持续爬升，势必会导致穿越多个高度层，在繁忙的机场很容易跟空中其他飞机造成飞行冲突，任何一个冲突都可能会造成事故征候级别的后果，何况受到干扰的飞机不止你一架，大家全都满油门大角度爬升，估计空管都要疯掉了。所以空客公司在最近的一个&amp;ldquo;关于应对虚假TAWS告警&amp;rdquo;的技术通告里面很委婉的提示飞行员，首先，如果在安全高度之上&amp;ldquo;不应当机械的执行TAWS记忆项目&amp;rdquo;；其次，在明确GPS受到干扰并执行完地形意识简令之后可以考虑关闭飞机的TERR告警功能。不过，可能是为了免责，空客在通告中还是强调触发TAWS告警之后处置原则依然是立即执行地形避让的记忆项目。&lt;/p&gt;

&lt;p&gt;在犹豫了0.01秒之后我选择了违反原则，忽略警告并抑制TERR告警功能，继续进近。虽然从结果来看我的选择是对的，如果我选择拉升避让，可能后果会非常严重。但是直到现在我一直在反复思考这个问题：飞行员一直以来被要求原则上应当无条件服从机器的告警指示，是因为我们一直以来都认为机器比人更可靠，人会犯错，机器不会，但是当这个机器明确被证实存在某个Bug，在某些情况下会给出错误的指示，当这种情况发生时，我们是否还应该遵循之前的原则。&lt;/p&gt;

&lt;p&gt;这个问题就不多写了，大家有兴趣可以讨论一下。最后留点科普时间给大家粗略的讲解一下飞机FMGS计算位置的方法。&lt;/p&gt;

&lt;p&gt;首先讲一下惯性导航系统（INS），惯导是一种完全自主的导航设备，它不依赖任何外部信号，仅通过飞机自身，就能实时计算出自身的位置、速度、姿态信息。惯导的核心部件有两个，一个是激光陀螺：负责测量飞机的俯仰、倾斜、转弯角度等，跟位置计算没啥关系，这里就不细说了。另一个核心部件是加速度计：负责测量飞机在空间坐标系中xyz三个方向的加速度。根据牛顿运动定律，加速度对时间积分得到速度，速度对时间积分得到位移，初始位置加位移就是飞机的当前位置。惯导的短时精度非常高，但惯导有一个无法避免的缺陷&amp;mdash;&amp;mdash;累计误差。因为积分运算会不断累积测量过程中的微小偏差，随着飞行时间延长、飞行距离增加，这些误差会一点点累积，导致位置精度逐渐下降。飞行时间越久，惯导算出的位置与真实位置偏差就越大，单纯依靠惯导，无法满足长途飞行的高精度定位需求。&lt;/p&gt;

&lt;p&gt;再说一下GPS定位，GPS是我们最熟悉的定位方式，飞机通过接收卫星发射的信号，实时测算与多颗卫星的距离，通过计算得出自身的位置。它的优势十分突出：定位精度高，且误差不会随飞行时间累积，无论飞多久，只要能稳定接收卫星信号，就能保持较高的位置准确性。但GPS的短板也很明显：信号极易受干扰。就像此前提到的GPS干扰问题，一旦GPS失效，飞机就会失去这一精准定位来源。&lt;/p&gt;

&lt;p&gt;飞机上的FMGS计算机融合惯导与GPS各自的优点，将二者数据融合，取长补短，打造出稳定又精准的定位最优解。工作原理简单来说就是利用惯导短时精度高的优点来对接收到的GPS位置进行校验，采纳可信GPS位置，舍弃不可信GPS位置，不断修正惯导的累积位置偏差。&lt;/p&gt;

&lt;p&gt;所以大家大可不必担心GPS干扰甚至GPS欺骗会对飞机FMGS的定位导航造成严重的影响，就算是受到严重的GPS干扰，飞机位置也不会突然发生剧烈的变化，最多不过是随着时间的累积，慢慢的发生微小漂移。再说了，失去GPS辅助以后FMGS还会降级到无线电位置更新，利用地面导航台的方位距离信息来计算自身的位置，并修正惯导的漂移。就算是无线电位置更新也不可用，惯导仅依靠自身的精度也能维持一段时间的导航精度。这就是为什么没有GPS的飞机如果执行RNAV10的跨洋航线会限制纯惯导的飞行时间不能大于6小时，因为据说惯导漂移的适航认证要求每小时漂移距离不得大于2海里，纯惯导飞行6小时基本能满足RNAV10的要求。实际飞行时我观察过惯导的漂移量，一般每小时只有零点几海里的误差，远低于适航认证的上限。RNP AR(0.3)进离港的GPS空洞期最大允许5分钟也跟这个有关，其逻辑就是在GPS信号丢失之后，惯导仅靠自身的精度能保证在5分钟之内位置偏差不会超过0.3海里。&lt;/p&gt;

&lt;p&gt;最后再说一下受到GPS干扰之后，如果两部GPS都失效的情况下到底能不能使用RNAV进离港程序的问题。我查阅了RNAV1的设计规范，发现设计规范并不强制机载设备必须具备GNSS，可使用 GNSS、DME/DME 或DME/DME/IRU中的任意一种，只有RNP进近和RNP AR进近才强制必须具备GNSS，普通RNP进近要求1部GPS，RNP AR要求两部GPS。但是实际运行中我发现国内几乎所有机场的RNAV进离港程序都要求GNSS，所以在这些机场运行时，一旦GPS失效，就只能申请飞传统进离港程序。不过也有例外，比如成都双流机场的进离港程序就没有要求必须要有GNSS，理论上，双GPS失效的情况下只要DME/DME/IRU可用，在成都双流机场一样能飞RNAV进离港程序，只不过最后进近只能接盲降或者VOR程序，不能飞RNP进近。&lt;/p&gt;
</description><pubDate>Wed, 08 Apr 2026 22:23:17 +0800</pubDate></item><item><title>飞行员专用座舱高度监控报警器</title><link>https://blog.fairysoft.net/post/65.html</link><description>&lt;p&gt;&lt;img alt=&quot;&quot; src=&quot;https://www.fairysoft.net/zb_users/upload/2024/12/20241208110662916291.jpg&quot; style=&quot;height:270px; width:360px&quot; /&gt;&lt;/p&gt;

&lt;p&gt;空客A319飞机在飞高原的时候，座舱压力控制计算机有一个BUG，偶尔小概率会触发一个假警告，空客公司认为只是个假警告而已，不理他就行了，所以一直不去修复。但CAAC太卷，领导说了哪怕是假警告也坚决不能响，所以制定了一套补充程序要求机组在座舱高度达到8500ft时执行，以避免这个假警告。但是机组有时候事太多，经常忘了去做这个程序，如果运气不好碰巧这个警告响了的话，回来就会被收拾。所以我做了这个物理外挂，在需要执行程序的时候来提醒机组。使用ESP32处理器，MS5611高精度气压传感器，1.3寸低功耗OLED显示屏，三个物理按键，内置500mAh电池。&lt;/p&gt;

&lt;p&gt;以下是产品简要说明：&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;简介：&lt;/strong&gt;&lt;/p&gt;

&lt;p style=&quot;margin-left:40px&quot;&gt;一个带有高度报警和升降率报警功能的气压高度表。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;基本操作：&lt;/strong&gt;&lt;/p&gt;

&lt;p style=&quot;margin-left:21pt&quot;&gt;1、&amp;nbsp;在主界面下，左键开关显示屏，关屏状态下高度提醒功能仍然在后台工作。&lt;/p&gt;

&lt;p style=&quot;margin-left:21pt&quot;&gt;2、&amp;nbsp;主界面下短按中键进入设置模式，设置气压基准和高度提醒值。&lt;/p&gt;

&lt;p style=&quot;margin-left:21pt&quot;&gt;3、&amp;nbsp;主界面下长按中键设置升降率报警值。&lt;/p&gt;

&lt;p style=&quot;margin-left:21pt&quot;&gt;4、&amp;nbsp;主界面下短按右键切换公英制。&lt;/p&gt;

&lt;p style=&quot;margin-left:21pt&quot;&gt;5、&amp;nbsp;主界面下长按右键将气压基准恢复为1013hPa，高度提醒值恢复为8300ft。&lt;/p&gt;

&lt;p style=&quot;margin-left:21pt&quot;&gt;6、&amp;nbsp;设置模式下，左右键操作数值的减和加。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;四种提醒：&lt;/strong&gt;&lt;/p&gt;

&lt;p style=&quot;margin-left:21pt&quot;&gt;1、&amp;nbsp;高度超过8300ft，提醒CPC转换，持续滴滴约20秒，按任意键停止。&lt;/p&gt;

&lt;p style=&quot;margin-left:21pt&quot;&gt;2、&amp;nbsp;巡航时首次升降率超过500ft/m，滴滴两声，提醒主控CPC已进入下降模式。&lt;/p&gt;

&lt;p style=&quot;margin-left:21pt&quot;&gt;3、&amp;nbsp;座舱高度超过13000ft，滴滴5声，提醒检查高高度电门是否接通。&lt;/p&gt;

&lt;p style=&quot;margin-left:21pt&quot;&gt;4、&amp;nbsp;升降率超过设定值提醒，只要升降率超过设定值就一直滴滴，无法通过按键停止。本提醒只在亮屏模式下才工作，因为需要高采样频率的支持。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;自动熄屏：&lt;/strong&gt;&lt;/p&gt;

&lt;p style=&quot;margin-left:40px&quot;&gt;正常开机后默认5分钟无按键操作自动熄屏进入待机模式。&lt;/p&gt;

&lt;p style=&quot;margin-left:40px&quot;&gt;按住右键再打开电源开关则不启用自动熄屏功能。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;气压传感器校准：&lt;/strong&gt;&lt;/p&gt;

&lt;p style=&quot;margin-left:40px&quot;&gt;按住左键再打开电源开关进入气压传感器校准页面，左右键调整校准值，中键保存。&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&amp;nbsp;&lt;/p&gt;

&lt;p&gt;记录一下开发过程：&lt;/p&gt;

&lt;p&gt;突然有一天有了这个想法，于是画了这张手稿&lt;/p&gt;

&lt;p&gt;&lt;img alt=&quot;&quot; src=&quot;https://www.fairysoft.net/zb_users/upload/2024/12/2024120810420196196.jpg&quot; style=&quot;height:640px; width:360px&quot; /&gt;&lt;/p&gt;

&lt;p&gt;说干就干，用ESP32开发板搭建试验平台&lt;/p&gt;

&lt;p&gt;&lt;img alt=&quot;&quot; src=&quot;https://www.fairysoft.net/zb_users/upload/2024/12/20241208104379977997.jpg&quot; style=&quot;height:270px; opacity:0.9; width:360px&quot; /&gt;&lt;/p&gt;

&lt;p&gt;学习单片机编程，第一步当然是写&amp;ldquo;Hello World！&amp;rdquo;&lt;/p&gt;

&lt;p&gt;&lt;img alt=&quot;&quot; src=&quot;https://www.fairysoft.net/zb_users/upload/2024/12/20241208104772177217.jpg&quot; style=&quot;height:203px; width:360px&quot; /&gt;&lt;/p&gt;

&lt;p&gt;带上飞机调试，精度非常满意&lt;/p&gt;

&lt;p&gt;&lt;img alt=&quot;&quot; src=&quot;https://www.fairysoft.net/zb_users/upload/2024/12/2024120810490568568.jpg&quot; style=&quot;height:480px; width:360px&quot; /&gt;&lt;/p&gt;

&lt;p&gt;开始学习画电路板&lt;/p&gt;

&lt;p&gt;&lt;img alt=&quot;&quot; src=&quot;https://www.fairysoft.net/zb_users/upload/2024/12/20241208105093029302.jpg&quot; style=&quot;height:411px; width:360px&quot; /&gt;&lt;/p&gt;

&lt;p&gt;学习手工焊接CPU&lt;/p&gt;

&lt;p&gt;&lt;img alt=&quot;&quot; src=&quot;https://www.fairysoft.net/zb_users/upload/2024/12/20241208105163256325.jpg&quot; style=&quot;height:270px; width:360px&quot; /&gt;&lt;/p&gt;

&lt;p&gt;第一台试验样机组装成功&lt;/p&gt;

&lt;p&gt;&lt;img alt=&quot;&quot; src=&quot;https://www.fairysoft.net/zb_users/upload/2024/12/20241208105280448044.jpg&quot; style=&quot;height:480px; width:360px&quot; /&gt;&lt;/p&gt;

&lt;p&gt;为了提高续航，研究ESP32的睡眠模式，经过一番努力终于把待机电流控制到了2毫安以下，算下来待机时间可以达到200小时以上。&lt;/p&gt;

&lt;p&gt;&lt;img alt=&quot;&quot; src=&quot;https://www.fairysoft.net/zb_users/upload/2024/12/20241208105392809280.jpg&quot; style=&quot;height:480px; width:360px&quot; /&gt;&lt;/p&gt;

&lt;p&gt;最终实现了量产。&lt;/p&gt;

&lt;p&gt;经济版用的3D打印塑料外壳，定价319元。&lt;/p&gt;

&lt;p&gt;豪华版使用铝合金CNC外壳，定价499元。&lt;/p&gt;

&lt;p&gt;&lt;img alt=&quot;&quot; src=&quot;https://www.fairysoft.net/zb_users/upload/2024/12/20241208105688668866.jpg&quot; style=&quot;height:270px; width:360px&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;img alt=&quot;&quot; src=&quot;https://www.fairysoft.net/zb_users/upload/2024/12/20241208105762646264.jpg&quot; style=&quot;height:480px; width:360px&quot; /&gt;&lt;/p&gt;

&lt;p&gt;有需要的可以联系我购买&lt;/p&gt;

&lt;p&gt;&lt;img alt=&quot;&quot; src=&quot;https://www.fairysoft.net/zb_users/upload/2024/12/20241208105981938193.jpg&quot; style=&quot;height:480px; width:360px&quot; /&gt;&lt;/p&gt;
</description><pubDate>Sun, 08 Dec 2024 10:37:56 +0800</pubDate></item><item><title>当前高高原中止进近之后的CPC转换程序存在的问题</title><link>https://blog.fairysoft.net/post/64.html</link><description>&lt;p&gt;先插入一个我自己开发的CPC转换提醒器的广告，详情点击：&lt;a href=&quot;https://blog.fairysoft.net/post/65.html&quot; target=&quot;_self&quot; textvalue=&quot;“飞行员专用座舱高度监控报警器”&quot;&gt;“飞行员专用座舱高度监控报警器”&lt;/a&gt;&lt;/p&gt;&lt;p&gt;进入主题之前我先说一个很多人对于CPC转换的误解。可能很多人都认为CPC转换的作用是强制让备用CPC跟主用CPC“同步”，其实这个理解是不正确的。转换CPC并不会立刻让两部CPC的工作模式同步，每一部CPC原本处于什么模式，转换之后还是什么模式。既然这样，那为什么在下降之后当座舱高度达到8500ft的时候进行一次CPC转换能有效避免虚假的“座舱高度过高”警告呢？我来仔细分析一下这个CPC转换程序背后的逻辑：&lt;/p&gt;&lt;p&gt;飞机正常下降之后当座舱高度开始上升，说明这时候主用CPC已经进入了下降模式。但这时候备用CPC却不一定也进入了下降模式，也就是所谓的“不同步”或者叫“延迟”，此时有两种可能性：&lt;br/&gt;A.备用CPC也进入了下降模式&lt;br/&gt;B.备用CPC还停留在巡航模式&lt;/p&gt;&lt;p&gt;平时飞行时大多数情况都是A，在这种情况下就算不转换也不会触发警告，不过转换一下也没坏处。&lt;/p&gt;&lt;p&gt;如果是情况B，只要飞机持续保持下降，备用CPC还是会探测到飞机已下降，从而进入下降模式，转换时机虽有延迟但大多数时候也不会延迟到座舱高度达到9550ft的报警高度，所以也不会出现警告。这就是为什么在以前，大家虽然都没有做8500转换CPC的程序，也很少出现这个假警告。&lt;/p&gt;&lt;p&gt;只有在极少数情况下，当主用CPC进入到了下降模式而备用CPC还停留在巡航模式正巧又遇到飞机改平，那么备用CPC就会一直停留在巡航模式，主用CPC因为在下降模式，会控制座舱高度一直上升，当上升到9550ft的时候，备用CPC就会发出警告。&lt;/p&gt;&lt;p&gt;在这种情况下，只要在座舱高度达到9550ft之前进行一次CPC的转换，就能使原本卡在巡航模式的CPC变成主用，转换之后处于巡航模式的主用CPC会控制座舱高度从上升转为下降，座舱高度不会达到9550ft，于是避免了触发警告。随后当飞机转入下降，这部停留在巡航模式的CPC迟早也会探测到下降，所以座舱高度又会从8000ft再次上升，这时候也不用担心到了9550ft会触发警告了，因为两部CPC都已经是下降模式了。&lt;/p&gt;&lt;p&gt;CPC转换这个动作并不能直接使两部CPC立刻同步。只是在某些特定的阶段，比如在飞机从巡航开始下降之后当主用CPC已经进入了下降模式的时候，CPC转换程序能确保两部CPC都进入下降模式，从结果上看，两部CPC确实“同步”了，所以才会给大家造成误解。大家一定要知道，转换CPC这个操作并不是在所有情况下都能让两部CPC同步，比如说中止进近之后的转换，后面会详细分析。&lt;/p&gt;&lt;p&gt;写到这里忍不住吐槽一下公司的手册，连猜带蒙才大致看懂写的什么意思：&lt;/p&gt;&lt;p&gt;&lt;img alt=&quot;&quot; src=&quot;https://www.fairysoft.net/zb_users/upload/2024/12/20241207181588278827.jpg&quot; style=&quot;height:412px; width:400px&quot;/&gt;&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;了解了CPC转换并不一定能使两部CPC同步这个原理，我们再看一下中止进近之后的CPC转换程序：&lt;/p&gt;&lt;p&gt;&lt;img alt=&quot;&quot; src=&quot;https://www.fairysoft.net/zb_users/upload/2024/12/2024120718190631631.jpg&quot; style=&quot;height:306px; width:400px&quot;/&gt;&lt;/p&gt;&lt;p&gt;为什么我说这个程序存在BUG？下面我会通过两起典型的案例分析来解释这个问题。&lt;/p&gt;&lt;p&gt;首先看一下空客A320飞机的CPC转换逻辑图&lt;/p&gt;&lt;p&gt;&lt;img alt=&quot;&quot; src=&quot;https://www.fairysoft.net/zb_users/upload/2024/12/20241207193879867986.jpg&quot; style=&quot;height:349px; width:400px&quot;/&gt;&lt;/p&gt;&lt;p&gt;从这个图表中可以看到CPC从下降模式进入上升模式需要探测到持续60秒的飞机爬升，所以中止进近之后如果只上升60秒左右改平，就会导致两部CPC有可能其中一部进入了上升模式而另一部却停留在下降模式。一共有四种可能性：&lt;/p&gt;&lt;p&gt;A.两部都进入了上升模式&lt;br/&gt;B.两部都停留在下降模式&lt;br/&gt;C.主用进入了上升模式而备用停留在下降模式&lt;br/&gt;D.主用停留在下降模式而备用进入了上升模式&lt;/p&gt;&lt;p&gt;其中A和B两种情况下两部CPC处于“同步”状态，所以不管是否转换CPC都不会触发假警告。只有当C或者D两种情况出现时，才有可能触发这个警告。&lt;/p&gt;&lt;p&gt;先说情况D，这个案例是我亲自遇到的，2022年2月在邦达中止进近，改平之后未进行CPC转换（当时手册中还没有中止进近之后的CPC转换程序）。当时是从6300米上升到6600米，只上升了1000英尺就改平（爬升过程大概60秒左右），刚中止进近的时候两部CPC都处于下降模式，座舱高度13800ft。飞机上升一段时间之后备用CPC探测到了超过60秒的持续爬升，进入了爬升模式。但是主用CPC却由于未探测到持续60秒的爬升，还停留在下降模式，所以座舱高度也一直保持在13800ft没有下降。飞机在6600米改平之后不久，备用CPC进入了巡航模式，触发了“座舱高度过高”的警告。如果当时我们改平之后立即（备用CPC进入巡航模式之前）进行一次CPC的转换，就能避免这个假警告。&lt;/p&gt;&lt;p&gt;再说情况C，这个案例是2024年2月在拉萨中止进近，改平之后机组进行了CPC转换，在转换完CPC后不久触发了警告。当时是从6900米爬升到7200米，同样也是只上升了1000英尺就改平（爬升过程大概60秒左右），刚中止进近的时候两部CPC都处于下降模式，座舱高度11300ft，CPC2为主控，CPC1为备用。随后CPC2探测到爬升时间超过了60秒，进入到了上升模式，于是CPC2控制座舱高度下降到目标高度8000英尺。CPC1直到飞机改平都没有探测到60秒的持续爬升，所以未能进入上升模式，仍然停留在下降模式。改平之后不久CPC2从上升模式转为巡航模式，但是CPC1仍然停留在下降模式（因为下降模式无法直接跳到巡航模式）。当座舱高度下降到8500ft时，机组转换CPC，导致处于下降模式的CPC1成为了主控，处于巡航模式的CPC2被转换到了备用，成为主控的CPC1（下降模式）控制座舱高度从8500ft再次转为上升，当座舱高度上升到9550英尺时，处于备用状态的CPC2（巡航模式）触发了警告。&lt;/p&gt;&lt;p&gt;通过上面的案例分析我发现：情况C如果不转换CPC不会出现警告，转换之后反而会触发警告。情况D不转换CPC会出现假警告，如果转换则能避免这个警告。但是目前的FCOM手册的程序并没有区分这两种不同的情况，统一要求机组在改平之后进行CPC转换。也就是说机组现在所执行的这个程序，在某些情况下能避免假警告，但是在某些情况下不仅不能避免警告，反而还会导致假警告。&lt;/p&gt;&lt;p&gt;以上分析都是我个人的理解，不一定正确，因为缺乏更多的技术资料和案例可查阅，但是根据目前我所知的这几起案例，以上整个分析过程从逻辑上是能自洽的。如果大家有不同看法，请留言指出，不吝赐教！&amp;nbsp;&lt;/p&gt;</description><pubDate>Sat, 07 Dec 2024 17:49:12 +0800</pubDate></item><item><title>RNP进近的温度限制</title><link>https://blog.fairysoft.net/post/60.html</link><description>&lt;p&gt;我们先来看一个某公司最近的事件报告中的片段：&lt;/p&gt;&lt;p&gt;“机组在做进近准备收到签派电话：告知西双版纳/嘎洒机场盲降不工作，因机场温度过高不能使用 RNP LNAV/VNAV进近程序，可能使用 LNAV 或 VOR/DME 进近。机组收到消息后主计划按 RNP LNAV 程序准备，二计划按 VOR/DME 程序准备……。13:56 机组首次联系嘎洒进近（后简称进近），被告知使用 16 号 RNP 程序进近，机组考虑温度超限制，提出使用 LNAV 进近，进近告知机组：不能使用 LNAV 程序，更改指令使用 16 号跑道 VOR/DME 进近……”&lt;/p&gt;&lt;p&gt;看完这段报告，我们来思考一个问题：版纳机场16号RNP进近航图上标注了这样一个限制：“无温度补偿的航空器，运行温度范围为0℃-29℃(Baro-VNAV)”。现在机场气温30℃，能否运行RNP进近程序？&lt;/p&gt;&lt;p&gt;&lt;img alt=&quot;&quot; src=&quot;https://www.fairysoft.net/zb_users/upload/2024/7/2024072801340481481.jpeg&quot; style=&quot;height:120px; width:338px&quot;/&gt;&lt;/p&gt;&lt;p&gt;要回答这个问题，首先要弄清楚RNP进近中的“LNAV/VNAV”与“LNAV”是什么。我们都知道LNAV代表水平导航、VNAV代表垂直导航，这是航空器的两个导航功能。但是在RNP进近程序中，这两者却另有所指。我查阅了民航局飞标司的咨询通告《在终端区和进近中实施RNP的运行批准指南》(AC-91-FS-2010-01R1)。通告中是这么写的：“RNP 进近一般包括 LNAV 和 LNAV/VNAV 两类运行最低标准。”&lt;/p&gt;&lt;p&gt;&lt;img alt=&quot;&quot; src=&quot;https://www.fairysoft.net/zb_users/upload/2024/7/20240728014747214721.jpeg&quot; style=&quot;height:132px; width:480px&quot;/&gt;&lt;/p&gt;&lt;p&gt;按照这个咨询通告的描述，“LNAV/VNAV”和“LNAV”在RNP进近中是两类最低标准，也可以理解为RNP进近程序的两个子类，前者是类精密进近，使用DA，后者是非精密进近，使用MDA。这两类进近由于垂直导航源使用的是气压式高度表，所以机场的温度会对飞机的飞行高度产生影响，简单来说就是温度高的时候飞机飞行轨迹会比预期高，温度低的时候飞机飞行轨迹会比预期低。于是程序设计上会对没有温度补偿功能的飞机（比如说常见的A320和B737）做出限制，当温度超出限制范围，就不能使用LNAV/VNAV的最低标准，只能使用LNAV的最低标准，也就是降级为非精密进近。使用LNAV最低标准的RNP进近和其他非精密进近程序一样是没有温度限制的，比如丽江20号的RNP进近只有LNAV标准，航图上就没有标注温度限制。&lt;/p&gt;&lt;p&gt;现在又有了一个新问题：同样是使用气压高度表作为垂直导航源，为什么类精密进近有温度限制而非精密进近没有温度限制呢？对这个问题，我翻了一下书，没找到标准答案，所以只能自己揣测一下：这大概是因为类精密进近在设计飞行程序的时候没有给飞行员提供低温修正的程序。而传统的非精密进近由于可以做低温修正，所以不需要设置运行温度的下限。类精密进近把垂直引导完全交给计算机控制，不允许飞行员对计算机程序数据进行修改，这其实是一种很先进的设计理念。只是目前受限于技术的原因使用了气压高度表来作为垂直导航数据源，所以才不得不加入温度限制。等到使用LPV技术的RNP进近在国内普及之后，温度限制的问题就不复存在了。&lt;/p&gt;&lt;p&gt;好了，总结一下：温度超出范围并不意味着RNP程序不可用，只是对于大部分不具备温度补偿功能的飞机来说不能执行LNAV/VNAV的最低标准，只能使用LNAV的最低标准，也就是降级为非精密进近，降级的目的是为了让你能做低温修正程序。但是温度高于最高限制值为什么也要降级为非精密进近，这一点我始终没有想明白。虽说温度高会带来下滑角过大的问题，但是降级为非精密进近之后并不能解决这个问题，因为我们只有低温修正程序而没有高温修正程序。&lt;/p&gt;&lt;p&gt;了解了以上这些知识，我们再来看一下具体操作上问题：RNP进近使用LNAV/VNAV最低标准和使用LNAV最低标准分别该怎么飞？很多人对此都有一个误区，认为LNAV不能使用FINAL APP模式来引导。为此我专门查阅了几个航司的飞行手册（空客系列），基本上大同小异：对于使用LNAV/VNAV最低标准的RNP进近，只能使用FINAL APP这一种模式来引导；对于使用LNAV最低标准的RNP进近，可以使用FINAL APP模式也可以使用NAV FPV模式，而且公司更推荐的是FINAL APP模式，NAV FPA模式大致是为了用于低温修正程序。&lt;/p&gt;&lt;p&gt;&lt;img alt=&quot;&quot; src=&quot;https://www.fairysoft.net/zb_users/upload/2024/7/20240729025375537553.jpeg&quot; style=&quot;height:456px; width:480px&quot;/&gt;&lt;/p&gt;&lt;p&gt;最后我们再回过头来分析一下文章开头那个事件报告中的几个问题，首先，机组和签派混淆了概念，把LNAV/VNAV当成了进近程序，认为温度超出限制范围就&amp;quot;不能使用LNAV/VNAV进近程序&amp;quot;，于是向管制员申请&amp;quot;使用LNAV进近&amp;quot;，管制员也许是没明白机组的意图，以为机组无法执行RNP进近程序，于是指挥机组使用VOR进近，而版纳16号VOR进近的下滑角是3.5度，比RNP还大，30多度的高温让梯度变得更陡，飞行难度更大，从而导致后面发生了一些其他不安全事件。机组当时其实不需要向管制员申请，直接执行LNAV的最低标准就可以了。至于在进近引导方式上，机组依然可以使用FINAL APP的模式来引导。我查阅了飞行操作手册，对于垂直管理引导模式，手册上只对低温做了限制，没有对高温做出使用限制。&lt;/p&gt;&lt;p&gt;&lt;img alt=&quot;&quot; src=&quot;https://www.fairysoft.net/zb_users/upload/2024/7/20240729030444854485.jpeg&quot; style=&quot;height:121px; width:480px&quot;/&gt;&lt;/p&gt;&lt;p&gt;&lt;em&gt;在文章最后我申明一下：文章开头写的“某公司的事件”是我瞎编的，如有雷同，纯属巧合。&lt;/em&gt;&lt;/p&gt;</description><pubDate>Sun, 28 Jul 2024 03:19:42 +0800</pubDate></item><item><title>安卓应用的https抓包简明教程</title><link>https://blog.fairysoft.net/post/59.html</link><description>&lt;p&gt;基本原理：在电脑上运行抓包工具Fiddler，安卓手机跟电脑在同一个局域网内，Fiddler会启动一个网络代理，在安卓手机的WIFI设置里面手动指定这个代理，手机所有的流量都会通过Fiddler来转发，于是Fiddler就能抓取手机的http数据包了。但是https的包是经过加密的，Fiddler抓到的包无法显示包的内容，这时我们就需要在手机上安装Fiddler的证书，并信任这个证书。在安卓手机上安装和信任证书需要Root，由于手机Root不太方便，所以我使用的是夜神安卓模拟器。&lt;/p&gt;

&lt;p&gt;方法很简单，教程如下：&lt;/p&gt;

&lt;p&gt;1.下载安装抓包工具Fiddler和夜神模拟器，下载地址和安装方法本文略过&lt;/p&gt;

&lt;p&gt;2.下载安装openssl，下载地址：http://slproweb.com/products/Win32OpenSSL.html&lt;/p&gt;

&lt;p&gt;3.安装完openssl之后，在环境变量PATH中添加openssl的可执行文件路径，比如说： C:\Program Files\OpenSSL-Win64\bin，然后打开命令提示符或者Power Shell输入命令：openssl version 查看openssl版本（验证是否安装成功）&lt;/p&gt;

&lt;p&gt;4.打开Fiddler、选项、HTTPS、勾上&amp;ldquo;抓取HTTPS&amp;rdquo;，然后点右边的Actions按钮，在下拉菜单中点击&amp;ldquo;导出证书到桌面&amp;rdquo;，你会发现桌面上有了一个名为&amp;ldquo;FiddlerRoot.cer&amp;rdquo;的证书文件&lt;/p&gt;

&lt;p&gt;4.用openssl将证书转换为pem格式：openssl x509 -inform DER -in FiddlerRoot.cer -out cacert.pem，执行完成之后桌面上会出现一个名为&amp;ldquo;cacert.pem&amp;rdquo;的pem格式证书&lt;/p&gt;

&lt;p&gt;5.获取cacert.pem的hash信息：openssl x509 -inform PEM -subject_hash_old -in cacert.pem，比如说hash信息为&amp;ldquo;269953fb&amp;rdquo;&lt;/p&gt;

&lt;p&gt;6.用这个hash信息来重命名cacert.pem文件为269953fb.0，然后将269953fb.0复制到安卓设备/system/etc/security/cacerts/这个目录下（文件管理器要支持root才行，我用的是ES文件浏览器）&lt;/p&gt;

&lt;p&gt;7.在手机设置---&amp;gt;安全--&amp;gt;加密凭证--&amp;gt;信任的凭证中找到名为&amp;ldquo;DO_NOT_TRUST&amp;rdquo;的证书，看一下有没有启用，如果没启用的话就启用。&lt;/p&gt;

&lt;p&gt;完成！&lt;/p&gt;

&lt;p&gt;&amp;nbsp;&lt;/p&gt;
</description><pubDate>Mon, 16 Oct 2023 15:44:18 +0800</pubDate></item><item><title>让局域网内网支持双网段</title><link>https://blog.fairysoft.net/post/58.html</link><description>&lt;p&gt;由于各种网络设备默认的IP地址都是192.168.1开头，新的设备接上家庭内网之后可能会有冲突，所以我将原来内网使用的192.168.1开头的网段改成了10.10.1开头的网段。改完之后才发现原来有几台设备没有启动DHCP，而是192.168.1网段的固定IP地址。为了保持对原网段的兼容，我想在内网中使用双网段，这样就不用手动指定IP地址就能直接访问内网中的两个网段的设备。&lt;/p&gt;

&lt;p&gt;我的思路是将LAN口增加绑定一个192.168.1.1的地址，然后添加一条静态路由，让192.168.1.0/24网段的数据包不被转发到WAN口，而是强行转发到LAN口。&lt;/p&gt;

&lt;p&gt;但是如何给LAN口绑定一个新的IP地址呢？一开始我尝试在Luci设置的&amp;ldquo;网络&amp;rdquo;-&amp;gt;&amp;ldquo;接口&amp;rdquo;里面添加一个LAN口，将IP设置为静态IP，指定IP地址192.168.1.1，试了一下没有成功。&lt;/p&gt;

&lt;p&gt;然后我又尝试了用ifconfig eth0 192.168.1.1 netmask 255.255.255.0 broadcast 192.168.1.255这个指令（借鉴了之前在WAN口绑定一个192.168.0.2的IP地址用来访问光猫的方法），结果还是失败了。&lt;/p&gt;

&lt;p&gt;后来在网上找到了一个简单的方法，那就是直接修改/etc/config/network，添加一段配置：&lt;/p&gt;

&lt;p&gt;config alias&lt;br /&gt;
&amp;nbsp;&amp;nbsp; &amp;nbsp;option interface &amp;#39;lan&amp;#39;&lt;br /&gt;
&amp;nbsp;&amp;nbsp; &amp;nbsp;option proto &amp;#39;static&amp;#39;&lt;br /&gt;
&amp;nbsp;&amp;nbsp; &amp;nbsp;option ipaddr &amp;#39;192.168.1.1&amp;#39;&lt;br /&gt;
&amp;nbsp;&amp;nbsp; &amp;nbsp;option netmask &amp;#39;255.255.255.0&amp;#39;&lt;br /&gt;
&amp;nbsp;&amp;nbsp; &amp;nbsp;option ip6assign &amp;#39;64&amp;#39;&lt;/p&gt;

&lt;p&gt;修改完之后保存，/etc/init.d/network restart重启网络&lt;/p&gt;

&lt;p&gt;进入&amp;ldquo;状态&amp;rdquo;-&amp;gt;&amp;ldquo;路由表&amp;rdquo;，你会发现系统自动添加了一条192.168.1.0/24到LAN口的路由。&lt;/p&gt;

&lt;p&gt;试一下访问192.168.1.x的地址，成功！&lt;/p&gt;
</description><pubDate>Sun, 15 Oct 2023 21:31:27 +0800</pubDate></item><item><title>两台OpenWRT通过Wireguard实现局域网互联</title><link>https://blog.fairysoft.net/post/57.html</link><description>&lt;p&gt;两处住所A和B都是家用宽带，A处有一台wifi打印机，但是在外网不能打印，需要外网打印的时候可以通过OpenVPN连接到A的内网来打印。B处有一台威联通的NAS，虽说可以通过动态域名加端口转发来远程访问，但是想要用Windows的文件共享也只能用OpenVPN连接到B的内网来使用。虽说OpenVPN能满足外网访问的需求，但是需要在局域网内每台设备上都安装OpenVPN的客户端，每次使用之前要先连接到对方的内网，用完之后要断开连接，不够方便。&lt;/p&gt;

&lt;p&gt;最近发现了一个叫Wireguard的组网神器，它能在两台网络设备上建立一个加密隧道，并将部分或者所有流量通过这个隧道转发。比方说如果在两台网关路由器上通过Wireguard建立一个隧道，然后将对方的局域网段IP地址设置为通过这个隧道转发，这两个路由器下的所有网络设备就可以直接访问对方的局域网了，非常方便。据说Wireguard的代码非常精简，只有4000多行，而OpenVPN大约有10万行代码，而且Wireguard以Linux内核模块的形式运行，资源占用小。从2020年1月开始，它已经并入了Linux内核的5.6版本，这意味着大多数Linux发行版的用户不用安装任何三方软件就能直接使用。连Linux创始人Linus Torvalds都称其为&amp;ldquo;一件艺术品&amp;rdquo;。&lt;/p&gt;

&lt;p&gt;Wireguard配置起来非常简单，网上教程一大把，但是很多教程都是手把手的讲步骤，却没有说清楚它的原理，跟着这些教程来做一不小心就容易踩坑。本文着重讲一下他的工作原理，看懂了原理再跟着教程来做就不容易踩坑了。&lt;/p&gt;

&lt;p&gt;一、安装&lt;br /&gt;
如果你的OpenWRT没有预装Wireguard，我建议还是重新刷个带Wireguard的固件，如果通过软件包来安装，大概率装了之后用不了。我就是在这一步卡了很久，后来才知道安装kmod-wireguard版本必须要跟内核版本一致，但是软件源提供的kmod-wireguard大概率跟你的内核不一致，装了也用不了。&lt;/p&gt;

&lt;p&gt;二、工作模式&lt;br /&gt;
Wireguard跟其他的VPN不一样，首先，没有服务器端和客户端的概念，通信双方是对等的，双方事先就配置好了对方的公钥，所以在初次连接的时候不需要复杂的握手和认证过程；其次，双方的IP地址也是自己填写的固定值，没有DHCP分配一说。由于以上两点的原因，Wireguard最好是用于两台网关设备的对连，如果你想要让多台设备连接到同一个OpenWRT路由器，那么每增加一个设备，OpenWRT上都要增加一个Peer的配置。一开始我以为OpenWRT上运行的是服务器端，手机上安装的是客户端，所以很多教程一开始没看明白，在配置的时候很多地方都填错了。&lt;/p&gt;

&lt;p&gt;三、配置&lt;br /&gt;
每个设备上Wireguard的设置都分为两部分，一个是本机设置，另一个是对端的设置。本机也被称为Interface，可以理解为本地的一个虚拟网卡，你需要填写三个东西：自己的私钥、本接口的IP地址、本地侦听端口。一个Interface可以跟多个对端分别建立加密隧道，每一个对端被称为一个Peer，你需要填写三个东西：对端的公钥、需要路由的IP网段、对端的公网地址和端口，其中对端的公网地址和端口不是必填的项目，如果你需要主动去跟对方发起连接你就填，反之就不用填，比如说对方没有公网地址那就没法填，这种情况下只能被动等对方跟自己建立连接。&lt;/p&gt;

&lt;p&gt;四、私钥和公钥&lt;br /&gt;
私钥和公钥是成对的，每个设备都要有一个私钥公钥对。每个设备的Interface里面填的是自己的私钥，对方要把你的公钥填进他的Peer里面，同样你的Peer配置里面填的是对方的公钥。私钥公钥对可以在终端里通过命令来生成，步骤为先生成私钥然后通过私钥生成公钥，这两个命令可以合并为&lt;span style=&quot;font-family:courier new,courier,monospace&quot;&gt;&lt;em&gt;&lt;span style=&quot;background-color:#E6E6FA&quot;&gt;$ wg genkey | tee privatekey | wg pubkey &amp;gt; publickey&lt;/span&gt;&lt;/em&gt;&lt;/span&gt;，手机上则可以在APP里面直接生成一对随机的私钥公钥对，甚至如果你在OpenWRT上通过&lt;span style=&quot;font-family:courier new,courier,monospace&quot;&gt;&lt;em&gt;&lt;span style=&quot;background-color:#E6E6FA&quot;&gt;$ wg genkey &amp;gt; privatekey&lt;/span&gt;&lt;/em&gt;&lt;/span&gt;这个命令生成了一个私钥，然后把这个私钥填进手机APP里，手机也能通过这个私钥来生成对应的公钥。所以如果你不想用SSH进入OpenWRT的命令行终端，你也可以在手机上生成两对私钥公钥，一对自己用另一对给OpenWRT用。&lt;/p&gt;

&lt;p&gt;五、路由的网段&lt;br /&gt;
需要路由的网段是配置在Peer里面的，意思是你可以将不同的IP网段的流量发送给不同的Peer，这里应该填对方路由器的内网网段，这样你要访问对方局域网网段的时候流量就会被转发到Wireguard的虚拟网卡，Wireguard再将这些流量通过相应的加密隧道发送给对方。这里一定要注意的是，一旦填了这个东西，系统的路由表就会被修改，我第一次填这个东西的时候一不小心把路由器的Lan口网段填了进去，后果就是所有Lan口的流量全都被转发到了Wireguard的虚拟网卡，然后家里就断网了，而且还不光是断网这么简单，连路由器自身的管理页面都无法访问了。正当我心灰意冷的准备给路由器重新刷机的时候，我发现家里有人还在愉快的用手机逛淘宝，我问是不是用的移动网络，回答不是。我将信将疑的拿过手机一看，确实连的是家里的wifi，开浏览器输入baidu.com，无法访问。我忽然恍然大悟，淘宝走的是IPv6！哈哈，天无绝人之路，我之前在OpenWRT路由器上做个一个IPv6的DDNS，现在终于可以派上用场了，马上在电脑上用IPv6的地址访问路由器，熟悉的Luci界面出现了，还好不用重新刷机。&lt;/p&gt;
</description><pubDate>Sat, 07 Oct 2023 02:35:28 +0800</pubDate></item><item><title>修改威联通NAS磁盘挂载点路径中CACHEDEV的数字编号</title><link>https://blog.fairysoft.net/post/56.html</link><description>&lt;p&gt;昨天给NAS换了一块硬盘，顺便把厚卷改成了静态卷。&lt;/p&gt;

&lt;p&gt;过程很简单，先把新硬盘插到4号空盘位，新创建一个静态卷Volume4，然后用HBS把数据从Volume3同步到Volume4，完了之后取下3号硬盘。&lt;/p&gt;

&lt;p&gt;本以为大功告成，结果后来却发现有些应用出了问题，经过一番排查发现在这些应用的设置中使用了绝对路径：&lt;/p&gt;

&lt;p&gt;原来的/share/CACHEDEV3_DATA现在变成了/share/CACHEDEV4_DATA，导致应用无法找到文件。&lt;/p&gt;

&lt;p&gt;这个简单，安全卸载Volume4，然后将这块4号盘位的硬盘换到3号盘位，恢复存储池。&lt;/p&gt;

&lt;p&gt;结果没想到的是，新的硬盘虽然换到了3号盘位，但是路径居然还是/share/CACHEDEV4_DATA。&lt;/p&gt;

&lt;p&gt;安全卸载不行，那就干脆拔掉硬盘并删除Volume4，再插入硬盘恢复存储池。&lt;/p&gt;

&lt;p&gt;结果那个数字不但没有变成3反而路径还变成了/share/CACHEDEV5_DATA。&lt;/p&gt;

&lt;p&gt;再试一次，拔掉硬盘，删除Volume4，重启，插入硬盘，恢复存储池。&lt;/p&gt;

&lt;p&gt;再一看，居然又变成了/share/CACHEDEV6_DATA，每次都自动加1。&lt;/p&gt;

&lt;p&gt;我要崩溃了！看来这个投机耍滑的伎俩不好用。&lt;/p&gt;

&lt;p&gt;虽然这不是什么大问题，在应用中改一下路径就能解决，但这不是我的作风，强迫症患者怎么能接受3号盘位的硬盘叫DEV6呢！&lt;/p&gt;

&lt;p&gt;必须得改回来！&lt;/p&gt;

&lt;p&gt;经过一番研究发现，/share/CACHEDEV6_DATA是一个挂载点，它挂载的是逻辑卷/dev/vg290/lv6的数据。&lt;/p&gt;

&lt;p&gt;那么先卸载/share/CACHEDEV6_DATA，然后把/dev/vg290/lv6改名为/dev/vg290/lv3再挂载不就可以了吗。&lt;/p&gt;

&lt;p&gt;PuTTY出场，SSH进系统，admin账号登录，经过一番尝试，发现没这么简单。&lt;/p&gt;

&lt;p&gt;/share/CACHEDEV6_DATA这个挂载点并不是直接挂载的逻辑卷/dev/vg290/lv6，QNAP为了提高性能，在挂载点跟逻辑卷中间加了一层flashcache，路径为/dev/mapper/cachedev6。&lt;/p&gt;

&lt;p&gt;原理搞清楚就有办法了，又经过一番折腾，终于成功了，下面是详细的步骤：&lt;/p&gt;

&lt;p&gt;1.先卸载/share/CACHEDEV6_DATA&lt;/p&gt;

&lt;pre class=&quot;prettyprint&quot;&gt;
[~] # umount /share/CACHEDEV6_DATA&lt;/pre&gt;

&lt;p&gt;2.看一下/dev/mapper/cachedev6还在不在&lt;/p&gt;

&lt;pre class=&quot;prettyprint&quot;&gt;
[~] # ls -l /dev/mapper/&lt;/pre&gt;

&lt;p&gt;3.居然还在，删掉它&lt;/p&gt;

&lt;pre class=&quot;prettyprint&quot;&gt;
[~] # dmsetup remove cachedev6&lt;/pre&gt;

&lt;p&gt;4.将逻辑卷lv6改名为lv3&lt;/p&gt;

&lt;pre class=&quot;prettyprint&quot;&gt;
[~] # lvrename /dev/vg290/lv6 /dev/vg290/lv3&lt;/pre&gt;

&lt;p&gt;5.重建存储系统&lt;/p&gt;

&lt;pre class=&quot;prettyprint&quot;&gt;
[~] # /etc/init.d/init_lvm.sh&lt;/pre&gt;

&lt;p&gt;OK，终于变回/share/CACHEDEV3_DATA了&lt;br /&gt;
不过还有一点小问题，原来的共享文件夹不见了。&lt;br /&gt;
不用怕，数据还在，重新在&amp;ldquo;控制台&amp;rdquo;--&amp;gt;&amp;ldquo;共享文件夹&amp;rdquo;中新建共享文件夹，磁盘选Volume3，路径手动选择就能找到原来的数据了。&lt;br /&gt;
当然也可以先移除这个Volume3再恢复存储池也能恢复原来的共享文件夹。&lt;/p&gt;
</description><pubDate>Mon, 25 Apr 2022 12:13:15 +0800</pubDate></item></channel></rss>