ssr's blog
https://leanhe.dev/
Recent content on ssr's blog
Hugo -- gohugo.io
zh-cn
Mon, 09 Sep 2024 20:33:00 +0800
-
在 Arch 上设置 UPS
https://leanhe.dev/posts/2024.09.09.1/
Mon, 09 Sep 2024 20:33:00 +0800
https://leanhe.dev/posts/2024.09.09.1/
<p>因为数据丢失恐惧症的缘故,还是买了 UPS 放在 NAS 旁,以防 NAS 突然断电导致硬盘数据损坏。</p>
<h2 id="设置-nas">设置 NAS</h2>
<p>QNAP 的 UPS 协议符合 Network UPS Tools (nut) 规范,作为 Master 或者 Slave 都可以正常使用,如果作为 Master 使用,连接上 UPS 并且确认 UPS 工作正常以后填入从机的 IP 地址即可使用。</p>
<p>不过由于 NAS 有一点点耗电,所以我想直接在 Arch 上监视 UPS ,让 NAS 作为 nut 的从机。于是我们走一些弯路,设置 UPS 过程并没有那么顺利。</p>
<h2 id="回溯查找协议的心路历程">回溯查找协议的心路历程</h2>
<p>一开始我安装了 APC UPS 包,安装之后才发现好像实现有些不对劲。首先访问的端口不是 APC NAS 的网络预设端口,重新抓了包之后发现其访问的是另一个端口。其次就算配置了正确访问的端口, NAS 那边还是获得不了数据,所以我决定重新查询 QNAP 的文档。</p>
<p>经过了多个论坛的搜索,最后发现 QNAP 是利用 NUT 协议来和其他的 UPS 进行通信的。<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup></p>
<p>并且经过查询资料<sup id="fnref:2"><a href="#fn:2" class="footnote-ref" role="doc-noteref">2</a></sup><sup id="fnref:3"><a href="#fn:3" class="footnote-ref" role="doc-noteref">3</a></sup>得知,它的访问条件比较严格,用户名和密码都是硬编码的。其 NUT 会访问 qnapnas ,然后用户名密码分别是 qnapnas 和 123456 。很奇怪,我似乎没有在官方文档中找到这部分内容的说明,其只是告知网页上应该如何操作修改 NAS 的设置。<sup id="fnref:4"><a href="#fn:4" class="footnote-ref" role="doc-noteref">4</a></sup></p>
<h2 id="在-arch-上设置-ups">在 Arch 上设置 UPS</h2>
<p>直接打开 <a href="https://wiki.archlinux.org/title/Network_UPS_Tools">Arch Wiki</a> 关于此工具的页面按照步骤操作,安装包后执行扫描功能 <code>nut-scanner</code> 查看是否能识别到 NAS 。如果可以识别到 NAS 则将扫描结果直接写入配置文件中。</p>
<pre tabindex="0"><code class="language-conf" data-lang="conf">[nutdev-usb1]
driver = "usbhid-ups"
port = "auto"
vendorid = "MASKED"
productid = "MASKED"
product = "MASKED"
serial = "MASKED"
vendor = "American Power Conversion"
# bus = "001"
# device = "002"
# busport = "003"
</code></pre><p>搜寻结果如上所示,直接把这一部分内容贴到 nut 的配置文件中即可。不过需要把 NAS 名称修改为 <code>qnapnas</code> 否则 NAS 上无法正确读取。</p>
<p>然后,我们还需要给这个设备设置 nut 的用户组,否则 nut 可能无法正确读取(会不停报错)。</p>
<pre tabindex="0"><code>SUBSYSTEM=="usb", ATTR{idVendor}=="XXXX", ATTR{idProduct}=="YYYY", GROUP="nut"
</code></pre><p>将以上内容添加到 <code>/etc/udev/rules.d/50-ups.rules</code> 中,然后执行以下指令。<sup id="fnref:5"><a href="#fn:5" class="footnote-ref" role="doc-noteref">5</a></sup></p>
<pre tabindex="0"><code># udevadm control --reload
# udevadm trigger
</code></pre><p>完成以上操作后,先执行 <code>sudo upsdrvctl start</code> 来创建 UPS 的配置文件,如果没有其它错误的话,可以执行 <code>sudo systemctl enable nut-driver-enumerator.service</code> 让其在之后可以自动配置 UPS 相关的服务。</p>
<p>设置所有权之后,我们需要将 NAS 上用来请求的用户添加到 nut 的用户列表里。同时在 <code>/etc/nut/upsd.conf</code> 文件中指定 upsd 需要监听的网络地址(预设是设置为只监听 <code>localhost</code>) <sup id="fnref:6"><a href="#fn:6" class="footnote-ref" role="doc-noteref">6</a></sup></p>
<pre tabindex="0"><code class="language-conf" data-lang="conf">[qnapups]
password = 123456
upsmon secondary
</code></pre><p>将以上内容添加到 <code>/etc/nut/upsd.users</code> 中,远程用户就有访问权限了。添加用户之后,我们可以执行 <code>sudo systemctl enable nut-server.service</code> 来让 upsd 服务在 <code>nut.target</code> 时自动启动。接着 <code>sudo systemctl enable nut.target</code> 来让以上提到的服务自动开机启动。</p>
<p>在 NAS 的 UPS 设置中,记得选择 <code>Network slave</code> 选项,并将 IP 地址设置为配置好 nut 的机子 IP。</p>
<p>重新启动/手动启动 nut 相关服务之后,在 NAS 配置上应该可以看到 UPS 已经在线,并且正常工作了。</p>
<h2 id="本地设置-ups-monitor">本地设置 ups monitor</h2>
<pre tabindex="0"><code class="language-conf" data-lang="conf">[upsuser]
password = password
upsmon primary
actions = SET
instcmds = ALL
</code></pre><p>首先创建一个用户添加到文件 <code>upsd.users</code> 中,接着配置 <code>upsmon.conf</code> ,将新建的用户添加进文件中。</p>
<pre tabindex="0"><code>MONITOR qnapups@localhost 1 $upsuser $password primary
</code></pre><p>重启 <code>nut-server</code> 服务,使用 <code>upsc qnapups</code> 来查询当前系统的 UPS 状态。也可以启用 <code>nut-monitor</code> 服务,使其可以自动执行关机操作。</p>
<pre tabindex="0"><code>battery.charge: 100
battery.charge.low: 96
battery.mfr.date: 2001/01/01
battery.runtime: 3618
battery.runtime.low: 120
battery.type: PbAc
battery.voltage: 13.6
battery.voltage.nominal: 12.0
device.mfr: American Power Conversion
device.model: MASKED
device.serial: MASKED
device.type: ups
driver.debug: 0
driver.flag.allow_killpower: 0
driver.name: usbhid-ups
driver.parameter.pollfreq: 30
driver.parameter.pollinterval: 2
driver.parameter.port: auto
driver.parameter.product: MASKED
driver.parameter.serial: MASKED
driver.parameter.synchronous: auto
driver.parameter.vendor: American Power Conversion
driver.state: quiet
driver.version: 2.8.2
driver.version.data: APC HID 0.100
driver.version.internal: 0.53
driver.version.usb: libusb-1.0.27 (API: 0x100010a)
input.sensitivity: low
input.transfer.high: 278
input.transfer.low: 160
input.transfer.reason: input voltage out of range
input.voltage: 220.0
input.voltage.nominal: 220
ups.beeper.status: disabled
ups.delay.shutdown: 20
ups.load: 0
ups.mfr: American Power Conversion
ups.mfr.date: 2024/06/05
ups.model: MASKED
ups.productid: MASKED
ups.realpower.nominal: 390
ups.serial: MASKED
ups.status: OL
ups.test.result: Done and passed
ups.timer.reboot: 0
ups.timer.shutdown: -1
ups.vendorid: MASKED
</code></pre><div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p><a href="https://www.qnap.com/en/how-to/faq/article/can-i-use-the-qnap-nas-as-a-ups-master-to-shutdown-windows-pc">https://www.qnap.com/en/how-to/faq/article/can-i-use-the-qnap-nas-as-a-ups-master-to-shutdown-windows-pc</a> <a href="#fnref:1" class="footnote-backref" role="doc-backlink">↩︎</a></p>
</li>
<li id="fn:2">
<p><a href="https://community.nethserver.org/t/using-qnap-nas-as-ups-server-change-nut-username/12887/3">https://community.nethserver.org/t/using-qnap-nas-as-ups-server-change-nut-username/12887/3</a> <a href="#fnref:2" class="footnote-backref" role="doc-backlink">↩︎</a></p>
</li>
<li id="fn:3">
<p><a href="https://www.reddit.com/r/qnap/comments/ehubuq/quick_network_ups_slave_setup_for_windows_using/">https://www.reddit.com/r/qnap/comments/ehubuq/quick_network_ups_slave_setup_for_windows_using/</a> <a href="#fnref:3" class="footnote-backref" role="doc-backlink">↩︎</a></p>
</li>
<li id="fn:4">
<p><a href="https://docs.qnap.com/operating-system/qts/4.3.x/en-us/GUID-BBA89DEC-E325-4C30-B66A-2BBD0CC01A5F.html">https://docs.qnap.com/operating-system/qts/4.3.x/en-us/GUID-BBA89DEC-E325-4C30-B66A-2BBD0CC01A5F.html</a> <a href="#fnref:4" class="footnote-backref" role="doc-backlink">↩︎</a></p>
</li>
<li id="fn:5">
<p><a href="https://bbs.archlinux.org/viewtopic.php?id=290778">https://bbs.archlinux.org/viewtopic.php?id=290778</a> <a href="#fnref:5" class="footnote-backref" role="doc-backlink">↩︎</a></p>
</li>
<li id="fn:6">
<p><a href="https://networkupstools.org/docs/user-manual.chunked/ar01s06.html">https://networkupstools.org/docs/user-manual.chunked/ar01s06.html</a> <a href="#fnref:6" class="footnote-backref" role="doc-backlink">↩︎</a></p>
</li>
</ol>
</div>
-
MxRoute 概要
https://leanhe.dev/posts/2024.06.12.1/
Wed, 12 Jun 2024 20:17:14 +0800
https://leanhe.dev/posts/2024.06.12.1/
<p>感觉在现在这个环境下 Yandex 已经式微了,主要可能还是广告挺多,以及不知道什么时候免费的邮箱会被强制升级为付费邮箱的政策,所以我早早就做好了抛弃 Yandex 的打算。</p>
<p>和其他几位朋友讨论了一段时间以后,决定使用 MxRoute 这家提供的服务。一方面它使用上确实还行,另一方面它网页宣传很有意思,全是 spammer 想说的话,所以我决定购买其 2023 年黑五提供的优惠活动试试水。</p>
<h2 id="dns-配置">DNS 配置</h2>
<p>下订单付费过程不表,重点它会往你注册的邮箱发送一封邮件,该邮件内有需要你在 DNS 服务器上设置的内容, MX 及 TXT 记录。依照邮件内容在对应的域名 DNS 上设置即可。 DKIM 相关的设置在邮件中发送的邮箱管理页面相对应域名下(如果没有请手动添加相对应的域名)的 <code>DKIM keys</code> 中,找到 <code>x._domain</code> 所对应条目,把值复制下来后设置到同名的 TXT 记录中。</p>
<h2 id="webmail">WebMail</h2>
<p>该邮箱系统有自带的 WebMail ,可以在账号管理中选择账号直接登录,也可从左边的 WebMail 界面选择需要的 WebMail 直接登陆即可。</p>
-
记一次成功的 Btrfs RAID 1 掉盘救回
https://leanhe.dev/posts/2024.01.04.1/
Thu, 04 Jan 2024 20:16:59 +0800
https://leanhe.dev/posts/2024.01.04.1/
<p>很不幸,我也不希望新年的时候在修自己的伺服器,但是它的 Btrfs 确实是坏了</p>
<h1 id="发现故障">发现故障</h1>
<p>这台伺服器的一部分功能是用来做自己 pt 的种,有一天我想下一点种的时候发现怎么样都没法把种传上去, qb web 是正常的,但这是为什么呢?</p>
<p>上去看了一眼 journalctl 发现一堆红色的 btrfs 报错。</p>
<p><img src="https://img.shabiwangyou.com/images/2024/02/08/image.png" alt=""></p>
<p>初步看了一下,大概是某个盘掉了。不过还好,我这个盘是 raid 1 由 btrfs 挂载,数据应该没有问题。</p>
<p>盘的话,因为是 Hetzner 的杜甫,所以请他们帮忙换个盘就行。</p>
<h1 id="换盘">换盘</h1>
<p>发工单请 Hetzner 那边帮忙换一个能用的磁盘即可。它有两个选项,一个是保证磁盘 < 1000h 的使用时间,不过要额外加钱,另一个是尽量新,不需要额外的钱。那这样我们选尽量新就行(虽然给了我们一个 10000+ h 的)。</p>
<p>接着它要我们填写受影响的磁盘序列号,我这里只有两块磁盘,盘都没有了怎么读序列号,于是我们就填写了没有受影响的磁盘序列号。它也可以选择时间进行替换,还有替换的方式。因为跑的是非关键服务,就让它 ASAP 吧,同时也可以关机替换,反正无所谓。</p>
<p>接着等 Hetzner 给我们发邮件就可以了。</p>
<h1 id="修复-btrfs">修复 btrfs</h1>
<p>大概过了一个小时, Hetzner 发送邮件给我大概说他们已经替换完成了,现在服务器已经帮我们重新打开应该可以正常使用了。在发工单之前,我已经通过控制台让服务器先进入救援模式,这样它就不会尝试启动系统,而进入到网络挂载的系统中。</p>
<p>引导救援系统成功之后,首先先检查一下目前磁盘的状态。 <code>fdisk -l</code> 可以看到两块磁盘都已经可以重新读出来了,一块是我们以前使用过的所以上面存在分区表。</p>
<!--这里应该有图-->
<p>首先先把新的磁盘分区一下,分成目前磁盘的分区格式,这里就不再概述了。</p>
<p>然后检查一下 btrfs 分区的状态</p>
<pre tabindex="0"><code># btrfs fi show
</code></pre><pre tabindex="0"><code>Label: 'arch' uuid: 00000000-0000-0000-0000-000000000000
Total devices 2 FS bytes used 5.16TiB
devid 1 size 0 used 0 path MISSING
devid 2 size 5.44TiB used 5.43TiB path /dev/sda2
</code></pre><p>可以看到它并不能读到另一个盘,这时候我们直接使用 replace 大法,从原有的 raid 1 数据盘中替换成另一个盘。</p>
<pre tabindex="0"><code># btrfs replace start 1 /dev/sdb2 /mnt
</code></pre><p>这里要注意一个点,就是你替换的 <code>device id</code> 一定是你 <code>MISSING</code> 的,而不是现有的 <code>device id</code> ,不要问我为什么这样提醒。</p>
<p>然后经过漫长的等待之后, replace 操作就顺利完成了。</p>
<pre tabindex="0"><code># btrfs fi show
Label: 'arch' uuid: e753ab70-b8ad-4983-9a87-eb19c53cb93f
Total devices 2 FS bytes used 5.16TiB
devid 1 size 5.44TiB used 5.43TiB path /dev/sda2
devid 2 size 5.44TiB used 5.44TiB path /dev/sdb2
</code></pre><h1 id="恢复-btrfs-的-profile">恢复 btrfs 的 profile</h1>
<p>替换完之后还有一些步骤,比如说修复引导,因为引导可能是之前已经损坏的盘上的,这部分就不细述了。</p>
<p>成功引导进系统之后,我发现有一个奇怪的事情,就是我目前的盘上有 raid 1 的配置,也有 single 的配置。</p>
<p><img src="https://img.shabiwangyou.com/images/2024/02/08/btrfs-convert-start.png" alt=""></p>
<p>经过我多方搜索,发现只要对 data 部分进行 balance 就行</p>
<pre tabindex="0"><code># btrfs balance start -dconvert=raid1,soft /
</code></pre><p>这里加 soft 可以直接跳过已经是正确 raid 的部分,从而加快转换的速度。</p>
<p><img src="https://img.shabiwangyou.com/images/2024/02/08/btrfs-convert-finish.png" alt=""></p>
<p>至此, btrfs 的文件系统就恢复完成了。</p>
-
在 Arch 下利用 pg_upgrade 执行 PostgreSQL 版本升级
https://leanhe.dev/posts/2022.05.26.1/
Thu, 26 May 2022 20:16:59 +0800
https://leanhe.dev/posts/2022.05.26.1/
<h1 id="版本检查">版本检查</h1>
<p>首先确认你正在使用的 PostgreSQL 是上一个版本,检查资料库版本以便进行下一步操作。</p>
<p>一般情况下,可以使用下列命令检查</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-plain" data-lang="plain"><span style="display:flex;"><span># cat /var/lib/postgres/data/PG_VERSION
</span></span></code></pre></div><p>而后,检查你所新安装的包的版本,确认其是否不一致且始终仅大于一个版本。</p>
<p>这个升级方法理应对所有的<strong>传统</strong>资料库有效。对于非传统的资料库,比如说通过流复制或日志传送,可能需要<a href="https://www.postgresql.org/docs/current/pgupgrade.html">查看文档</a><sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup></p>
<h1 id="准备工作">准备工作</h1>
<p>首先需要安装 <a href="https://archlinux.org/packages/extra/x86_64/postgresql-old-upgrade/">postgresql-old-upgrade</a> 这个包。</p>
<p>进行升级之前,您或许需要执行一次资料库备份。</p>
<h1 id="开始升级">开始升级</h1>
<ol>
<li>如果 postgresql 的实例仍然在运行,请收集 <code>initdb</code> 的参数。</li>
<li>停止 <code>postgresql.service</code> ,并检查其确实停止了。</li>
<li>可以在这个步骤操作升级,但是如果已经升级了,跳过这一步。</li>
<li>重命名旧实例的目录,然后创建新实例的目录和临时工作目录。</li>
</ol>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-plain" data-lang="plain"><span style="display:flex;"><span># mv /var/lib/postgres/data /var/lib/postgres/olddata
</span></span><span style="display:flex;"><span># mkdir /var/lib/postgres/data /var/lib/postgres/tmp
</span></span><span style="display:flex;"><span># chown postgres:postgres /var/lib/postgres/data /var/lib/postgres/tmp
</span></span><span style="display:flex;"><span>[postgres]$ cd /var/lib/postgres/tmp
</span></span></code></pre></div><ol start="5">
<li>利用 <code>initdb</code> 初始化新实例的资料库(利用之前收集的 <code>initdb</code> 的参数)</li>
</ol>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-plain" data-lang="plain"><span style="display:flex;"><span>[postgres]$ initdb -D /var/lib/postgres/data --locale=xy_XY.UTF-8 --encoding=UTF8 --data-checksums
</span></span></code></pre></div><ol start="6">
<li>利用 <code>pg_upgrade</code> 升级资料库,把 <code>PG_VERSION</code> 替换成之前的 PostgreSQL 版本 (例如 <code>13</code>)</li>
</ol>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-plain" data-lang="plain"><span style="display:flex;"><span>[postgres]$ pg_upgrade -b /opt/pgsql-PG_VERSION/bin -B /usr/bin -d /var/lib/postgres/olddata -D /var/lib/postgres/data
</span></span></code></pre></div><p>如果需要的话,调整新资料库的配置文件 (例如: <code>pg_hba.conf</code> 和 <code>postgresql.conf</code>) 以便和旧资料库符合。</p>
<p>如果遇到 <code>pg_upgrade</code> 失败且信息为: <code>The source cluster was not shut down cleanly.</code> 请参阅<a href="https://wiki.archlinux.org/title/PostgreSQL#Upgrading_PostgreSQL">这个链接</a></p>
<ol start="7">
<li>
<p>重新启动 <code>postgresql.service</code></p>
</li>
<li>
<p>如果一切正常,可删除 <code>/var/lib/postgres/olddata</code> 和 <code>/var/lib/postgres/tmp</code> 目录</p>
</li>
</ol>
<p>这里有指令被我忽略了,例如可以执行 <code>/usr/bin/vacuumdb --all --analyze-in-stages --jobs=$(nproc)</code> 来提升升级后的查询性能。</p>
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p><a href="https://wiki.archlinux.org/title/PostgreSQL#Upgrading_PostgreSQL">https://wiki.archlinux.org/title/PostgreSQL#Upgrading_PostgreSQL</a> <a href="#fnref:1" class="footnote-backref" role="doc-backlink">↩︎</a></p>
</li>
</ol>
</div>
-
GPG 简单入门
https://leanhe.dev/posts/2022.03.17.1/
Thu, 17 Mar 2022 21:37:17 +0800
https://leanhe.dev/posts/2022.03.17.1/
<p>本文操作环境为 Arch Linux, 在 Windows 下可以用 GUI 程序 Kleopatra 进行类似的操作。</p>
<p>首先我们来介绍一下 GPG , gpg 是一个密钥对,分为公钥和私钥。公钥用来加密和验证签名,私钥用来签名和解密。</p>
<p>当然还有其他的操作可以使用,比如利用 GPG 密钥来登陆 ssh ,但是这里就不先表述了。</p>
<p>而关于公私钥的使用,可以参考如下的图。</p>
<p><a href="https://idea-instructions.com/public-key/"><img src="https://idea-instructions.com/public-key.png" alt="publik key krypto"></a></p>
<p>如名字所述,公钥用来公开给其他人,然后私钥自己保留,绝对不要把私钥发送给别人。</p>
<h2 id="生成">生成</h2>
<p>可参见 <a href="https://leanhe.dev/posts/2021.08.04.1/">这篇部落格</a></p>
<h2 id="导出密钥">导出密钥</h2>
<p>密钥文件很重要,如果你需要备份密钥文件,请使用 <code>gpg -a --export-secret-keys <KEY_ID></code></p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-plain" data-lang="plain"><span style="display:flex;"><span>$ gpg -a --export-secret-keys 0000000000000000
</span></span><span style="display:flex;"><span>-----BEGIN PGP PRIVATE KEY BLOCK-----
</span></span><span style="display:flex;"><span>...
</span></span><span style="display:flex;"><span>-----END PGP PRIVATE KEY BLOCK-----
</span></span></code></pre></div><p><code>-a</code> 的参数,在下文加密中有叙述。</p>
<p>当然,你如果要把你的公钥给其他人,请使用 <code>gpg -a --export <KEY_ID></code></p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-plain" data-lang="plain"><span style="display:flex;"><span>$ gpg -a --export 0000000000000000
</span></span><span style="display:flex;"><span>-----BEGIN PGP PUBLIC KEY BLOCK-----
</span></span><span style="display:flex;"><span>...
</span></span><span style="display:flex;"><span>-----END PGP PUBLIC KEY BLOCK-----
</span></span></code></pre></div><p>把输出的一大串东西统统发给其他人就行了。</p>
<h2 id="导入公钥">导入公钥</h2>
<p>首先你需要取得对方的公钥文件,它一般情况下是这个格式。</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-plain" data-lang="plain"><span style="display:flex;"><span>-----BEGIN PGP PUBLIC KEY BLOCK-----
</span></span><span style="display:flex;"><span>...
</span></span><span style="display:flex;"><span>-----END PGP PUBLIC KEY BLOCK-----
</span></span></code></pre></div><h2 id="加密">加密</h2>
<p>可以加密任何文件,或者文本。前置条件是你有你需要加密的人的公钥。</p>
<p>输入 <code>gpg -ae</code> 根据提示操作即可。</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-plain" data-lang="plain"><span style="display:flex;"><span>$ gpg -ae
</span></span><span style="display:flex;"><span>You did not specify a user ID. (you may use "-r")
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>Current recipients:
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>Enter the user ID. End with an empty line: [email protected]
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>Current recipients:
</span></span><span style="display:flex;"><span>cv25519/0000000000000000 1145-01-04 "Test <[email protected]>"
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>Enter the user ID. End with an empty line:
</span></span><span style="display:flex;"><span>233
</span></span><span style="display:flex;"><span>-----BEGIN PGP MESSAGE-----
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>hF4D/00000000000000000000000000000000000000000000000000000000RQw
</span></span><span style="display:flex;"><span>uG00000000000000/00000000000000000000000000000000000000000000tGq
</span></span><span style="display:flex;"><span>000000000000000000000000000000000000000000000000000000000000000q
</span></span><span style="display:flex;"><span>a/000000000000000000008=
</span></span><span style="display:flex;"><span>=0007
</span></span><span style="display:flex;"><span>-----END PGP MESSAGE-----
</span></span></code></pre></div><p><code>-a</code> 参数用来输出非二进制,在控制台使用的情况下,输出二进制可能不是用户所想要的功能。而使用这个方式加密完文本之后,会有相对比较清楚的显示所以可以直接复制。</p>
<p>而 <code>-e</code> 参数用来告诉 GPG 进入加密模式。</p>
<p>注意:如果你加密了信息,但是你没有把你自己的 id 放到 recipients 中的话,你自己是无法解密你加密的这条讯息的。</p>
<h2 id="签名">签名</h2>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-plain" data-lang="plain"><span style="display:flex;"><span>$ gpg -sa
</span></span><span style="display:flex;"><span>test
</span></span><span style="display:flex;"><span>-----BEGIN PGP MESSAGE-----
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>00000000000000000000000000000000000000000000000000000000000000nq
</span></span><span style="display:flex;"><span>/000000000000000000000000000000000000000000000000000/+000000000x
</span></span><span style="display:flex;"><span>00000000000000000000000000000000000000000000000000000000000000==
</span></span><span style="display:flex;"><span>=vd3U
</span></span><span style="display:flex;"><span>-----END PGP MESSAGE-----
</span></span></code></pre></div><p><code>-s</code> 参数告诉 GPG 进入签名模式。</p>
<p>当然如果你只是想签名,而想让其他人直接看到被签名的消息,可以使用 <code>gpg --clear-sign</code> 。</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-plain" data-lang="plain"><span style="display:flex;"><span>$ gpg --clear-sign
</span></span><span style="display:flex;"><span>test
</span></span><span style="display:flex;"><span>-----BEGIN PGP SIGNED MESSAGE-----
</span></span><span style="display:flex;"><span>Hash: SHA512
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>test
</span></span><span style="display:flex;"><span>-----BEGIN PGP SIGNATURE-----
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>000000000000000000000000000000000000000000000000000000000+000000
</span></span><span style="display:flex;"><span>000000000000000000000000000000+000000000000000000000000000000000
</span></span><span style="display:flex;"><span>0000000000000000000000000000000=
</span></span><span style="display:flex;"><span>=kaxV
</span></span><span style="display:flex;"><span>-----END PGP SIGNATURE-----
</span></span></code></pre></div><p>这样你签名的文字就会直接显示在文字中。</p>
<p>当然,你如果需要在加密时附上你的签名,只要把 <code>-s</code> 附加到参数中就行。</p>
<h2 id="解密验证签名">解密/验证签名</h2>
<p>这两个都可以使用 <code>gpg -d</code> 来完成。</p>
<p>输入后,粘贴上想要解密/验证的文本。 gpg 会自动帮你处理。</p>
<h3 id="解密">解密</h3>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-plain" data-lang="plain"><span style="display:flex;"><span>$ gpg -d
</span></span><span style="display:flex;"><span>-----BEGIN PGP MESSAGE-----
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>0000/00000000000000000000000000000000000000000000000000000/00000
</span></span><span style="display:flex;"><span>0000000000000000000+000/00000000000/000000000000000000000000000/
</span></span><span style="display:flex;"><span>0000000000000000000000000000000000000000000000000000000000000000
</span></span><span style="display:flex;"><span>00000000000000000+00+00=
</span></span><span style="display:flex;"><span>=25q7
</span></span><span style="display:flex;"><span>-----END PGP MESSAGE-----
</span></span><span style="display:flex;"><span>gpg: encrypted with 255-bit ECDH key, ID 0000000000000000, created 1145-01-04
</span></span><span style="display:flex;"><span> "Test <[email protected]>"
</span></span><span style="display:flex;"><span>test
</span></span></code></pre></div><h3 id="验证签名">验证签名</h3>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-plain" data-lang="plain"><span style="display:flex;"><span>$ gpg -d
</span></span><span style="display:flex;"><span>-----BEGIN PGP MESSAGE-----
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>00000000000000000000000000000000000000000000000000000000000000nq
</span></span><span style="display:flex;"><span>/000000000000000000000000000000000000000000000000000/+000000000x
</span></span><span style="display:flex;"><span>00000000000000000000000000000000000000000000000000000000000000==
</span></span><span style="display:flex;"><span>=vd3U
</span></span><span style="display:flex;"><span>-----END PGP MESSAGE-----
</span></span><span style="display:flex;"><span>test
</span></span><span style="display:flex;"><span>gpg: Signature made Thu 04 Jan 1145 19:19:08 CST
</span></span><span style="display:flex;"><span>gpg: using EDDSA key 0000000000000000000000000000000000000000
</span></span><span style="display:flex;"><span>gpg: Good signature from "Test <[email protected]>" [ultimate]
</span></span><span style="display:flex;"><span>gpg: aka "Test <[email protected]>" [ultimate]
</span></span></code></pre></div><h2 id="信任">信任</h2>
<p>GPG 容易受到中间人攻击,请利用其他的方式确保你获得的 key 是对方实际正在使用的 key 。</p>
-
在 iPXE 上从网路安装 Debian
https://leanhe.dev/posts/2021.11.11.1/
Thu, 11 Nov 2021 13:01:47 +0800
https://leanhe.dev/posts/2021.11.11.1/
<!--看到这个标题可能会问为什么要在 iPXE 上装,直接装不就好了,但是如果是 Windows 的本机安装呢,是不是这样最简便了。-->
<p>由于安装的时候没有什么截图,本篇文章就不怎么配图了。</p>
<h2 id="准备工作">准备工作</h2>
<p>首先我们先重启系统,进入 iPXE 界面。</p>
<p>可能会走 netboot ,记得根据提示,按 <code>C-b</code> 来进入 Shell 。</p>
<p>如需手动设置 IP 的话,可以使用下列命令来设置 IP 和 DNS</p>
<pre tabindex="0"><code class="language-ipxe" data-lang="ipxe">set net0/ip 192.168.0.1
set net0/netmask 255.255.255.0
set net0/gateway 192.168.0.254
set dns 8.8.8.8
</code></pre><p>记得使用 <code>ifstat net0</code> 看看是不是 open ,如果不是的话,可以使用 <code>ifopen net0</code> 来使它连线上。<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup></p>
<h2 id="引导系统">引导系统</h2>
<p>这里我们拿的是 <a href="http://deb.debian.org/debian/dists/bullseye/main/installer-amd64/current/images/netboot/debian-installer/amd64/">Debian Bullseye</a> ,要记得,这里的地址只能使用 http 而非 https 哦。</p>
<p>手打 <code>base-url</code> 进去 shell ,如果不想打那么长的话可以自己搞一个 http server 存 <code>linux</code> 和 <code>initrd.gz</code> 两个文件,实测没有影响</p>
<pre tabindex="0"><code class="language-ipxe" data-lang="ipxe">set base-url http://deb.debian.org/debian/dists/bullseye/main/installer-amd64/current/images/netboot/debian-installer/amd64
</code></pre><p>然后告诉 iPXE 需要引导什么</p>
<pre tabindex="0"><code class="language-ipxe" data-lang="ipxe">kernel ${base-url}/linux console=ttyS1,115200n8 initrd=initrd.gz
initrd ${base-url}/initrd.gz
</code></pre><p>如果没有问题的话,应该会经过一段下载过程,然后显示 OK</p>
<p>这个时候输入 <code>boot</code> 回车后,如果马上出现 <code>Probing EDD (edd=off to disable)</code> ,则说明引导成功了。</p>
<p>如果像我昨天一开始引导 ubuntu 等很久的话,基本上就是引导失败了。</p>
<p>然后就会出现 Debian 的安装界面了,这时候按正常安装过程处理就好。</p>
<h3 id="参考资料">参考资料</h3>
<p><a href="https://gianarb.it/blog/first-journeys-with-netboot-ipxe">https://gianarb.it/blog/first-journeys-with-netboot-ipxe</a>
<a href="https://github.com/AdrianKoshka/ipxe-scripts/blob/master/boot/linux/debian.ipxe">https://github.com/AdrianKoshka/ipxe-scripts/blob/master/boot/linux/debian.ipxe</a>
<a href="https://wiki.debian.org/PXEBootInstall#Provide_the_boot_image">https://wiki.debian.org/PXEBootInstall#Provide_the_boot_image</a></p>
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p><a href="https://ipxe.org/cmd/ifstat">https://ipxe.org/cmd/ifstat</a> <a href="#fnref:1" class="footnote-backref" role="doc-backlink">↩︎</a></p>
</li>
</ol>
</div>
-
利用 ssh config 来控制 gpg agent 使用的 key
https://leanhe.dev/posts/2021.09.23.1/
Thu, 23 Sep 2021 19:55:01 +0800
https://leanhe.dev/posts/2021.09.23.1/
<p>在 ssh 密钥还是文件的时候,控制 ssh key 在哪个 host 使用不是一个特别困难的选项,但是当密钥文件换成了 key 呢?本文就来介绍一下这个情况下的控制密钥使用</p>
<p>本文适用在 ssh 其它 key 都可以被接受但是只有某个 key 能起作用的情况</p>
<p>原答案: <a href="https://serverfault.com/a/964317">StackExchange</a></p>
<h2 id="使用方法">使用方法</h2>
<p>首先先把 pub key 导出来,保存成文件</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-plain" data-lang="plain"><span style="display:flex;"><span>$ gpg --export-ssh-key 123456789012345678901234567890 > ~/.ssh/1234.pub
</span></span></code></pre></div><p>之后打开 <code>~/.ssh/config</code> 文件,添加上你需要利用这个 key 访问的域名,配置文件如下所示</p>
<pre tabindex="0"><code class="language-conf" data-lang="conf">host example.com
# hostname example.com
IdentityFile ~/.ssh/1234.pub
IdentitiesOnly yes
PasswordAuthentication no
PubkeyAuthentication yes
</code></pre><p>如果有需要一个 host 多 key 用的话,可以 host 后面写别名,而后在下面写 hostname 来做不同 key 的使用区别。</p>
-
使用 Yubikey 在 archlinux 下 login/sudo/ssh
https://leanhe.dev/posts/2021.08.12.1/
Thu, 12 Aug 2021 14:57:47 +0800
https://leanhe.dev/posts/2021.08.12.1/
<h2 id="处理登录事件">处理登录事件</h2>
<p>此处需要安装额外软体 <a href="https://archlinux.org/packages/community/x86_64/yubico-pam/">yubico-pam</a></p>
<p>Yubikey 可用两种方式来进行登录的验证,一种是在线验证,另一种是<a href="https://en.wikipedia.org/wiki/Challenge%E2%80%93response_authentication">Challenge-response</a>认证<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup>。</p>
<p>这里考虑到网络的情况,即在登录前可能没有网络,那可能会陷入无限循环状态。所以我选择了后者进行认证,其实也只是配置参数的区别而已。</p>
<p>按照它的配置,我们需要在 Yubikey 的 OTP 选项中,选择一个栏位用于 Challenge-response 。</p>
<p>这里我选择了使用短暂触碰的栏位,这样的话可以降低误触的概率(比你想要过认证但是输出了一大串 OTP 的无用字符好)</p>
<p>配置完成后,我们按照官方的教程的操作步骤进行操作。</p>
<p>首先新建一个目录用来存放配置文件。再使用它的 ykpamcfg 来生成验证所需要的东西。</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-plain" data-lang="plain"><span style="display:flex;"><span>$ mkdir ~/.yubico
</span></span><span style="display:flex;"><span>$ ykpamcfg -2 -v
</span></span><span style="display:flex;"><span>...
</span></span><span style="display:flex;"><span>Stored initial challenge and expected response in '/home/alice/.yubico/challenge-123456'.
</span></span></code></pre></div><p>此处的 <code>-2</code> 选项是选择哪个槽位来进行 Challenge-response ,如果配置的是短触的栏位,则使用 <code>-1</code> (或者干脆直接删掉这个参数) 。</p>
<p>官方教程中有可选的增强安全性的选项,这里也把相应的步骤写一下</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-plain" data-lang="plain"><span style="display:flex;"><span>$ sudo mkdir /var/yubico
</span></span><span style="display:flex;"><span>$ sudo chown root.root /var/yubico
</span></span><span style="display:flex;"><span>$ sudo chmod 700 /var/yubico
</span></span><span style="display:flex;"><span>$ ykpamcfg -2 -v
</span></span><span style="display:flex;"><span>...
</span></span><span style="display:flex;"><span>Stored initial challenge and expected response in '$HOME/.yubico/challenge-123456'.
</span></span><span style="display:flex;"><span>$ sudo mv ~/.yubico/challenge-123456 /var/yubico/alice-123456
</span></span><span style="display:flex;"><span>$ sudo chown root.root /var/yubico/alice-123456
</span></span><span style="display:flex;"><span>$ sudo chmod 600 /var/yubico/alice-123456
</span></span></code></pre></div><p><span style="color: red;"><strong>请在更改 pam 文件前,仔细斟酌能否回滚文件更改,以防出现无法登录的情况</strong></span></p>
<p>之后,在 pam 配置 <code>/etc/pam.d/systemd-login</code> 中添加一行</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-plain" data-lang="plain"><span style="display:flex;"><span>auth sufficient pam_yubico.so mode=challenge-response chalresp_path=/var/yubico
</span></span></code></pre></div><p>或者把 <code>sufficient</code> 改为 <code>required</code> 如果强制需要 yubikey 进行登录的话。</p>
<p>重启系统后就可以体验到新的配置了,如果有需要按 yubikey 上的按钮的话别忘记按。</p>
<h2 id="处理-sudo-事件">处理 sudo 事件</h2>
<p>sudo 的话这里需要用到它的 u2f ,这里同样也许要安装额外软体 <a href="https://archlinux.org/packages/community/x86_64/pam-u2f/">pam-u2f</a>。</p>
<p>首先生成 pam 的配置</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span>pamu2fcfg -o pam://<span style="color:#f92672">[</span>YOUR HOSTNAME<span style="color:#f92672">]</span> -i pam://<span style="color:#f92672">[</span>YOUR HOSTNAME<span style="color:#f92672">]</span> > ~/.config/Yubico/u2f_keys
</span></span></code></pre></div><p><span style="color: red;"><strong>请在更改 pam 文件前,额外另开另一个 root shell 以防出现修改失败无法回滚的情况</strong></span></p>
<p>而后在 <code>/etc/pam.d/sudo</code> 的第一行之前,添加这一行,并确认 <code>[YOUR HOSTNAME]</code> 已经替换成了您机器的 <code>hostname</code></p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-plain" data-lang="plain"><span style="display:flex;"><span>auth sufficient pam_u2f.so origin=pam://[YOUR HOSTNAME] appid=pam://[YOUR HOSTNAME]
</span></span></code></pre></div><p>保存后,执行 <code>sudo ls</code> ,你的 yubikey 应该会闪烁,触摸它一下即应该成功执行这个指令。</p>
<h2 id="配置-ssh-远程登录">配置 ssh 远程登录</h2>
<p>这里需要用到 GPG 的配置,具体就参考之前的部落格吧,因为使用的是 GPG 的 ssh key 来进行认证。</p>
<p>这里假设已经配置好了,我们首先拿一下它的公钥。</p>
<p>使用 <code>gpg --export-ssh-key <YOUR KEY ID></code> 应该能输出如下栏位,把公钥先添加到你想要链接的伺服器上。</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-plain" data-lang="plain"><span style="display:flex;"><span>$ gpg --export-ssh-key [MASKED]
</span></span><span style="display:flex;"><span>ssh-ed25519 [MASKED] openpgp:[MASKED]
</span></span></code></pre></div><p>这时的下一步,就是让 gpg-agent 接管 ssh-agent 来进行认证。</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-plain" data-lang="plain"><span style="display:flex;"><span>$ export SSH_AUTH_SOCK=$(gpgconf --list-dirs agent-ssh-socket)
</span></span></code></pre></div><p>当然还要配置一下 gpg-agent 的配置文件: <code>~/.gnupg/gpg-agent.conf</code> 加上一行</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-plain" data-lang="plain"><span style="display:flex;"><span>enable-ssh-support
</span></span></code></pre></div><p>然后把自己的 key id 添加到 <code>~/.gnupg/sshcontrol</code> , key id 可以用 <code>gpg -K --with-keygrip</code> 找到。</p>
<p>再启动 gpg-agent</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-plain" data-lang="plain"><span style="display:flex;"><span>$ gpgconf --launch gpg-agent
</span></span></code></pre></div><p>如果 gpg-agent 已经启动了,那我们需要 reload 一下 agent ,使用下列命令来 reload</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-plain" data-lang="plain"><span style="display:flex;"><span>$ gpg-connect-agent reloadagent /bye
</span></span></code></pre></div><p>此时使用 ssh 的话,应该就是使用你的 yubikey 上的 GPG key 进行认证的了。</p>
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p><a href="https://developers.yubico.com/yubico-pam/Authentication_Using_Challenge-Response.html">https://developers.yubico.com/yubico-pam/Authentication_Using_Challenge-Response.html</a> <a href="#fnref:1" class="footnote-backref" role="doc-backlink">↩︎</a></p>
</li>
</ol>
</div>
-
不怎么精简地添加 GPG key 到 Yubikey 教程
https://leanhe.dev/posts/2021.08.04.1/
Wed, 04 Aug 2021 22:21:59 +0800
https://leanhe.dev/posts/2021.08.04.1/
<p>年初的时候买了个 Yubikey 但是它一直在抽屉里面吃灰,也没好好用过几次,所以这次打算趁有空的时候,折腾一下这个东西。</p>
<p>太长不看的话,请直接往下拉到有关添加的章节。</p>
<h2 id="准备工作">准备工作</h2>
<p>首先我们要确定你的机子可以读出你插入的 Yubikey ,如果读不出来的话,可以参考一下官方的疑难解答。</p>
<p>我们需要初始化一下 Yubikey ,设置它的密码和管理员密码。</p>
<p>使用 <code>gpg --edit-card</code> 指令来进行一个智能卡的编辑。</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-plain" data-lang="plain"><span style="display:flex;"><span>$ gpg --edit-card
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>Reader ...........: Yubico YubiKey OTP FIDO CCID 00 00
</span></span><span style="display:flex;"><span>Application ID ...: [MASKED]
</span></span><span style="display:flex;"><span>Application type .: OpenPGP
</span></span><span style="display:flex;"><span>Version ..........: 3.4
</span></span><span style="display:flex;"><span>Manufacturer .....: Yubico
</span></span><span style="display:flex;"><span>Serial number ....: [MASKED]
</span></span><span style="display:flex;"><span>Name of cardholder: [not set]
</span></span><span style="display:flex;"><span>Language prefs ...: [not set]
</span></span><span style="display:flex;"><span>Salutation .......:
</span></span><span style="display:flex;"><span>URL of public key : [not set]
</span></span><span style="display:flex;"><span>Login data .......: user
</span></span><span style="display:flex;"><span>Signature PIN ....: not forced
</span></span><span style="display:flex;"><span>Key attributes ...: ed25519 cv25519 ed25519
</span></span><span style="display:flex;"><span>Max. PIN lengths .: 127 127 127
</span></span><span style="display:flex;"><span>PIN retry counter : 3 3 3
</span></span><span style="display:flex;"><span>Signature counter : 0
</span></span><span style="display:flex;"><span>KDF setting ......: off
</span></span></code></pre></div><p>一般显示大概会如上所示,这时候我们需要进行一个密码重设的操作(如果重设过密码,请跳过这步)</p>
<p>预设的密码是 <code>123456</code> ,预设的管理密码是 <code>12345678</code> 。</p>
<p>首先我先输入 <code>admin</code> 允许管理员选项,之后输入 <code>passwd</code> 按照操作进行重设。</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-plain" data-lang="plain"><span style="display:flex;"><span>gpg/card> admin
</span></span><span style="display:flex;"><span>Admin commands are allowed
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>gpg/card> passwd
</span></span><span style="display:flex;"><span>gpg: OpenPGP card no. [MASKED] detected
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>1 - change PIN
</span></span><span style="display:flex;"><span>2 - unblock PIN
</span></span><span style="display:flex;"><span>3 - change Admin PIN
</span></span><span style="display:flex;"><span>4 - set the Reset Code
</span></span><span style="display:flex;"><span>Q - quit
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>Your selection?
</span></span></code></pre></div><p>输入 1 来更改预设密码,输入 3 来更改管理员密码。 2 的选项是你在忘记第一个密码的时候给你解锁用的,所以务必记得管理员密码。 4 的话可以做一个恢复代码的设置,也是用来重设的。</p>
<p>设置好密码后,就可以开始进行下一步了。</p>
<h2 id="生成-gpg-key">生成 gpg key</h2>
<p>这里只讲解 ed25519 的生成, RSA 的方式应该差不多。如果已经有 key 则可以考虑跳过这一步。</p>
<p>首先我们执行 <code>gpg --expert --full-generate-key</code></p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-plain" data-lang="plain"><span style="display:flex;"><span>$ gpg --expert --full-generate-key
</span></span><span style="display:flex;"><span>gpg (GnuPG) 2.2.29; Copyright (C) 2021 Free Software Foundation, Inc.
</span></span><span style="display:flex;"><span>This is free software: you are free to change and redistribute it.
</span></span><span style="display:flex;"><span>There is NO WARRANTY, to the extent permitted by law.
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>Please select what kind of key you want:
</span></span><span style="display:flex;"><span> (1) RSA and RSA (default)
</span></span><span style="display:flex;"><span> (2) DSA and Elgamal
</span></span><span style="display:flex;"><span> (3) DSA (sign only)
</span></span><span style="display:flex;"><span> (4) RSA (sign only)
</span></span><span style="display:flex;"><span> (7) DSA (set your own capabilities)
</span></span><span style="display:flex;"><span> (8) RSA (set your own capabilities)
</span></span><span style="display:flex;"><span> (9) ECC and ECC
</span></span><span style="display:flex;"><span> (10) ECC (sign only)
</span></span><span style="display:flex;"><span> (11) ECC (set your own capabilities)
</span></span><span style="display:flex;"><span> (13) Existing key
</span></span><span style="display:flex;"><span> (14) Existing key from card
</span></span><span style="display:flex;"><span>Your selection?
</span></span></code></pre></div><p>这里选择 9</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-plain" data-lang="plain"><span style="display:flex;"><span>Please select which elliptic curve you want:
</span></span><span style="display:flex;"><span> (1) Curve 25519
</span></span><span style="display:flex;"><span> (3) NIST P-256
</span></span><span style="display:flex;"><span> (4) NIST P-384
</span></span><span style="display:flex;"><span> (5) NIST P-521
</span></span><span style="display:flex;"><span> (6) Brainpool P-256
</span></span><span style="display:flex;"><span> (7) Brainpool P-384
</span></span><span style="display:flex;"><span> (8) Brainpool P-512
</span></span><span style="display:flex;"><span> (9) secp256k1
</span></span><span style="display:flex;"><span>Your selection? 1
</span></span></code></pre></div><p>这里选择 1 使用 Cv25519 做加密工作。因为 Ed25519 没办法执行加密操作,只能靠 Cv25519 代劳。</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-plain" data-lang="plain"><span style="display:flex;"><span>Please specify how long the key should be valid.
</span></span><span style="display:flex;"><span> 0 = key does not expire
</span></span><span style="display:flex;"><span> <n> = key expires in n days
</span></span><span style="display:flex;"><span> <n>w = key expires in n weeks
</span></span><span style="display:flex;"><span> <n>m = key expires in n months
</span></span><span style="display:flex;"><span> <n>y = key expires in n years
</span></span><span style="display:flex;"><span>Key is valid for? (0)
</span></span><span style="display:flex;"><span>Key does not expire at all
</span></span><span style="display:flex;"><span>Is this correct? (y/N) y
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>GnuPG needs to construct a user ID to identify your key.
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>Real name: user
</span></span><span style="display:flex;"><span>Email address: [email protected]
</span></span><span style="display:flex;"><span>Comment:
</span></span><span style="display:flex;"><span>You selected this USER-ID:
</span></span><span style="display:flex;"><span> "user <[email protected]>"
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>Change (N)ame, (C)omment, (E)mail or (O)kay/(Q)uit? O
</span></span><span style="display:flex;"><span>We need to generate a lot of random bytes. It is a good idea to perform
</span></span><span style="display:flex;"><span>some other action (type on the keyboard, move the mouse, utilize the
</span></span><span style="display:flex;"><span>disks) during the prime generation; this gives the random number
</span></span><span style="display:flex;"><span>generator a better chance to gain enough entropy.
</span></span><span style="display:flex;"><span>We need to generate a lot of random bytes. It is a good idea to perform
</span></span><span style="display:flex;"><span>some other action (type on the keyboard, move the mouse, utilize the
</span></span><span style="display:flex;"><span>disks) during the prime generation; this gives the random number
</span></span><span style="display:flex;"><span>generator a better chance to gain enough entropy.
</span></span><span style="display:flex;"><span>gpg: key D2F475D29D00AB02 marked as ultimately trusted
</span></span><span style="display:flex;"><span>gpg: revocation certificate stored as '479F2CF8B814423F45A58B05D2F475D29D00AB02.rev'
</span></span><span style="display:flex;"><span>public and secret key created and signed.
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>pub ed25519 2021-08-04 [SC]
</span></span><span style="display:flex;"><span> 479F2CF8B814423F45A58B05D2F475D29D00AB02
</span></span><span style="display:flex;"><span>uid user <[email protected]>
</span></span><span style="display:flex;"><span>sub cv25519 2021-08-04 [E]
</span></span></code></pre></div><p>然后预设的话会生成一个 primary key 和一个 sub key 。我们还需要一个 key 来进行一个 ssh 的认证,所以这里在再添加一个 sub key (或者更改 primary key 来支援认证也可以)</p>
<h2 id="添加-sub-key">添加 sub key</h2>
<p>使用 <code>gpg --expert --edit-key <YOUR KEY ID></code> 来进行一个key的编辑。</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-plain" data-lang="plain"><span style="display:flex;"><span>$ gpg --expert --edit-key 479F2CF8B814423F45A58B05D2F475D29D00AB02
</span></span><span style="display:flex;"><span>gpg (GnuPG) 2.2.29; Copyright (C) 2021 Free Software Foundation, Inc.
</span></span><span style="display:flex;"><span>This is free software: you are free to change and redistribute it.
</span></span><span style="display:flex;"><span>There is NO WARRANTY, to the extent permitted by law.
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>Secret key is available.
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>sec ed25519/D2F475D29D00AB02
</span></span><span style="display:flex;"><span> created: 2021-08-04 expires: never usage: SC
</span></span><span style="display:flex;"><span> trust: ultimate validity: ultimate
</span></span><span style="display:flex;"><span>ssb cv25519/CE765FB433B0B0DB
</span></span><span style="display:flex;"><span> created: 2021-08-04 expires: never usage: E
</span></span><span style="display:flex;"><span>[ultimate] (1). user <[email protected]>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>gpg>
</span></span></code></pre></div><p>这时候输入 <code>addkey</code></p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-plain" data-lang="plain"><span style="display:flex;"><span>gpg>addkey
</span></span><span style="display:flex;"><span>Please select what kind of key you want:
</span></span><span style="display:flex;"><span> (3) DSA (sign only)
</span></span><span style="display:flex;"><span> (4) RSA (sign only)
</span></span><span style="display:flex;"><span> (5) Elgamal (encrypt only)
</span></span><span style="display:flex;"><span> (6) RSA (encrypt only)
</span></span><span style="display:flex;"><span> (7) DSA (set your own capabilities)
</span></span><span style="display:flex;"><span> (8) RSA (set your own capabilities)
</span></span><span style="display:flex;"><span> (10) ECC (sign only)
</span></span><span style="display:flex;"><span> (11) ECC (set your own capabilities)
</span></span><span style="display:flex;"><span> (12) ECC (encrypt only)
</span></span><span style="display:flex;"><span> (13) Existing key
</span></span><span style="display:flex;"><span> (14) Existing key from card
</span></span><span style="display:flex;"><span>Your selection?
</span></span></code></pre></div><p>选择 11</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-plain" data-lang="plain"><span style="display:flex;"><span>Your selection? 11
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>Possible actions for a ECDSA/EdDSA key: Sign Authenticate
</span></span><span style="display:flex;"><span>Current allowed actions: Sign
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> (S) Toggle the sign capability
</span></span><span style="display:flex;"><span> (A) Toggle the authenticate capability
</span></span><span style="display:flex;"><span> (Q) Finished
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>Your selection?
</span></span></code></pre></div><p>然后输入一个 <code>A</code> 来允许使用 key 进行认证。</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-plain" data-lang="plain"><span style="display:flex;"><span>Your selection? A
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>Possible actions for a ECDSA/EdDSA key: Sign Authenticate
</span></span><span style="display:flex;"><span>Current allowed actions: Sign Authenticate
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> (S) Toggle the sign capability
</span></span><span style="display:flex;"><span> (A) Toggle the authenticate capability
</span></span><span style="display:flex;"><span> (Q) Finished
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>Your selection?
</span></span></code></pre></div><p>然后 Q 即可,接着按提示操作。</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-plain" data-lang="plain"><span style="display:flex;"><span>sec ed25519/D2F475D29D00AB02
</span></span><span style="display:flex;"><span> created: 2021-08-04 expires: never usage: SC
</span></span><span style="display:flex;"><span> trust: ultimate validity: ultimate
</span></span><span style="display:flex;"><span>ssb cv25519/CE765FB433B0B0DB
</span></span><span style="display:flex;"><span> created: 2021-08-04 expires: never usage: E
</span></span><span style="display:flex;"><span>ssb ed25519/927366AF0BD16702
</span></span><span style="display:flex;"><span> created: 2021-08-04 expires: never usage: A
</span></span><span style="display:flex;"><span>[ultimate] (1). user <[email protected]>
</span></span></code></pre></div><p>最后应该会如上显示,接着我们就需要把这个 GPG key 转移到 Yubikey 里面。</p>
<h2 id="添加-gpg-key-到-yubikey">添加 GPG key 到 Yubikey</h2>
<p><span style="color: red;"><strong>请注意,这个指令会导致你的 secret key 永久地移动到 Yubikey 中,请事先做好备份</strong></span></p>
<p>如果您没有在 GnuPG edit key 界面,请打开它。使用 <code>gpg --edit-key <YOUR KEY ID></code> 来打开界面。</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-plain" data-lang="plain"><span style="display:flex;"><span>$ gpg --expert --edit-key 479F2CF8B814423F45A58B05D2F475D29D00AB02
</span></span><span style="display:flex;"><span>gpg (GnuPG) 2.2.29; Copyright (C) 2021 Free Software Foundation, Inc.
</span></span><span style="display:flex;"><span>This is free software: you are free to change and redistribute it.
</span></span><span style="display:flex;"><span>There is NO WARRANTY, to the extent permitted by law.
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>Secret key is available.
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>sec ed25519/D2F475D29D00AB02
</span></span><span style="display:flex;"><span> created: 2021-08-04 expires: never usage: SC
</span></span><span style="display:flex;"><span> trust: ultimate validity: ultimate
</span></span><span style="display:flex;"><span>ssb cv25519/CE765FB433B0B0DB
</span></span><span style="display:flex;"><span> created: 2021-08-04 expires: never usage: E
</span></span><span style="display:flex;"><span>ssb ed25519/927366AF0BD16702
</span></span><span style="display:flex;"><span> created: 2021-08-04 expires: never usage: A
</span></span><span style="display:flex;"><span>[ultimate] (1). user <[email protected]>
</span></span></code></pre></div><p>稍加讲解一下 usage 代表的意思, S 代表 sign , E 代表 encrypt , A 代表 authenticate 。</p>
<p>然后我们把 key 复制进卡里,首先选择你需要的 key ,使用 <code>key <id></code> id 为上面显示的 key 的顺序。</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-plain" data-lang="plain"><span style="display:flex;"><span>gpg> key 1
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>sec ed25519/D2F475D29D00AB02
</span></span><span style="display:flex;"><span> created: 2021-08-04 expires: never usage: SC
</span></span><span style="display:flex;"><span> trust: ultimate validity: ultimate
</span></span><span style="display:flex;"><span>ssb cv25519/CE765FB433B0B0DB
</span></span><span style="display:flex;"><span> created: 2021-08-04 expires: never usage: E
</span></span><span style="display:flex;"><span>ssb* ed25519/927366AF0BD16702
</span></span><span style="display:flex;"><span> created: 2021-08-04 expires: never usage: A
</span></span><span style="display:flex;"><span>[ultimate] (1). user <[email protected]>
</span></span></code></pre></div><p>选择成功后,会在上面显示一个星号。</p>
<p><span style="color: red;"><strong>再提醒一次,这个指令会导致你的 secret key 永久地移动到 Yubikey 中,请事先做好备份</strong></span></p>
<p>然后输入 <code>keytocard</code> 后,根据提示操作。</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-plain" data-lang="plain"><span style="display:flex;"><span>gpg> keytocard
</span></span><span style="display:flex;"><span>Please select where to store the key:
</span></span><span style="display:flex;"><span> (1) Signature key
</span></span><span style="display:flex;"><span> (3) Authentication key
</span></span><span style="display:flex;"><span>Your selection?
</span></span></code></pre></div><p>如此反复之后,应该就可以从 <code>gpg --edit-card</code> 中看到 Yubikey 中的 key 的情况。</p>
<h2 id="导出-gpg-ssh-key">导出 GPG ssh key</h2>
<p>使用 <code>gpg --export-ssh-key <YOUR KEY ID></code> 即可</p>
<h2 id="可选的安全选项1">可选的安全选项<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup></h2>
<p>建议开启的选项(可自定义),需要 <a href="https://developers.yubico.com/yubikey-manager/">Yubikey Manager CLI</a></p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-plain" data-lang="plain"><span style="display:flex;"><span>ykman openpgp keys set-touch aut off
</span></span><span style="display:flex;"><span>ykman openpgp keys set-touch sig on
</span></span><span style="display:flex;"><span>ykman openpgp keys set-touch enc on
</span></span></code></pre></div><p>这几个选项要求 Yubikey 在使用 gpg 时,是否需要进行物理的触碰。按需开启。</p>
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p><a href="https://zach.codes/ultimate-yubikey-setup-guide/">https://zach.codes/ultimate-yubikey-setup-guide/</a> <a href="#fnref:1" class="footnote-backref" role="doc-backlink">↩︎</a></p>
</li>
</ol>
</div>
-
Systemd 在系统启动后立即执行用户态下的服务
https://leanhe.dev/posts/2021.07.28.1/
Wed, 28 Jul 2021 15:26:18 +0800
https://leanhe.dev/posts/2021.07.28.1/
<p>以前都比较愚蠢,一直以为只有系统服务才可以做到开机后运行,不然就只有用户登录后才可以运行。一直到最近迁移服务器了才开始重新研究这一部分的内容。</p>
<p>先把命令拿出来,只要这一行指令,系统引导后就会自动帮你执行你需要开机执行的程序</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span>sudo loginctl enable-linger <span style="color:#66d9ef">$(</span>whoami<span style="color:#66d9ef">)</span>
</span></span></code></pre></div><p>当然也可以不写那个 <code>$(whoami)</code> 或者手动改成你需要的用户名。</p>
<h2 id="服务配置">服务配置</h2>
<p>对于系统服务,用户态服务的配置稍有不同,所以大部分提供给系统服务的服务文件是不能直接使用的。</p>
<p>首先便是 Install 这一块,系统服务一般写的是 <code>multi-user.target</code></p>
<pre tabindex="0"><code class="language-conf" data-lang="conf">[Install]
WantedBy=multi-user.target
</code></pre><p>而用户服务,则需要改写成 <code>default.target</code></p>
<pre tabindex="0"><code class="language-conf" data-lang="conf">[Install]
WantedBy=default.target
</code></pre><p>另,用户的服务也不能依赖系统的服务。因为用户服务的 <code>systemd</code> 主进程和系统服务的 <code>systemd</code> 主进程是相互独立的,所以没办法用显式依赖的方式来只依赖声明。但是依赖 target 还是可以的。</p>
<h2 id="配置服务自动启动">配置服务自动启动</h2>
<p>只需要使用下列命令就行,记得用户态的服务务必加上 <code>--user</code></p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span>systemctl --user enable <your service>.service
</span></span></code></pre></div><h2 id="致谢">致谢</h2>
<p>特别感谢 <a href="https://billchen.bid/">Billchen</a> 为我提供了自动启动的思路</p>
-
一个不完整的 VPS 安装 Archlinux 教程
https://leanhe.dev/posts/2021.07.24.1/
Sat, 24 Jul 2021 22:06:48 +0800
https://leanhe.dev/posts/2021.07.24.1/
<p>Arch linux 似乎需要 700MiB+ 的记忆体空间才能成功引导 Arch linux iso ,如果不满足这个需求的话,请使用 <a href="https://github.com/KunoiSayami/vps2arch">vps2arch</a> (当然这个版本是我自己修改过的,可以用原来的版本)。</p>
<p>如果 VNC 不方便使用,比如不方便粘贴之类的,可以用 ssh 来链接(请记得设置密码)。</p>
<p>我们引导上之后,先确认一下网络有连接上,可以用 <code>ping</code> 指令来测试。一般情况下,通过 DHCP 获得 ip 地址的机器都可以正常工作。</p>
<p>而后第一件事就是磁盘分区,因为我用 <code>fdisk</code> 所以我这边就不过多叙述了,这个可以有很多的方法去处理分区。</p>
<p>一般情况下,我们应该设置至少一个分区为 <code>EFI system</code> ,这样才可以正确通过 <a href="https://en.wikipedia.org/wiki/Unified_Extensible_Firmware_Interface">UEFI</a> 来进行引导。</p>
<p>分区完成后,应该进行格式化。</p>
<p>分区处理好以后,我们需要先对其进行挂载。</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span>mount /dev/sda2 /mnt
</span></span><span style="display:flex;"><span>mkdir /mnt/boot
</span></span><span style="display:flex;"><span>mount /dev/sda1 /mnt/boot
</span></span></code></pre></div><h2 id="安装系统">安装系统</h2>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span>pacstrap /mnt linux linux-firmware base base-devel vim
</span></span></code></pre></div><p><code>base-devel</code> 和 <code>vim</code> 都是可选安装的。</p>
<p>过一会儿应该就跑完了,这时候我们把 <code>fstab</code> 文件更新一下</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span>genfstab -U /mnt >> /mnt/etc/fstab
</span></span></code></pre></div><h2 id="系统调整">系统调整</h2>
<p>我们先 chroot 进去系统</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span>arch-chroot /mnt
</span></span></code></pre></div><p>先设置一下时区</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span>ln -sf /usr/share/zoneinfo/Asia/Taipei /etc/localtime
</span></span></code></pre></div><p>配置一下本地化</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span>sed -i <span style="color:#e6db74">'s|#en_GB.UTF-8|en_GB.UTF-8|g'</span> /etc/locale.gen
</span></span><span style="display:flex;"><span>locale-gen
</span></span></code></pre></div><p>然后在 <code>/etc/locale.conf</code> 中写上 <code>LANG=en_GB.UTF-8</code></p>
<h2 id="安装引导">安装引导</h2>
<p>这里只写 UEFI 下的引导安装,为了验证是 UEFI 环境,请确认 <code>/sys/firmware/efi/efivars/</code> 资料夹是否存在。</p>
<p>这里使用 <a href="https://wiki.archlinux.org/title/systemd-boot">systemd-boot</a> 来进行引导。</p>
<p>首先我们先使用安装引导程序命令</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span>bootctl install
</span></span></code></pre></div><p>如果前面的分区类型没有问题,那这里应该不会有任何报错。如有报错,请确保分区类型和挂载位置是否正确。</p>
<p>接着我们就可以编辑引导文件了,在 chroot 环境中,引导文件应在 <code>/efi/loader</code> 此处的 <code>/efi</code> 按照上文配置应为 <code>/boot</code>。</p>
<p>编辑引导菜单文件 <code>/boot/loader/loader.conf</code> ,设定预设选项为 <code>arch.conf</code></p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-plain" data-lang="plain"><span style="display:flex;"><span>default arch
</span></span><span style="display:flex;"><span>timeout 3
</span></span></code></pre></div><p>新建引导选项 <code>/boot/loader/entries/arch.conf</code></p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-plain" data-lang="plain"><span style="display:flex;"><span>title Arch Linux
</span></span><span style="display:flex;"><span>linux /vmlinuz-linux
</span></span><span style="display:flex;"><span>initrd /intel-ucode.img
</span></span><span style="display:flex;"><span>initrd /initramfs-linux.img
</span></span><span style="display:flex;"><span>options root="UUID=xxx-xxx-xxx" rw
</span></span></code></pre></div><p>ucode 可以按照需求安装, AMD 系统请安装 <code>amd-ucode</code> 。 UUID 可以通过命令 <code>blkid</code> 获得,请使用 <code>UUID</code> 而非 <code>PARTUUID</code> 。</p>
<p>可选添加菜单 <code>fallback.conf</code> ,这里就不叙述了。</p>
<p>用 <key>Ctrl</key> + <key>D</key> 来退出 chroot 环境,输入 <code>reboot</code> 即可重启系统。</p>
<p>如果安装顺利的话,应该会自动引导到 Arch linux 了。</p>
<p>本文如有未尽事宜,会通过水其他部落格篇目的形式来进行补充。</p>
-
我的 VS Code 为什么不能下载扩展软件
https://leanhe.dev/posts/2021.07.22.1/
Thu, 22 Jul 2021 19:52:45 +0800
https://leanhe.dev/posts/2021.07.22.1/
<p>好久没水部落格了,那我今天就来水一篇部落格吧。</p>
<p>大概是我新装了 Arch linux 在笔记本上后,今天打算用 VS Code 写部落格,然后想看一下扩展应用里面有什么好玩的东西来的,然后就发现我无论如何都没办法刷新出市场。</p>
<p>那怎么办呢,挂了代理也不行,那大概就不是我的问题了。</p>
<p>于是去 GitHub 上面找了一下有没有人跟我有同样情况的,首先找到了<a href="https://github.com/microsoft/vscode/issues/81115">#81115</a>。</p>
<p>这条没有什么用,但是里面说了<a href="https://github.com/microsoft/vscode/issues/81115#issuecomment-533446002">一句</a>,可以从 Developer tools (<key>F1</key> -> Search <code>Developer tools</code> 或 <key>Ctrl</key> + <key>Shift</key> + <key>I</key>) 里面来看网络请求。</p>
<p>然后我就发现这东西不对劲,怎么是 CORS 的问题</p>
<p><a href="https://img.shabiwangyou.com/images/2021/07/22/image.png"><img src="https://img.shabiwangyou.com/images/2021/07/22/image.png" alt="VS code 中 Developer tools spawn介面"></a></p>
<p>那这个简单了,直接拿 CORS 去 GitHub 查即可,于是找到了这个 <a href="https://github.com/microsoft/vscode/issues/128583">#128583</a></p>
<p>然后顺藤摸瓜找到了这个<a href="https://github.com/VSCodium/vscodium/issues/746#issuecomment-881049046">解决方案</a></p>
<p>应用解决方案后,重启 VS Code 即可正常工作。</p>
-
搭建 Git 伺服器并利用 cgit 作为前端
https://leanhe.dev/posts/2021.05.02.1/
Sun, 02 May 2021 14:22:48 +0800
https://leanhe.dev/posts/2021.05.02.1/
<p>本文预设环境为: <a href="https://archlinux.org/">Arch linux x86_64</a> with <a href="https://archlinux.org/packages/extra/x86_64/nginx/">Nginx</a></p>
<h2 id="搭建-git-伺服器">搭建 Git 伺服器</h2>
<p>首先我们需要安装 <a href="https://archlinux.org/packages/extra/x86_64/git/">git</a> 包</p>
<p>本文这里只阐述利用 ssh 连线的 git 伺服器</p>
<p>由于系统预设已经添加了 <code>git</code> 用户,这里我们只需要做两个小修改即可</p>
<p>新建一个目录 <code>/srv/git</code> 并且记得把所有者设置为 <code>git</code></p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-plain" data-lang="plain"><span style="display:flex;"><span>$ sudo mkdir /srv/git
</span></span><span style="display:flex;"><span>$ sudo chown git:git /srv/git
</span></span></code></pre></div><p>然后修改 <code>/etc/passwd</code> 将其 home 目录从 <code>/</code> 设置成 <code>/srv/git</code></p>
<p>接着编辑 <code>/usr/lib/systemd/system/[email protected]</code></p>
<p>在 <code>ExecStart</code> 栏位把 <code>--base-path</code> 的参数修改为 <code>/srv/git</code></p>
<p>全部修改完以后,我们就可以测试一下</p>
<p>首先切换到目录 <code>/srv/git</code> 中(如果没有权限的话,可以尝试用 <code>root</code> 用户操作(<code>sudo -i</code>))</p>
<p>使用下列命令新建一个 repo</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-plain" data-lang="plain"><span style="display:flex;"><span># mkdir test.git
</span></span><span style="display:flex;"><span># cd test.git
</span></span><span style="display:flex;"><span># git init --bare
</span></span><span style="display:flex;"><span># cd ..
</span></span><span style="display:flex;"><span># chown -R git:git test.git
</span></span></code></pre></div><p>而后本地如果要 clone 的话,首先将自己的 ssh key 添加到 <code>/srv/git/.ssh/authorized_keys</code> 中</p>
<p>使用 <code>git clone git@<your host name>:test.git</code> 来 clone</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-plain" data-lang="plain"><span style="display:flex;"><span>$ git clone git@githost:test.git
</span></span><span style="display:flex;"><span>Cloning into 'test'...
</span></span><span style="display:flex;"><span>warning: You appear to have cloned an empty repository.
</span></span></code></pre></div><p>一般可以 clone 的话,其他的操作也不会有什么问题</p>
<h2 id="配置-cgit">配置 cgit</h2>
<p>首先安装 <a href="https://archlinux.org/packages/community/x86_64/cgit/">cgit</a></p>
<p>它有几个可选的依赖,可以自行选择是否需要安装</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-plain" data-lang="plain"><span style="display:flex;"><span>Optional dependencies for cgit
</span></span><span style="display:flex;"><span> groff: about page using man page syntax
</span></span><span style="display:flex;"><span> python-pygments: syntax highlighting support
</span></span><span style="display:flex;"><span> python-docutils: about page formatted with reStructuredText
</span></span><span style="display:flex;"><span> python-markdown: about page formatted with markdown
</span></span><span style="display:flex;"><span> gzip: gzip compressed snapshots
</span></span><span style="display:flex;"><span> bzip2: bzip2 compressed snapshots
</span></span><span style="display:flex;"><span> lzip: lzip compressed snapshots
</span></span><span style="display:flex;"><span> xz: xz compressed snapshots
</span></span><span style="display:flex;"><span> zstd: zstd compressed snapshots
</span></span><span style="display:flex;"><span> mime-types: serve file with correct content-type header
</span></span></code></pre></div><h3 id="配置-nginx">配置 Nginx</h3>
<p>安装 <a href="https://archlinux.org/packages/community/x86_64/fcgiwrap/">fcgiwrap</a></p>
<p>然后启用 <code>fcgiwrap.socket</code></p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-plain" data-lang="plain"><span style="display:flex;"><span>$ sudo systemctl enable --now fcgiwrap.socket
</span></span></code></pre></div><p>以下是参考的 nginx server 配置文件</p>
<p>如需启用 ssl 可以参考<a href="https://leanhe.dev/posts/2021.04.17.1">往期的文章</a></p>
<pre tabindex="0"><code>server {
listen 80;
server_name git.example.com;
root /usr/share/webapps/cgit;
try_files $uri @cgit;
location @cgit {
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root/cgit.cgi;
fastcgi_param PATH_INFO $uri;
fastcgi_param QUERY_STRING $args;
fastcgi_param HTTP_HOST $server_name;
fastcgi_pass unix:/run/fcgiwrap.sock;
}
}
</code></pre><p>配置完成后应使用 <code>nginx -t</code> 测试配置文件的有效性。</p>
<h3 id="配置-cgitrc">配置 cgitrc</h3>
<p>我这里直接使用<a href="https://wiki.archlinux.org/title/Cgit#Basic_configuration">预设配置</a></p>
<p>配置文件: <code>/etc/cgitrc</code> ,由于这个文件是写死的,出费用环境变数改,或者重新编译 cgit ,所以我就使用预设的路径了。</p>
<pre tabindex="0"><code class="language-conf" data-lang="conf">#
# cgit config
#
css=/cgit.css
logo=/cgit.png
# Following lines work with the above Apache config
#css=/cgit-css/cgit.css
#logo=/cgit-css/cgit.png
# Following lines work with the above Lighttpd config
#css=/cgit/cgit.css
#logo=/cgit/cgit.png
# Allow http transport git clone
#enable-http-clone=0
# if you do not want that webcrawler (like google) index your site
robots=noindex, nofollow
# if cgit messes up links, use a virtual-root. For example, cgit.example.org/ has this value:
virtual-root=/
</code></pre><p>我们为了简洁名了,把仓库的设置独立出来,放在其他的文件。只需要再上面的配置文件中添加一行 <code>include=/path/to/repos</code> 即可</p>
<pre tabindex="0"><code class="language-conf" data-lang="conf">#
# List of repositories.
# This list could be kept in a different file (e.g. '/etc/cgitrepos')
# and included like this:
# include=/etc/cgitrepos
#
repo.url=test
repo.path=/srv/git/test.git
repo.desc=This is my git repository
</code></pre><p>填写这个配置后访问配置的域名,应该就可以看到已经将仓库内容 show 出来了。</p>
<p><a href="https://git.leanhe.dev"><img src="https://img.shabiwangyou.com/images/2021/05/16/git.leanhe.dev_.png" alt="git.leanhe.dev 显示效果"></a></p>
<h2 id="鉴权">鉴权</h2>
<p>当然,我们配置的这个是任何人都可以访问的。之所以这篇文章鸽了这么久,因为我在写鉴权的程式。</p>
<p>如需简单的鉴权,可以参考这个项目 <a href="https://github.com/KunoiSayami/cgit-simple-authentication">cgit-simple-authentication</a> ,这个项目提供了简单的对仓库的分权限控制。</p>
-
利用 dd + ssh 备份分区到其他电脑
https://leanhe.dev/posts/2021.04.26.1/
Mon, 26 Apr 2021 23:04:34 +0800
https://leanhe.dev/posts/2021.04.26.1/
<p>本文所用到的软体,在 <a href="https://archlinux.org/download/">Arch Linux ISO</a> 中均预设包含,无需另行安装</p>
<h2 id="启用-sshd">启用 sshd</h2>
<p>由于 sshd 在 iso 上也是预装的,所以只需要一行启动指令即可</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-plain" data-lang="plain"><span style="display:flex;"><span># systemd start sshd
</span></span></code></pre></div><p>记得修改一下 root 用户的密码,不然是连不上的</p>
<h2 id="传输文件到本地">传输文件到本地</h2>
<p>这里假设需要备份目标伺服器的 <code>/dev/sda</code> ,这里使用 <a href="https://github.com/facebook/zstd"><code>zstd</code></a> 进行传输前压缩<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup></p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-plain" data-lang="plain"><span style="display:flex;"><span>$ ssh <user name>@<host> "dd bs=1M iflag=fullblock if=/dev/sda status=progress | zstd -16" > sda.zst.img
</span></span></code></pre></div><h2 id="传输文件回伺服器">传输文件回伺服器</h2>
<p>这里按照之前的假设原样回档</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-plain" data-lang="plain"><span style="display:flex;"><span>$ ssh <user name>@<host> "zstdcat -- | dd bs=1M iflag=fullblock of=/dev/sda status=progress " < sda.zst.img
</span></span></code></pre></div><div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p><a href="https://unix.stackexchange.com/a/492779">https://unix.stackexchange.com/a/492779</a> <a href="#fnref:1" class="footnote-backref" role="doc-backlink">↩︎</a></p>
</li>
</ol>
</div>
-
进入 Rescue mode 重置 root 用户密码
https://leanhe.dev/posts/2021.04.22.1/
Thu, 22 Apr 2021 00:53:25 +0800
https://leanhe.dev/posts/2021.04.22.1/
<p>由于我 VPS 网络配置的问题,从快照中恢复我的主机并不能主动连上网,但是当我满怀期望地打算连上 VNC 大干一番后,发现我居然没有设置 root 的密码</p>
<p>反正我试了一下密码进不去,就大概猜到我用了脚本之后可能把密码重设成什么东西的 hash 了但是我忘记是什么的hash了,总之,我现在要来重设我的密码就是了</p>
<p>由于有 VNC 可用,所以我直接想到用 Rescue mode (Single User Mode)</p>
<p>这里只讲 GRUB 引导的环境,因为我暂时不知道 systemd-boot 引导的环境怎么配置 Rescue mode</p>
<h2 id="修改-grub-启动项">修改 GRUB 启动项</h2>
<p>首先我们在 GRUB 选择系统页面中断它的引导,按下 <code>e</code> 来编辑它</p>
<p><em>此处应有图片</em></p>
<p>而后我们只要在主引导项的 <code>linux</code> 参数后面加一行 <code>init=/bin/bash</code> 即可</p>
<p><code>C-x</code> 或者 <code>F10</code> 来保存,切记不要直接 <code>Esc</code></p>
<p>而后直接选择刚刚修改过的启动就可以了。</p>
<p><em>此处应有图片</em></p>
<h2 id="排除问题">排除问题</h2>
<p>由于教程<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup>写说我们需要先挂载 <code>/</code> 成 <code>rw</code> 才可以写入,但是我实际操作的时候并没有遇到</p>
<p>但,这里还是先挂载</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-plain" data-lang="plain"><span style="display:flex;"><span># mount -n -o remount,rw /
</span></span></code></pre></div><p>而后呢,我们需要更改密码,那使用 <code>passwd</code> 指令修改就可以了</p>
<p>修改好以后,不需要重启系统,直接输入 <code>exec /sbin/init</code> 就可以按照正常的步骤进入系统</p>
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p><a href="https://www.linuxtechi.com/boot-arch-linux-single-user-mode-rescue-mode/">https://www.linuxtechi.com/boot-arch-linux-single-user-mode-rescue-mode/</a> <a href="#fnref:1" class="footnote-backref" role="doc-backlink">↩︎</a></p>
</li>
</ol>
</div>
-
使用 systemd-timer 代替 crontab 执行定时任务
https://leanhe.dev/posts/2021.04.21.1/
Wed, 21 Apr 2021 23:57:49 +0800
https://leanhe.dev/posts/2021.04.21.1/
<p>Arch 上已经不预装 crontab 了,但是有全套(?)的 systemd 套件,所以可以用 <a href="https://www.freedesktop.org/software/systemd/man/systemd.timer.html">systemd-timer</a> 来代替定时任务的执行。</p>
<p>这里就讲一下定时(固定时间执行)任务好了,我觉得它的缺点(?)是需要一个 <code>.service</code> 和它相匹配,所以就是有一个 <code>timer</code> 跟着一个 <code>service</code> ,才能把定时任务组合起来。</p>
<h2 id="service-配置">Service 配置</h2>
<p>这里假设我需要执行一个 Python 所写的程序,并且将这个服务命名为 <code>python-timer.service</code></p>
<p>之后把下列的文件添加进 <code>/etc/systemd/system/</code> 中</p>
<pre tabindex="0"><code class="language-conf" data-lang="conf">[Unit]
Description=Python timer test
Wants=python-timer.timer
[Service]
Type=oneshot
ExecStart=/usr/bin/python -c "import time; print(time.time())"
[Install]
WantedBy=multi-user.target
</code></pre><p>这个服务会在指定的时候输出当前时间</p>
<h3 id="timer-配置">Timer 配置</h3>
<p>那我们也同时需要一个 timer 文件,放在 <code>/etc/systemd/system/</code> 中。这个 timer 文件命名为 <code>python-timer.timer</code> 好了</p>
<p>这里的例子是让其每天的三点钟运行</p>
<pre tabindex="0"><code class="language-conf" data-lang="conf">[Unit]
Description=Python timer test timer
Wants=python-timer.service
[Timer]
Unit=python-timer.service
OnCalendar=*-*-* 4:30:00
[Install]
WantedBy=timers.target
</code></pre><p>把这两个配置文件写好以后,就执行重载指令</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-plain" data-lang="plain"><span style="display:flex;"><span>$ sudo systemctl daemon-reload
</span></span></code></pre></div><p>然后把 timer 启用</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-plain" data-lang="plain"><span style="display:flex;"><span>$ sudo systemctl enable python-timer.timer
</span></span></code></pre></div><p>之后我们使用下列指令就能看到 timer 情况了</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-plain" data-lang="plain"><span style="display:flex;"><span>$ sudo systemctl status python-timer.timer
</span></span><span style="display:flex;"><span>● python-timer.timer - Python timer test timer
</span></span><span style="display:flex;"><span> Loaded: loaded (/etc/systemd/system/python-timer.timer; enabled; vendor preset: disabled)
</span></span><span style="display:flex;"><span> Active: active (waiting) since Thu 2021-04-22 01:12:58 CST; 7s ago
</span></span><span style="display:flex;"><span> Trigger: Thu 2021-04-22 04:30:00 CST; 3h 16min left
</span></span><span style="display:flex;"><span> Triggers: ● python-timer.service
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>Apr 22 01:12:58 systemd[1]: Started Python timer test timer.
</span></span></code></pre></div>
-
在VPS上安装 openSUSE TW
https://leanhe.dev/posts/2021.04.17.2/
Sat, 17 Apr 2021 22:28:25 +0800
https://leanhe.dev/posts/2021.04.17.2/
<p>因为我前面的步骤完全失败了,所以当我前面在放屁就好了</p>
<h2 id="第一次尝试-net-iso">第一次尝试 NET iso</h2>
<p>先挂载到ISO上</p>
<p><img src="https://img.shabiwangyou.com/images/2021/04/17/image.png" alt="openSUSE TW ISO 加载 Kernel 介面"></p>
<p><img src="https://img.shabiwangyou.com/images/2021/04/17/image5fb202e52f6fe7bd.png" alt="openSUSE TW ISO 选择镜像源介面"></p>
<p>这里需要手动设置 IP,先选择 <code>Back</code></p>
<p>然后选择语言和键盘后到这个菜单。</p>
<p><img src="https://img.shabiwangyou.com/images/2021/04/17/image3aca5d9da8c4a202.png" alt="openSUSE TW ISO 主菜单介面"></p>
<p>选择 Settings ,选择到 Network Setup 选项,手动设置网络</p>
<p><img src="https://img.shabiwangyou.com/images/2021/04/17/image48c4cfee5be69775.png" alt="openSUSE TW ISO 选项介面"></p>
<p>选择手动设置IP,因为DHCP会失败</p>
<p>然后手动输入 Vultr 提供的 IP 和网关,以及 DNS 。 DNS 可以自定义
<img src="https://img.shabiwangyou.com/images/2021/04/17/image53acbec3fc9e0f7e.png" alt="openSUSE TW ISO 输入IP地址介面"></p>
<p>设置好后回到主菜单,选择开始安装。</p>
<p>因为找不到远端的仓库,我选择直接从DVD安装好了,就不网络安装了。</p>
<h2 id="第二次尝试-offline-iso">第二次尝试 Offline ISO</h2>
<p><img src="https://img.shabiwangyou.com/images/2021/04/18/image.png" alt="openSUSE TW ISO 安装程式错误介面"></p>
<p>检查过 md5 ,也自我检查过 iso ,放弃</p>
<h2 id="第三次尝试-remote-install">第三次尝试 Remote Install</h2>
<p>本来打算用远端安装的方法,但是最后也是失败了。</p>
<p>下载 <code>initrd</code> 引导安装程序</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-sh" data-lang="sh"><span style="display:flex;"><span>cd /boot
</span></span><span style="display:flex;"><span>curl -o vmlinuz.install http://mirror.siena.edu/opensuse/tumbleweed/repo/oss/boot/x86_64/loader/linux
</span></span><span style="display:flex;"><span>curl -o initrd.install http://mirror.siena.edu/opensuse/tumbleweed/repo/oss/boot/x86_64/loader/initrd
</span></span></code></pre></div><p><code>grub.cfg</code> 配置</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-plain" data-lang="plain"><span style="display:flex;"><span> menuentry 'openSUSE install' {
</span></span><span style="display:flex;"><span> insmod gzio
</span></span><span style="display:flex;"><span> set root='hd0,gpt1'
</span></span><span style="display:flex;"><span> linux /boot/vmlinuz.install noapic usessh=1 sshpassword="12345678" install=http://mirror.siena.edu/opensuse/tumbleweed/repo/oss hostip=192.168.1.50 netmask=255.255.255.0 gateway=192.168.1.1 nameserver=8.8.8.8
</span></span><span style="display:flex;"><span> initrd /boot/initrd.install
</span></span><span style="display:flex;"><span> }
</span></span></code></pre></div><h2 id="第四次尝试-升级配置后安装">第四次尝试 升级配置后安装</h2>
<p>最终,我决定曲线救国一下。利用 Block storage ,将作业系统安装到 Block storage 中,然后转移到原计划安装的机器,使用 <code>dd</code> 安装。</p>
<p>很成功,已经可以看到载入图形的界面了,要知道,我之前是连这个都打不开的</p>
<p><img src="https://img.shabiwangyou.com/images/2021/04/18/image08f74a93945c7355.png" alt="openSUSE TW ISO 加载内核介面"></p>
<p><img src="https://img.shabiwangyou.com/images/2021/04/18/image9b9565df06061d8b.png" alt="openSUSE TW ISO 仓库选择介面"></p>
<p>询问是否要开启线上的仓库,这里我选择的不要</p>
<p><img src="https://img.shabiwangyou.com/images/2021/04/18/imagea70aebeef4a3baaf.png" alt="openSUSE TW ISO 选择系统角色介面"></p>
<p>选择作业系统的 Role ,这里选择 Server</p>
<p>之后依照自己的配置配置就行(因为我发现我完全忘记截图了 顾着点下一步)</p>
<p><code>dd</code> 的过程我就不详细叙述了,因为好像也不是主题的一部分,它做功了,总之。</p>
<p><img src="https://img.shabiwangyou.com/images/2021/04/18/imaged61c144c88820fe6.png" alt="安装完成后使用 neofetch 执行的介面"></p>
-
acme.sh + CloudFlare + Nginx
https://leanhe.dev/posts/2021.04.17.1/
Sat, 17 Apr 2021 13:23:43 +0800
https://leanhe.dev/posts/2021.04.17.1/
<h1 id="准备工作">准备工作</h1>
<p>你首先需要一个 <a href="https://www.cloudflare.com/">CloudFlare</a> 的账号,由于申请证书的缘故,你还需要一个域名。</p>
<p>接着你需要将域名的 NameServer 设置成 CloudFlare 提供的 NS ,这样才能透过 CloudFlare 管理您域名的 DNS 记录。</p>
<p>安装 Nginx 这里就不再赘述,对于安装 <a href="https://acme.sh">acme.sh</a> , Arch linux 用户可以直接使用 <code>pacman</code> 安装<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup>:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-plain" data-lang="plain"><span style="display:flex;"><span>$ sudo pacman -S acme.sh
</span></span></code></pre></div><p>也可以参考<a href="https://github.com/acmesh-official/acme.sh/wiki/How-to-install">其他的安装方法</a>来安装</p>
<h1 id="申请证书2">申请证书<sup id="fnref:2"><a href="#fn:2" class="footnote-ref" role="doc-noteref">2</a></sup></h1>
<p><strong>注意: 连通性较差地区的用户建议使用代理</strong></p>
<h2 id="准备工作-1">准备工作</h2>
<p>以下两种方法使用其中一种即可</p>
<h3 id="使用-global-key">使用 Global Key</h3>
<p>Global Key 可以在<a href="https://dash.cloudflare.com/profile/api-tokens">这个页面</a>看到,将这个 Key 导入到环境变数中</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-sh" data-lang="sh"><span style="display:flex;"><span>export CF_Key<span style="color:#f92672">=</span><span style="color:#e6db74">"example_key"</span>
</span></span><span style="display:flex;"><span>export CF_Email<span style="color:#f92672">=</span><span style="color:#e6db74">"[email protected]"</span>
</span></span></code></pre></div><h3 id="使用-api-token">使用 API Token</h3>
<p>API Token 同样也可以在<a href="https://dash.cloudflare.com/profile/api-tokens">这个页面</a>看到,你需要新建一个 API Token,这个 Token 需要 <code>Zone.Zone</code> 的 <code>Read</code> 权限,和对于各个你需要的 <code>Zone</code> 的 <code>Zone.DNS</code> 的 <code>Write</code> 权限。</p>
<p>Accound ID 在 <a href="https://dash.cloudflare.com/">Dashboard</a> 重定向的网址中可以看到,如: <code>https://dash.cloudflare.com/faaaaaaaaaaaaaaaaaaaaaaaaaaaaaa3</code> 中的 <code>faaaaaaaaaaaaaaaaaaaaaaaaaaaaaa3</code></p>
<p>或者点选你的域名(Zone),在右下角可以看到</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-sh" data-lang="sh"><span style="display:flex;"><span>export CF_Token<span style="color:#f92672">=</span><span style="color:#e6db74">"example_token"</span>
</span></span><span style="display:flex;"><span>export CF_Account_ID<span style="color:#f92672">=</span><span style="color:#e6db74">"account_id"</span>
</span></span></code></pre></div><p>当然,你可能也只需要改一个 Zone 的 DNS 即可,这时候可以再指定上 Zone 的 ID</p>
<p>Zone 的 ID 在 Zone 页面的右下角(和Account ID位置相同)</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-sh" data-lang="sh"><span style="display:flex;"><span>export CF_Zone_ID<span style="color:#f92672">=</span><span style="color:#e6db74">"xxxxxxxxxxxxx"</span>
</span></span></code></pre></div><p>目前 acme.sh 官方尚不支援多 Zone 不同 Token</p>
<h2 id="开始申请">开始申请</h2>
<p>配置完环境变数后,就可以开始申请证书了。</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-sh" data-lang="sh"><span style="display:flex;"><span>acme.sh --issue --dns dns_cf -d example.com -d www.example.com
</span></span></code></pre></div><p>Sample output:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-plain" data-lang="plain"><span style="display:flex;"><span>$ acme.sh --issue --dns dns_cf -d example.com
</span></span><span style="display:flex;"><span>[Sat 17 Apr 13:51:58 CST 2021] Using CA: https://acme-v02.api.letsencrypt.org/directory
</span></span><span style="display:flex;"><span>[Sat 17 Apr 13:51:58 CST 2021] Creating domain key
</span></span><span style="display:flex;"><span>[Sat 17 Apr 13:51:58 CST 2021] The domain key is here: /home/user/.acme.sh/example.com/example.com.key
</span></span><span style="display:flex;"><span>[Sat 17 Apr 13:51:58 CST 2021] Single domain='example.com'
</span></span><span style="display:flex;"><span>[Sat 17 Apr 13:51:58 CST 2021] Getting domain auth token for each domain
</span></span><span style="display:flex;"><span>[Sat 17 Apr 13:51:59 CST 2021] Getting webroot for domain='example.com'
</span></span><span style="display:flex;"><span>[Sat 17 Apr 13:51:59 CST 2021] Adding txt value: **MASKED** for domain: _acme-challenge.example.com
</span></span><span style="display:flex;"><span>[Sat 17 Apr 13:51:59 CST 2021] Adding record
</span></span><span style="display:flex;"><span>[Sat 17 Apr 13:52:00 CST 2021] Added, OK
</span></span><span style="display:flex;"><span>[Sat 17 Apr 13:52:00 CST 2021] The txt record is added: Success.
</span></span><span style="display:flex;"><span>[Sat 17 Apr 13:52:00 CST 2021] Let's check each DNS record now. Sleep 20 seconds first.
</span></span><span style="display:flex;"><span>[Sat 17 Apr 13:52:21 CST 2021] Checking example.com for _acme-challenge.example.com
</span></span><span style="display:flex;"><span>[Sat 17 Apr 13:52:21 CST 2021] Domain example.com '_acme-challenge.example.com' success.
</span></span><span style="display:flex;"><span>[Sat 17 Apr 13:52:21 CST 2021] All success, let's return
</span></span><span style="display:flex;"><span>[Sat 17 Apr 13:52:21 CST 2021] Verifying: example.com
</span></span><span style="display:flex;"><span>[Sat 17 Apr 13:52:23 CST 2021] Success
</span></span><span style="display:flex;"><span>[Sat 17 Apr 13:52:23 CST 2021] Removing DNS records.
</span></span><span style="display:flex;"><span>[Sat 17 Apr 13:52:23 CST 2021] Removing txt: **MASKED** for domain: _acme-challenge.example.com
</span></span><span style="display:flex;"><span>[Sat 17 Apr 13:52:24 CST 2021] Removed: Success
</span></span><span style="display:flex;"><span>[Sat 17 Apr 13:52:24 CST 2021] Verify finished, start to sign.
</span></span><span style="display:flex;"><span>[Sat 17 Apr 13:52:24 CST 2021] Lets finalize the order.
</span></span><span style="display:flex;"><span>[Sat 17 Apr 13:52:24 CST 2021] Le_OrderFinalize='https://acme-v02.api.letsencrypt.org/acme/finalize/**MASKED**/**MASKED**'
</span></span><span style="display:flex;"><span>[Sat 17 Apr 13:52:25 CST 2021] Downloading cert.
</span></span><span style="display:flex;"><span>[Sat 17 Apr 13:52:25 CST 2021] Le_LinkCert='https://acme-v02.api.letsencrypt.org/acme/cert/**MASKED**'
</span></span><span style="display:flex;"><span>[Sat 17 Apr 13:52:25 CST 2021] Cert success.
</span></span><span style="display:flex;"><span>-----BEGIN CERTIFICATE-----
</span></span><span style="display:flex;"><span>**MASKED**
</span></span><span style="display:flex;"><span>-----END CERTIFICATE-----
</span></span><span style="display:flex;"><span>[Sat 17 Apr 13:52:25 CST 2021] Your cert is in /home/user/.acme.sh/example.com/example.com.cer
</span></span><span style="display:flex;"><span>[Sat 17 Apr 13:52:25 CST 2021] Your cert key is in /home/user/.acme.sh/example.com/example.com.key
</span></span><span style="display:flex;"><span>[Sat 17 Apr 13:52:25 CST 2021] The intermediate CA cert is in /home/user/.acme.sh/example.com/ca.cer
</span></span><span style="display:flex;"><span>[Sat 17 Apr 13:52:25 CST 2021] And the full chain certs is there: /home/user/.acme.sh/example.com/fullchain.cer
</span></span></code></pre></div><h2 id="配置-nginx">配置 Nginx</h2>
<p>Nginx 只需要两个文件 <code>cert key</code> 和 <code>full chain certs</code> 也就是 <code>example.com.key</code> 和 <code>fullchain.cer</code>。</p>
<p>我们先把这两个文件扔到 Nginx 的目录下,方便它可以读到这两个文件</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-sh" data-lang="sh"><span style="display:flex;"><span><span style="color:#75715e"># 假设 nginx 的配置文件在该目录下</span>
</span></span><span style="display:flex;"><span>cd /etc/nginx
</span></span><span style="display:flex;"><span><span style="color:#75715e"># 建立一个专门用来存放证书的目录</span>
</span></span><span style="display:flex;"><span>sudo mkdir certs
</span></span><span style="display:flex;"><span>cd certs
</span></span><span style="display:flex;"><span><span style="color:#75715e"># 建一个域名专用的目录</span>
</span></span><span style="display:flex;"><span>mkdir example.com
</span></span><span style="display:flex;"><span><span style="color:#75715e"># 用 acme.sh 脚本将文件 cp 到这两个目录中</span>
</span></span><span style="display:flex;"><span>acme.sh --install-cert -d example.com --key-file example.com/example.com.key --fullchain-file example.com/fullchain.cer
</span></span></code></pre></div><p>打开需要配置的服务端部分,添加以下行<sup id="fnref:3"><a href="#fn:3" class="footnote-ref" role="doc-noteref">3</a></sup></p>
<pre tabindex="0"><code class="language-conf" data-lang="conf"> listen 443 ssl http2;
listen [::]:443 ssl http2;
server_name leanhe.dev;
ssl_certificate /etc/nginx/certs/example.com/fullchain.cer;
ssl_certificate_key /etc/nginx/certs/example.com/example.com.key;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384;
</code></pre><p>如需 <a href="https://en.wikipedia.org/wiki/HTTP_Strict_Transport_Security">HSTS</a> ,可以再添加以下行</p>
<pre tabindex="0"><code class="language-conf" data-lang="conf"> add_header Strict-Transport-Security "max-age=63072000" always;
</code></pre><p>最后测试一下 Nginx</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-plain" data-lang="plain"><span style="display:flex;"><span>$ sudo nginx -t
</span></span><span style="display:flex;"><span>nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
</span></span><span style="display:flex;"><span>nginx: configuration file /etc/nginx/nginx.conf test is successful
</span></span></code></pre></div><p>这样就可以放心的重载 Nginx 了</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-plain" data-lang="plain"><span style="display:flex;"><span>$ sudo systemctl reload nginx
</span></span></code></pre></div><div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p><a href="https://archlinux.org/packages/community/any/acme.sh/">https://archlinux.org/packages/community/any/acme.sh/</a> <a href="#fnref:1" class="footnote-backref" role="doc-backlink">↩︎</a></p>
</li>
<li id="fn:2">
<p><a href="https://github.com/acmesh-official/acme.sh/wiki/dnsapi#1-cloudflare-option">https://github.com/acmesh-official/acme.sh/wiki/dnsapi#1-cloudflare-option</a> <a href="#fnref:2" class="footnote-backref" role="doc-backlink">↩︎</a></p>
</li>
<li id="fn:3">
<p><a href="https://ssl-config.mozilla.org/">https://ssl-config.mozilla.org/</a> <a href="#fnref:3" class="footnote-backref" role="doc-backlink">↩︎</a></p>
</li>
</ol>
</div>
-
PostgreSQL 热备配置
https://leanhe.dev/posts/2021.04.16.1/
Fri, 16 Apr 2021 22:51:45 +0800
https://leanhe.dev/posts/2021.04.16.1/
<p>这里只配置了热备,没有配置归档处理,可能会导致伺服器关键数据无法回滚<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup>,或许下个文章会写一下(</p>
<h2 id="认证配置1">认证配置<sup id="fnref:2"><a href="#fn:2" class="footnote-ref" role="doc-noteref">2</a></sup></h2>
<p>首先我们需要创建一个可用于热备的账户,建议使用一个专用的账户用来做这个事情。</p>
<p>这个账户需要有 <code>REPLICATION</code> 和 <code>LOGIN</code> 的权限。虽然 <code>REPLICATION</code> 的权限很大,但是它并不能修改主伺服器的数据。</p>
<p>如果这个账户 <code>foo</code> 且账户是来自 <code>192.168.1.100</code> 则可以把以下配置放到主机的 <code>pg_hba.conf</code> 中。</p>
<pre tabindex="0"><code class="language-conf" data-lang="conf"># Allow the user "foo" from host 192.168.1.100 to connect to the primary
# as a replication standby if the user's password is correctly supplied.
#
# TYPE DATABASE USER ADDRESS METHOD
host replication foo 192.168.1.100/32 md5
</code></pre><p>同时,把以下的连接讯息放在备用伺服器的 <code>primary_conninfo</code> 中</p>
<p>这里假设主伺服器的 ip 位址是 <code>192.168.1.50</code></p>
<pre tabindex="0"><code class="language-conf" data-lang="conf"># The standby connects to the primary that is running on host 192.168.1.50
# and port 5432 as the user "foo" whose password is "foopass".
primary_conninfo = 'host=192.168.1.50 port=5432 user=foo password=foopass'
</code></pre><h2 id="配置伺服器">配置伺服器</h2>
<h3 id="主伺服器">主伺服器</h3>
<p>首先我们需要配置主伺服器,修改部分 <code>postgresql.conf</code> 文件内容</p>
<pre tabindex="0"><code class="language-conf" data-lang="conf">wal_level = replica
full_page_writes = on
wal_log_hints = on
</code></pre><p>配置好后,这里我们需要重启一下伺服器</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-plain" data-lang="plain"><span style="display:flex;"><span>root@:~ $ systemctl restart posgresql.service
</span></span></code></pre></div><p>(然后我在这边折腾了一个多小时,原因是配置文件全部改到旧的文件上去了,我说怎么老是用不了)</p>
<h3 id="从伺服器">从伺服器</h3>
<p>首先确保从伺服器 <code>postgresql.service</code> 没有在运行,而后确认从伺服器没有重要资料后,将从伺服器的数据直接删除(当然,你想备份把它移走也是可以的)</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-plain" data-lang="plain"><span style="display:flex;"><span>postgres@:~ $ rm -rf ~/data/
</span></span></code></pre></div><p>使用 <code>pg_basebackup</code> 指令,从远端拷贝一份数据到本地</p>
<p>此处加上 <code>--write-recovery-conf</code> 指令,这样可以直接将 <code>primary_conninfo</code> 写入到配置文件中。</p>
<p>此处也可以加上 <code>--wal-method=stream</code> 避免复制时间太久导致最后一步失败<sup id="fnref:3"><a href="#fn:3" class="footnote-ref" role="doc-noteref">3</a></sup></p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-plain" data-lang="plain"><span style="display:flex;"><span>postgres@:~ $ pg_basebackup --write-recovery-conf -h 192.168.1.50 -U foo -D data -P
</span></span><span style="display:flex;"><span>Password:
</span></span><span style="display:flex;"><span>713513/713513 kB (100%), 1/1 tablespace
</span></span></code></pre></div><p>复制完成后,可以检查 <code>data/postgres.conf</code> 文件,可以看到 <code>primary_conninfo</code> 已经帮我们写上了</p>
<pre tabindex="0"><code class="language-conf" data-lang="conf">primary_conninfo = 'user=foo password=foopass host=192.168.1.50 port=5432' # connection string to sending server
</code></pre><p>但是还没完,我们还要进一步修改内容</p>
<pre tabindex="0"><code class="language-conf" data-lang="conf">hot_standby = on
</code></pre><p>上面跟主伺服器重复的选项我这里把它注释掉了,好像没有什么影响。</p>
<p>修改完后保存,并且创建一个 <code>standby.signal</code> 文件</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-plain" data-lang="plain"><span style="display:flex;"><span>postgres@:~ $ touch data/standby.signal
</span></span></code></pre></div><p>这样就可以启动伺服器了。</p>
<h2 id="检查状态4">检查状态<sup id="fnref:4"><a href="#fn:4" class="footnote-ref" role="doc-noteref">4</a></sup></h2>
<h3 id="主伺服器-1">主伺服器</h3>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-plain" data-lang="plain"><span style="display:flex;"><span>postgres@:~ $ psql
</span></span><span style="display:flex;"><span>psql (13.2)
</span></span><span style="display:flex;"><span>Type "help" for help.
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>postgres=# \x
</span></span><span style="display:flex;"><span>Expanded display is on.
</span></span><span style="display:flex;"><span>postgres=# select * from pg_stat_replication;
</span></span><span style="display:flex;"><span>-[ RECORD 1 ]----+------------------------------
</span></span><span style="display:flex;"><span>pid | 12911
</span></span><span style="display:flex;"><span>usesysid | 16570
</span></span><span style="display:flex;"><span>usename | rundll32
</span></span><span style="display:flex;"><span>application_name | walreceiver
</span></span><span style="display:flex;"><span>client_addr | 10.1.96.3
</span></span><span style="display:flex;"><span>client_hostname |
</span></span><span style="display:flex;"><span>client_port | 57676
</span></span><span style="display:flex;"><span>backend_start | 2021-04-17 01:21:38.533617+08
</span></span><span style="display:flex;"><span>backend_xmin |
</span></span><span style="display:flex;"><span>state | streaming
</span></span><span style="display:flex;"><span>sent_lsn | 1/1D114338
</span></span><span style="display:flex;"><span>write_lsn | 1/1D114338
</span></span><span style="display:flex;"><span>flush_lsn | 1/1D114338
</span></span><span style="display:flex;"><span>replay_lsn | 1/1D114338
</span></span><span style="display:flex;"><span>write_lag |
</span></span><span style="display:flex;"><span>flush_lag |
</span></span><span style="display:flex;"><span>replay_lag |
</span></span><span style="display:flex;"><span>sync_priority | 0
</span></span><span style="display:flex;"><span>sync_state | async
</span></span><span style="display:flex;"><span>reply_time | 2021-04-17 02:20:34.118557+08
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>postgres=#
</span></span></code></pre></div><h3 id="从伺服器-1">从伺服器</h3>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-plain" data-lang="plain"><span style="display:flex;"><span>postgres@:~ $ psql
</span></span><span style="display:flex;"><span>psql (13.2)
</span></span><span style="display:flex;"><span>Type "help" for help.
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>postgres=# \x
</span></span><span style="display:flex;"><span>Expanded display is on.
</span></span><span style="display:flex;"><span>postgres=# select * from pg_stat_wal_receiver;
</span></span><span style="display:flex;"><span>-[ RECORD 1 ]---------+---------------------------------------------
</span></span><span style="display:flex;"><span>pid | 871359
</span></span><span style="display:flex;"><span>status | streaming
</span></span><span style="display:flex;"><span>receive_start_lsn | 1/1D000000
</span></span><span style="display:flex;"><span>receive_start_tli | 1
</span></span><span style="display:flex;"><span>written_lsn | 1/1D117658
</span></span><span style="display:flex;"><span>flushed_lsn | 1/1D117658
</span></span><span style="display:flex;"><span>received_tli | 1
</span></span><span style="display:flex;"><span>last_msg_send_time | 2021-04-17 02:20:50.522413+08
</span></span><span style="display:flex;"><span>last_msg_receipt_time | 2021-04-17 02:20:50.520713+08
</span></span><span style="display:flex;"><span>latest_end_lsn | 1/1D117658
</span></span><span style="display:flex;"><span>latest_end_time | 2021-04-17 02:20:50.522413+08
</span></span><span style="display:flex;"><span>slot_name |
</span></span><span style="display:flex;"><span>sender_host | 192.168.1.50
</span></span><span style="display:flex;"><span>sender_port | 5432
</span></span><span style="display:flex;"><span>conninfo | user=foo password=******** host=192.168.1.50
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>postgres=#
</span></span></code></pre></div><h2 id="参考资料">参考资料</h2>
<p><a href="https://www.postgresql.org/docs/current/hot-standby.html">https://www.postgresql.org/docs/current/hot-standby.html</a>
<a href="https://www.postgresql.org/docs/current/runtime-config-replication.html">https://www.postgresql.org/docs/current/runtime-config-replication.html</a>
<a href="https://www.postgresql.org/docs/current/app-pgbasebackup.html">https://www.postgresql.org/docs/current/app-pgbasebackup.html</a>
<a href="https://linux.onlinedoc.tw/2016/05/centos7rhel7-postgresql-replication.html">https://linux.onlinedoc.tw/2016/05/centos7rhel7-postgresql-replication.html</a>
<a href="https://www.postgresql.org/docs/current/transaction-iso.html">https://www.postgresql.org/docs/current/transaction-iso.html</a></p>
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p><a href="https://www.escapelife.site/posts/b47f1fcb.html">https://www.escapelife.site/posts/b47f1fcb.html</a> <a href="#fnref:1" class="footnote-backref" role="doc-backlink">↩︎</a></p>
</li>
<li id="fn:2">
<p><a href="https://www.postgresql.org/docs/current/warm-standby.html#STREAMING-REPLICATION">https://www.postgresql.org/docs/current/warm-standby.html#STREAMING-REPLICATION</a> <a href="#fnref:2" class="footnote-backref" role="doc-backlink">↩︎</a></p>
</li>
<li id="fn:3">
<p><a href="https://ravenonhill.blogspot.com/2019/12/streaming-replication-pgsql-12.html">https://ravenonhill.blogspot.com/2019/12/streaming-replication-pgsql-12.html</a> <a href="#fnref:3" class="footnote-backref" role="doc-backlink">↩︎</a></p>
</li>
<li id="fn:4">
<p><a href="https://stackoverflow.com/a/54164409">https://stackoverflow.com/a/54164409</a> <a href="#fnref:4" class="footnote-backref" role="doc-backlink">↩︎</a></p>
</li>
</ol>
</div>
-
动态设定执行在 systemd 中 service 的环境变数
https://leanhe.dev/posts/2021.04.13.1/
Tue, 13 Apr 2021 22:35:45 +0800
https://leanhe.dev/posts/2021.04.13.1/
<p>以本部落格主题的新功能为例,新功能需要传一个名为 <code>BLOG_VERSION</code> 的环境变数,这样会把附加的信息写在页脚,可以直接验证是从那个 commit 生成的 (是的 我下午被 CloudFlare 的缓存坑了)</p>
<p>比方说我使用 git 的 <code>short hash</code> 和 <code>commit date</code> ,就可以按如下所写</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-plain" data-lang="plain"><span style="display:flex;"><span>$ bash -c 'export BLOG_VERSION="$(git rev-parse --short HEAD) $(git --no-pager log -1 --format="%ai")"; echo $BLOG_VERSION'
</span></span></code></pre></div><p>Bash option <code>-c</code> <sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup>:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-plain" data-lang="plain"><span style="display:flex;"><span> -c If the -c option is present, then commands are read from
</span></span><span style="display:flex;"><span> the first non-option argument command_string. If there
</span></span><span style="display:flex;"><span> are arguments after the command_string, the first argument
</span></span><span style="display:flex;"><span> is assigned to $0 and any remaining arguments are assigned
</span></span><span style="display:flex;"><span> to the positional parameters. The assignment to $0 sets
</span></span><span style="display:flex;"><span> the name of the shell, which is used in warning and error
</span></span><span style="display:flex;"><span> messages.
</span></span></code></pre></div><p>来源:</p>
<ul>
<li><a href="https://stackoverflow.com/a/51403241">https://stackoverflow.com/a/51403241</a></li>
<li><a href="https://unix.stackexchange.com/a/324035">https://unix.stackexchange.com/a/324035</a></li>
</ul>
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p><a href="https://www.man7.org/linux/man-pages/man1/bash.1.html">https://www.man7.org/linux/man-pages/man1/bash.1.html</a> <a href="#fnref:1" class="footnote-backref" role="doc-backlink">↩︎</a></p>
</li>
</ol>
</div>
-
使用 ngx http realip module 模块重写透过 CloudFlare CDN 连接到伺服器的真实IP
https://leanhe.dev/posts/2021.04.10.1/
Sat, 10 Apr 2021 15:16:36 +0800
https://leanhe.dev/posts/2021.04.10.1/
<p>首先检查 Nginx 编译时有没有附带 <code>ngx_http_realip_module</code> 模块,可以通过 <code>nginx -V</code> 检查</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-plain" data-lang="plain"><span style="display:flex;"><span>$ nginx -V
</span></span><span style="display:flex;"><span>nginx version: nginx/1.18.0
</span></span><span style="display:flex;"><span>built with OpenSSL 1.1.1g 21 Apr 2020 (running with OpenSSL 1.1.1k 25 Mar 2021)
</span></span><span style="display:flex;"><span>TLS SNI support enabled
</span></span><span style="display:flex;"><span>configure arguments: --prefix=/etc/nginx --conf-path=/etc/nginx/nginx.conf --sbin-path=/usr/bin/nginx --pid-path=/run/nginx.pid --lock-path=/run/lock/nginx.lock --user=http --group=http --http-log-path=/var/log/nginx/access.log --error-log-path=stderr --http-client-body-temp-path=/var/lib/nginx/client-body --http-proxy-temp-path=/var/lib/nginx/proxy --http-fastcgi-temp-path=/var/lib/nginx/fastcgi --http-scgi-temp-path=/var/lib/nginx/scgi --http-uwsgi-temp-path=/var/lib/nginx/uwsgi --with-cc-opt='-march=x86-64 -mtune=generic -O2 -pipe -fno-plt -D_FORTIFY_SOURCE=2' --with-ld-opt=-Wl,-O1,--sort-common,--as-needed,-z,relro,-z,now --with-compat --with-debug --with-file-aio --with-http_addition_module --with-http_auth_request_module --with-http_dav_module --with-http_degradation_module --with-http_flv_module --with-http_geoip_module --with-http_gunzip_module --with-http_gzip_static_module --with-http_mp4_module --with-http_realip_module --with-http_secure_link_module --with-http_slice_module --with-http_ssl_module --with-http_stub_status_module --with-http_sub_module --with-http_v2_module --with-mail --with-mail_ssl_module --with-pcre-jit --with-stream --with-stream_geoip_module --with-stream_realip_module --with-stream_ssl_module --with-stream_ssl_preread_module --with-threads
</span></span></code></pre></div><p>如果有的话,将以下的代码添加到 <code>server</code> 配置的栏位,重载 Nginx 即可</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-plain" data-lang="plain"><span style="display:flex;"><span>set_real_ip_from 173.245.48.0/20;
</span></span><span style="display:flex;"><span>set_real_ip_from 103.21.244.0/22;
</span></span><span style="display:flex;"><span>set_real_ip_from 103.22.200.0/22;
</span></span><span style="display:flex;"><span>set_real_ip_from 103.31.4.0/22;
</span></span><span style="display:flex;"><span>set_real_ip_from 141.101.64.0/18;
</span></span><span style="display:flex;"><span>set_real_ip_from 108.162.192.0/18;
</span></span><span style="display:flex;"><span>set_real_ip_from 190.93.240.0/20;
</span></span><span style="display:flex;"><span>set_real_ip_from 188.114.96.0/20;
</span></span><span style="display:flex;"><span>set_real_ip_from 197.234.240.0/22;
</span></span><span style="display:flex;"><span>set_real_ip_from 198.41.128.0/17;
</span></span><span style="display:flex;"><span>set_real_ip_from 162.158.0.0/15;
</span></span><span style="display:flex;"><span>set_real_ip_from 104.16.0.0/13;
</span></span><span style="display:flex;"><span>set_real_ip_from 104.24.0.0/14;
</span></span><span style="display:flex;"><span>set_real_ip_from 172.64.0.0/13;
</span></span><span style="display:flex;"><span>set_real_ip_from 131.0.72.0/22;
</span></span><span style="display:flex;"><span>set_real_ip_from 2400:cb00::/32;
</span></span><span style="display:flex;"><span>set_real_ip_from 2606:4700::/32;
</span></span><span style="display:flex;"><span>set_real_ip_from 2803:f800::/32;
</span></span><span style="display:flex;"><span>set_real_ip_from 2405:b500::/32;
</span></span><span style="display:flex;"><span>set_real_ip_from 2405:8100::/32;
</span></span><span style="display:flex;"><span>set_real_ip_from 2a06:98c0::/29;
</span></span><span style="display:flex;"><span>set_real_ip_from 2c0f:f248::/32;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>real_ip_header CF-Connecting-IP;
</span></span></code></pre></div><p>也可以写入到文件中,然后通过在 <code>server</code> 配置的栏位,使用 <code>include</code> 语句导入。</p>
<p>参考资料:</p>
<ul>
<li><a href="https://danielmiessler.com/blog/getting-real-ip-addresses-using-cloudflare-nginx-and-varnish/">https://danielmiessler.com/blog/getting-real-ip-addresses-using-cloudflare-nginx-and-varnish/</a></li>
<li><a href="https://support.cloudflare.com/hc/en-us/articles/200170786-Restoring-original-visitor-IPs">https://support.cloudflare.com/hc/en-us/articles/200170786-Restoring-original-visitor-IPs</a></li>
<li><a href="https://www.cloudflare.com/en-gb/ips/">https://www.cloudflare.com/en-gb/ips/</a></li>
</ul>
-
Nginx 阻止某些应用内置浏览器访问
https://leanhe.dev/posts/2021.04.08.1/
Thu, 08 Apr 2021 11:18:59 +0800
https://leanhe.dev/posts/2021.04.08.1/
<p>首先我们需要定义阻止哪些应用,比如本站预设阻止 QQ 系浏览器访问</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-plain" data-lang="plain"><span style="display:flex;"><span> if ($http_user_agent ~* (MQQBrowser|QQ|TIM) ) {
</span></span><span style="display:flex;"><span> return 403;
</span></span><span style="display:flex;"><span> }
</span></span></code></pre></div><p>但是这样子设置太不友好了,有可能会被认为是 Nginx 不会配</p>
<p><img src="https://img.shabiwangyou.com/images/2021/04/08/593810c6b6d686d92.png" alt="某朋友对于本网站的错误页面评价"></p>
<p>那我们就简单美化一下网站</p>
<p>我们用一个自定义的页面好了,稍微把 Nginx 的页面修改一下</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-html" data-lang="html"><span style="display:flex;"><span><<span style="color:#f92672">html</span>>
</span></span><span style="display:flex;"><span><<span style="color:#f92672">head</span>><<span style="color:#f92672">title</span>>Built-in browser Forbidden</<span style="color:#f92672">title</span>></<span style="color:#f92672">head</span>>
</span></span><span style="display:flex;"><span><<span style="color:#f92672">body</span>>
</span></span><span style="display:flex;"><span><<span style="color:#f92672">center</span>><<span style="color:#f92672">h1</span>>Visit from some app built-in browser is Forbidden</<span style="color:#f92672">h1</span>></<span style="color:#f92672">center</span>>
</span></span><span style="display:flex;"><span><<span style="color:#f92672">hr</span>><<span style="color:#f92672">center</span>>nginx</<span style="color:#f92672">center</span>>
</span></span><span style="display:flex;"><span></<span style="color:#f92672">body</span>>
</span></span><span style="display:flex;"><span></<span style="color:#f92672">html</span>>
</span></span></code></pre></div><p>这样写可能太过精简了,大概还是需要在后面加上几行</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-html" data-lang="html"><span style="display:flex;"><span><span style="color:#75715e"><!-- a padding to disable MSIE and Chrome friendly error page --></span>
</span></span></code></pre></div><p>接着我们再把 Nginx 的配置调整一下</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-plain" data-lang="plain"><span style="display:flex;"><span> location /ban-builtin {
</span></span><span style="display:flex;"><span> root $WEBROOT;
</span></span><span style="display:flex;"><span> rewrite ^ /403-ban-built-in.html break;
</span></span><span style="display:flex;"><span> }
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> if ($http_user_agent ~* (MQQBrowser|QQ|TIM) ) {
</span></span><span style="display:flex;"><span> set $shouldban "1";
</span></span><span style="display:flex;"><span> }
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> if ($request_uri != /ban-builtin) {
</span></span><span style="display:flex;"><span> set $shouldban "${shouldban}0";
</span></span><span style="display:flex;"><span> }
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> if ($shouldban = "10") {
</span></span><span style="display:flex;"><span> return 307 $scheme://$host/ban-builtin;
</span></span><span style="display:flex;"><span> }
</span></span></code></pre></div><p>如果不加上第三个语句判别的话,会导致无限重定向。</p>
<p><del>那现在我们也可以主动<a href="https://leanhe.dev/ban-builtin">访问</a>一下来试试</del></p>
<hr>
<p>有朋友说这样对使用浏览器访问太不友好了,要想个办法用更好的方式来做</p>
<p>那还能怎么办, Nginx 肯定是做不到了,大概还是需要一个 php 页面</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-php" data-lang="php"><span style="display:flex;"><span><span style="color:#f92672"><?</span><span style="color:#a6e22e">php</span>
</span></span><span style="display:flex;"><span> $UA <span style="color:#f92672">=</span> $_SERVER[<span style="color:#e6db74">'HTTP_USER_AGENT'</span>];
</span></span><span style="display:flex;"><span> <span style="color:#75715e">//if (str_contains($UA, "QQ") || str_contains($UA, "MQQBrowser") || str_contains($UA, "TIM")) { // PHP8
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#66d9ef">if</span> (<span style="color:#a6e22e">strpos</span>($UA, <span style="color:#e6db74">"QQ"</span>) <span style="color:#f92672">||</span> <span style="color:#a6e22e">strpos</span>($UA, <span style="color:#e6db74">"MQQBrowser"</span>) <span style="color:#f92672">||</span> <span style="color:#a6e22e">strpos</span>($UA, <span style="color:#e6db74">"TIM"</span>)) {
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">http_response_code</span>(<span style="color:#ae81ff">403</span>);
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">echo</span> <span style="color:#e6db74"><<<</span><span style="color:#e6db74">EOT</span><span style="color:#e6db74">
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74"><html>
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74"><head><title>Built-in browser Forbidden</title></head>
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74"><body>
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74"><center><h1>Visit from some app built-in browser is Forbidden</h1>请点击右上角使用外部浏览器打开<br>
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74"><hr><center>nginx</center>
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74"></body>
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74"></html>
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74"></span><span style="color:#e6db74">EOT</span>;
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">die</span>();
</span></span><span style="display:flex;"><span> }
</span></span><span style="display:flex;"><span> $redirect_location <span style="color:#f92672">=</span> <span style="color:#e6db74">'/'</span>;
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> (<span style="color:#a6e22e">isset</span>($_GET[<span style="color:#e6db74">"to"</span>])) {
</span></span><span style="display:flex;"><span> $redirect_location <span style="color:#f92672">=</span> $_GET[<span style="color:#e6db74">"to"</span>];
</span></span><span style="display:flex;"><span> }
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">header</span>(<span style="color:#e6db74">"HTTP/1.1 307 Temporary Redirect"</span>);
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">header</span>(<span style="color:#e6db74">"Location: //"</span><span style="color:#f92672">.</span>$_SERVER[<span style="color:#e6db74">'HTTP_HOST'</span>]<span style="color:#f92672">.</span>$redirect_location);
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">die</span>();
</span></span><span style="display:flex;"><span><span style="color:#75715e">?></span><span style="color:#960050;background-color:#1e0010">
</span></span></span></code></pre></div><p>(PHP7 还没有 <code>str_contains</code> 函数,坑了我一把)</p>
<p>然后稍微的把 Nginx 配置调整一下</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-plain" data-lang="plain"><span style="display:flex;"><span> if ($shouldban = "10") {
</span></span><span style="display:flex;"><span> return 307 $scheme://$host/redirect.php?to=$request_uri;
</span></span><span style="display:flex;"><span> }
</span></span></code></pre></div><p>现在就是<a href="https://leanhe.dev/redirect.php?debug">访问</a>新的页面了</p>
-
安装 Chevereto 并将图床挂载到 OneDrive 上
https://leanhe.dev/posts/2021.04.07.2/
Wed, 07 Apr 2021 22:54:29 +0800
https://leanhe.dev/posts/2021.04.07.2/
<p>安装 Chevereto 就不赘述了,建议使用<a href="https://v3-docs.chevereto.com/get-started/installation.html#installation-methods">手动安装</a>方式,因为官方的安装方法出现问题是不会给你说问题出在哪里的,我用了手动安装才发现原来是少了 <a href="https://archlinux.org/packages/extra/x86_64/gd/">GD Library</a></p>
<p>对于 Arch 用户,由于 php8 不支援 chevereto (反正我是拿到了一个什么莫名其妙的错误),所以安装 <a href="https://archlinux.org/packages/extra/x86_64/php7-fpm/">php-fpm7</a></p>
<p>同样还需要安装 <a href="https://archlinux.org/packages/extra/x86_64/mariadb/">MariaDB</a> 和一个 Http Server</p>
<p>我没有试过用 Caddy 能不能跑,但是理论上都差不多吧大概(本人用的是 <a href="https://archlinux.org/packages/extra/x86_64/nginx/">Nginx</a>)</p>
<hr>
<p>挂载 OneDrive 使用的是 <a href="https://archlinux.org/packages/community/x86_64/rclone/">rclone</a> 同时还需要安装 <a href="https://archlinux.org/packages/extra/x86_64/fuse2/">fuse2</a></p>
<p>链接 OneDrive 的话也不赘述了,照着<a href="https://rclone.org/onedrive/">官网的教程</a>来就好了。</p>
<p>安装好 Chevereto 并且可以访问后,会发现其图片文件是放在 <code>$WEBROOT/images</code> 下,所以只要对这个目录操作就可以</p>
<p>本人是写了一个 <code>rclone-mount-images.service</code> 文件来执行挂载</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-plain" data-lang="plain"><span style="display:flex;"><span>[Unit]
</span></span><span style="display:flex;"><span>Description=rclone mount Service
</span></span><span style="display:flex;"><span>After=network.target
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>[Service]
</span></span><span style="display:flex;"><span>Type=simple
</span></span><span style="display:flex;"><span>Restart=on-failure
</span></span><span style="display:flex;"><span>RestartSec=1m
</span></span><span style="display:flex;"><span>ExecStart=/usr/bin/rclone mount onedrive:chevereto $WEBROOT/chevereto/images --vfs-cache-mode writes --allow-other --no-modtime --vfs-cache-max-size 20G
</span></span><span style="display:flex;"><span>#ExecStop=/usr/bin/fusermount -u $WEBROOT/chevereto/images
</span></span><span style="display:flex;"><span>TimeoutStopSec=10m
</span></span><span style="display:flex;"><span>WorkingDirectory=$WEBROOT/chevereto
</span></span><span style="display:flex;"><span>User=user
</span></span><span style="display:flex;"><span>KillSignal=SIGINT
</span></span></code></pre></div><p>这里把 ExecStop 注释掉是因为,直接让它在前台工作就好了大概,这里需要注意的事情是,除非用 php-fpm 的用户来挂载,不然怎么样挂载点都是写不进去的,需要使用 <code>--allow-other</code> 选项来允许其他用户写入</p>
<p>而 fuse2 要求你主动将其配置文件中的 <code>user_allow_other</code> 注释掉,这样才可以挂载成功</p>
-
使用 GitHub Webhook 执行自动化 Hugo 部署
https://leanhe.dev/posts/2021.04.07.1/
Wed, 07 Apr 2021 15:25:41 +0800
https://leanhe.dev/posts/2021.04.07.1/
<p>众所周知,我搭了个静态部落格,那每次我都要手动生成网站也太麻烦了,于是我就想方设法搞自动部署出来。</p>
<p>首先写一个 PHP 页面,用来接收 WebHook 。为什么要用 PHP 呢,用别的不行吗?当然可以,只是我因为跑了 Chevereto 本身就要跑一个 <code>php-fpm</code> 所以我不如干脆也用 PHP 来写接收好了。</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-php" data-lang="php"><span style="display:flex;"><span><span style="color:#f92672"><?</span><span style="color:#a6e22e">php</span>
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> (<span style="color:#a6e22e">isset</span>($_GET) <span style="color:#f92672">&&</span> <span style="color:#a6e22e">isset</span>($_GET[<span style="color:#e6db74">"token"</span>]) <span style="color:#f92672">&&</span> $_GET[<span style="color:#e6db74">"token"</span>] <span style="color:#f92672">===</span> <span style="color:#e6db74">"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"</span>) {
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> (<span style="color:#a6e22e">isset</span>($_SERVER[<span style="color:#e6db74">"X-GitHub-Event"</span>]) <span style="color:#f92672">&&</span> $_SERVER[<span style="color:#e6db74">"X-GitHub-Event"</span>] <span style="color:#f92672">===</span> <span style="color:#e6db74">"push"</span>) {
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">echo</span> <span style="color:#e6db74">"{</span><span style="color:#ae81ff">\"</span><span style="color:#e6db74">status</span><span style="color:#ae81ff">\"</span><span style="color:#e6db74">: 200, </span><span style="color:#ae81ff">\"</span><span style="color:#e6db74">result</span><span style="color:#ae81ff">\"</span><span style="color:#e6db74">: "</span><span style="color:#f92672">.</span><span style="color:#a6e22e">json_encode</span>(<span style="color:#a6e22e">shell_exec</span>(<span style="color:#e6db74">"export XDG_RUNTIME_DIR=/run/user/$(id -u user) && sudo -u user /usr/bin/systemctl --user start pull-blog.service && sudo -u user /usr/bin/journalctl --user -u pull-blog.service -n 20 --no-pager"</span>))<span style="color:#f92672">.</span><span style="color:#e6db74">"}"</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> } <span style="color:#66d9ef">else</span> {
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">echo</span> <span style="color:#e6db74">"{</span><span style="color:#ae81ff">\"</span><span style="color:#e6db74">status</span><span style="color:#ae81ff">\"</span><span style="color:#e6db74">: 200}"</span>;
</span></span><span style="display:flex;"><span> }
</span></span><span style="display:flex;"><span> } <span style="color:#66d9ef">else</span> {
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">http_response_code</span>(<span style="color:#ae81ff">403</span>);
</span></span><span style="display:flex;"><span> }
</span></span><span style="display:flex;"><span><span style="color:#75715e">?></span><span style="color:#960050;background-color:#1e0010">
</span></span></span></code></pre></div><p>那我们怎么把部落格的内容重新拉下来呢?很简单,用一个 service 到时候去用 <code>systemctl</code> 呼叫就行了。</p>
<p>首先创建一个 <code>pull-blog.service</code> 文件在 <code>~/.config/systemd/user</code> 中 (本例中个人用户名为 <code>user</code>)</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-plain" data-lang="plain"><span style="display:flex;"><span>[Unit]
</span></span><span style="display:flex;"><span>Description=Blog Deploy Service
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>[Service]
</span></span><span style="display:flex;"><span>Type=oneshot
</span></span><span style="display:flex;"><span>ExecStart=/usr/bin/git pull
</span></span><span style="display:flex;"><span>ExecStart=/usr/bin/git submodule update --init
</span></span><span style="display:flex;"><span>ExecStart=/usr/bin/rm -rf $WEBROOT/public
</span></span><span style="display:flex;"><span>ExecStart=/opt/hugo/hugo -d $WEBROOT/public
</span></span><span style="display:flex;"><span>WorkingDirectory=/home/user/blog
</span></span></code></pre></div><p>接着使用 <code>systemctl --user daemon-reload</code> 重新载入用户自定义的服务</p>
<p>然后找到 php-fpm 所使用的用户,本例为 <code>http</code></p>
<p>使用 <code>sudo visudo</code> 将以下几行添加到 <code>sudoers</code> 文件中 <a href="https://www.sudo.ws/man/1.8.15/sudoers.man.html">man</a></p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-plain" data-lang="plain"><span style="display:flex;"><span>Defaults:http env_keep=XDG_RUNTIME_DIR
</span></span><span style="display:flex;"><span>http ALL= (user) NOPASSWD: /usr/bin/systemctl --user start pull-blog.service, \
</span></span><span style="display:flex;"><span> /usr/bin/journalctl --user -u pull-blog.service -n 20 --no-pager
</span></span></code></pre></div><p>这样 php-fpm 就有权限使用如上的命令了,同时也会回报命令的执行情况给 WebHook 。</p>
<h3 id="tips">Tips</h3>
<p>使用如下命令可以绕过 <code>/usr/sbin/nologin</code> <sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup></p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-plain" data-lang="plain"><span style="display:flex;"><span>su -s /bin/bash -c '/path/to/your/script' testuser
</span></span></code></pre></div><div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p><a href="https://serverfault.com/a/351048">https://serverfault.com/a/351048</a> <a href="#fnref:1" class="footnote-backref" role="doc-backlink">↩︎</a></p>
</li>
</ol>
</div>
-
Google Photos 迁移
https://leanhe.dev/posts/2021.04.06.1/
Tue, 06 Apr 2021 23:36:27 +0800
https://leanhe.dev/posts/2021.04.06.1/
<h1 id="起因">起因</h1>
<p>因应 Google 教育版更改<a href="https://support.google.com/a/answer/10431555">政策</a>,教育版不再享有每个账户无限容量,而是改为每个学校共享 100TB 的云端硬碟空间</p>
<p>而我一直以来都依赖 Google 相册,于是就有了将教育版内的内容迁移到个人账户的计划</p>
<p>但是 Google 不支援直接迁移,而是需要我们从 <a href="https://takeout.google.com/settings/takeout">Google Takeout</a> 下载数据,再重新上传数据到云端,以此完成迁移</p>
<h2 id="准备文件">准备文件</h2>
<p>好吧,也就150G的东西,大不了我手动就是了。</p>
<p>说干就干,于是我从 3.3 就发起了 Takeout 的请求 再到 3.5 的时候所有的数据都准备好了</p>
<p>下载下来之后解压发现,里面是相片+json的组合压缩包</p>
<p>json档案中存放的是一些文件元数据</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-json" data-lang="json"><span style="display:flex;"><span>{
</span></span><span style="display:flex;"><span> <span style="color:#f92672">"title"</span>: <span style="color:#e6db74">"1.jpg"</span>,
</span></span><span style="display:flex;"><span> <span style="color:#f92672">"description"</span>: <span style="color:#e6db74">""</span>,
</span></span><span style="display:flex;"><span> <span style="color:#f92672">"imageViews"</span>: <span style="color:#e6db74">"0"</span>,
</span></span><span style="display:flex;"><span> <span style="color:#f92672">"creationTime"</span>: {
</span></span><span style="display:flex;"><span> <span style="color:#f92672">"timestamp"</span>: <span style="color:#e6db74">"1557265777"</span>,
</span></span><span style="display:flex;"><span> <span style="color:#f92672">"formatted"</span>: <span style="color:#e6db74">"7 May 2019, 21:49:37 UTC"</span>
</span></span><span style="display:flex;"><span> },
</span></span><span style="display:flex;"><span> <span style="color:#f92672">"photoTakenTime"</span>: {
</span></span><span style="display:flex;"><span> <span style="color:#f92672">"timestamp"</span>: <span style="color:#e6db74">"1552457709"</span>,
</span></span><span style="display:flex;"><span> <span style="color:#f92672">"formatted"</span>: <span style="color:#e6db74">"13 Mar 2019, 06:15:09 UTC"</span>
</span></span><span style="display:flex;"><span> },
</span></span><span style="display:flex;"><span> <span style="color:#f92672">"geoData"</span>: {
</span></span><span style="display:flex;"><span> <span style="color:#f92672">"latitude"</span>: <span style="color:#ae81ff">0.0</span>,
</span></span><span style="display:flex;"><span> <span style="color:#f92672">"longitude"</span>: <span style="color:#ae81ff">0.0</span>,
</span></span><span style="display:flex;"><span> <span style="color:#f92672">"altitude"</span>: <span style="color:#ae81ff">0.0</span>,
</span></span><span style="display:flex;"><span> <span style="color:#f92672">"latitudeSpan"</span>: <span style="color:#ae81ff">0.0</span>,
</span></span><span style="display:flex;"><span> <span style="color:#f92672">"longitudeSpan"</span>: <span style="color:#ae81ff">0.0</span>
</span></span><span style="display:flex;"><span> },
</span></span><span style="display:flex;"><span> <span style="color:#f92672">"geoDataExif"</span>: {
</span></span><span style="display:flex;"><span> <span style="color:#f92672">"latitude"</span>: <span style="color:#ae81ff">0.0</span>,
</span></span><span style="display:flex;"><span> <span style="color:#f92672">"longitude"</span>: <span style="color:#ae81ff">0.0</span>,
</span></span><span style="display:flex;"><span> <span style="color:#f92672">"altitude"</span>: <span style="color:#ae81ff">0.0</span>,
</span></span><span style="display:flex;"><span> <span style="color:#f92672">"latitudeSpan"</span>: <span style="color:#ae81ff">0.0</span>,
</span></span><span style="display:flex;"><span> <span style="color:#f92672">"longitudeSpan"</span>: <span style="color:#ae81ff">0.0</span>
</span></span><span style="display:flex;"><span> },
</span></span><span style="display:flex;"><span> <span style="color:#f92672">"googlePhotosOrigin"</span>: {
</span></span><span style="display:flex;"><span> <span style="color:#f92672">"mobileUpload"</span>: {
</span></span><span style="display:flex;"><span> <span style="color:#f92672">"deviceFolder"</span>: {
</span></span><span style="display:flex;"><span> <span style="color:#f92672">"localFolderName"</span>: <span style="color:#e6db74">"Telegram"</span>
</span></span><span style="display:flex;"><span> },
</span></span><span style="display:flex;"><span> <span style="color:#f92672">"deviceType"</span>: <span style="color:#e6db74">"ANDROID_PHONE"</span>
</span></span><span style="display:flex;"><span> }
</span></span><span style="display:flex;"><span> }
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>我一开始还在想这些东西给我们有什么用,但是一会儿就知道了。</p>
<h2 id="上传文件">上传文件</h2>
<p>由于拖延症的缘故,我直到 4.4 才把所有的相册整理完毕, 晚上我把文件分成两个部分直接上传</p>
<p>但是传完之后上去浏览发现情况有点不对劲,我 3.5 的照片有大概 5k 张,我肯定不可能有那么多照片,所以肯定是文件的时间丢失,导致时间都集合在这个时间点,正好是我解压的时间点</p>
<p>我很生气,去质问 Google One 客服能不能有点作用让我批量修改,但是她说不行,我只能一张一张</p>
<p>5k 张照片,我改到明年都不一定改的完</p>
<p>于是我就想了一个办法,我把文件的创建时间和修改时间从本地改了,再传上去,理论上不就是时间对的了?</p>
<p>于是我糊了一个<a href="https://github.com/KunoiSayami/google-photo-takeout-timestamp-reset/blob/f46d45ddc8cfaeb91f31d8e97ab429bf68e88d2d/reset.py">脚本</a>,可以从它提供的 json 中获得时间,然后再应用在文件上。</p>
<p>不过具体有没有用,得等上传完才知道了。</p>
<p>Update: 它做功了。</p>
-
image test
https://leanhe.dev/posts/2021.04.05.1/
Mon, 05 Apr 2021 01:37:01 +0800
https://leanhe.dev/posts/2021.04.05.1/
<p><img src="https://img.shabiwangyou.com/images/2021/04/05/vlcsnap-2019-06-02-01h48m37s142.png" alt="电影 Inception 中的一幕"></p>
-
在 Arch Linux Arm 上使用 systemd-networkd 配置树莓派无线网路
https://leanhe.dev/posts/2021.04.02.1/
Sat, 03 Apr 2021 00:35:06 +0800
https://leanhe.dev/posts/2021.04.02.1/
<p>近期有使用树莓派无线网路的需要,但是之前操作树莓派的无线网路基本上都是在 raspbian 上操作的,以前没有在 Arch 上操作过(或者说都是在台式上面操作的,因为**小螃蟹)</p>
<p>Arch linux arm 是默认安装的,我好像没有安装什么附加的套件</p>
<p>先在 <a href="https://wiki.archlinux.org/index.php/systemd-networkd">Arch linux wiki</a> 上找到一篇文章,但是它没有讲说怎么管理WPA的网络</p>
<p>首先在网路上找到<a href="https://bbs.archlinux.org/viewtopic.php?id=178625">一篇文章</a></p>
<p>大意是 首先将网络的名称和密码放到文件中</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-plain" data-lang="plain"><span style="display:flex;"><span># wpa_passphrase MyNetwork SuperSecretPassphrase > /etc/wpa_supplicant/wpa_supplicant-wlan0.conf
</span></span></code></pre></div><p>这里 <code>MyNetwork</code> 是网络名称,而 <code>SuperSecretPassphrase</code> 是网络的密码</p>
<p>然后让这个profile启用</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-plain" data-lang="plain"><span style="display:flex;"><span># systemctl enable [email protected]
</span></span></code></pre></div><p>接着把网络的配置塞到 <code>/etc/systemd/network</code> 下面</p>
<p>比方说就叫它 <code>00-wireless-dhcp.network</code> 好了 (实际上你想叫它什么都可以)</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-plain" data-lang="plain"><span style="display:flex;"><span>[Match]
</span></span><span style="display:flex;"><span>Name=wlan0
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>[Network]
</span></span><span style="display:flex;"><span>DHCP=yes
</span></span></code></pre></div><p>后面还有一些步骤是开 <code>systemd-networkd</code> 之类的,但是我本身已经用这玩意管理网络了,就没有继续参考了</p>
<h2 id="侦错">侦错</h2>
<p>重启之后当然是发现没有连上</p>
<p><code>ip a</code> 输出如下</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-plain" data-lang="plain"><span style="display:flex;"><span># ip a
</span></span><span style="display:flex;"><span>1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
</span></span><span style="display:flex;"><span> link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
</span></span><span style="display:flex;"><span> inet 127.0.0.1/8 scope host lo
</span></span><span style="display:flex;"><span> valid_lft forever preferred_lft forever
</span></span><span style="display:flex;"><span> inet6 ::1/128 scope host
</span></span><span style="display:flex;"><span> valid_lft forever preferred_lft forever
</span></span><span style="display:flex;"><span>2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP group default qlen 1000
</span></span><span style="display:flex;"><span> link/ether *MASKED* brd ff:ff:ff:ff:ff:ff
</span></span><span style="display:flex;"><span> inet 192.168.0.1/24 brd 192.168.0.255 scope global eth0
</span></span><span style="display:flex;"><span> valid_lft forever preferred_lft forever
</span></span><span style="display:flex;"><span> inet6 *MASKED* scope global dynamic noprefixroute
</span></span><span style="display:flex;"><span> valid_lft 6990sec preferred_lft 6990sec
</span></span><span style="display:flex;"><span> inet6 *MASKED* scope global dynamic noprefixroute
</span></span><span style="display:flex;"><span> valid_lft 6990sec preferred_lft 6990sec
</span></span><span style="display:flex;"><span> inet6 *MASKED* scope global mngtmpaddr noprefixroute
</span></span><span style="display:flex;"><span> valid_lft forever preferred_lft forever
</span></span><span style="display:flex;"><span> inet6 *MASKED* scope global dynamic mngtmpaddr noprefixroute
</span></span><span style="display:flex;"><span> valid_lft 185858sec preferred_lft 99458sec
</span></span><span style="display:flex;"><span> inet6 fe80::dea6:32ff:fe0f:c62c/64 scope link
</span></span><span style="display:flex;"><span> valid_lft forever preferred_lft forever
</span></span><span style="display:flex;"><span>3: wlan0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc fq_codel state DOWN group default qlen 1000
</span></span><span style="display:flex;"><span> link/ether *MASKED* brd ff:ff:ff:ff:ff:ff
</span></span></code></pre></div><p>嗯,这当然是没有连上啦</p>
<p>错误讯息显示如下</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-plain" data-lang="plain"><span style="display:flex;"><span># journalctl -u [email protected]
</span></span><span style="display:flex;"><span>-- Journal begins at Thu 2021-02-04 06:22:10 CST, ends at Sat 2021-03-27 00:53:55 CST. --
</span></span><span style="display:flex;"><span>Mar 27 00:51:46 raspi4b systemd[1]: Dependency failed for WPA supplicant daemon (interface-specific version).
</span></span><span style="display:flex;"><span>Mar 27 00:51:46 raspi4b systemd[1]: [email protected]: Job [email protected]/start failed with result 'dependency'
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>Mar 27 00:51:46 raspi4b systemd[1]: sys-subsystem-net-devices-wlan0.conf.device: Job sys-subsystem-net-devices-wlan0.conf.device/start timed out.
</span></span><span style="display:flex;"><span>Mar 27 00:51:46 raspi4b systemd[1]: Timed out waiting for device /sys/subsystem/net/devices/wlan0.conf.
</span></span><span style="display:flex;"><span>Mar 27 00:51:46 raspi4b systemd[1]: Dependency failed for WPA supplicant daemon (interface-specific version).
</span></span><span style="display:flex;"><span>Mar 27 00:51:46 raspi4b systemd[1]: [email protected]: Job [email protected]/start failed with result 'dependency'.
</span></span><span style="display:flex;"><span>Mar 27 00:51:46 raspi4b systemd[1]: sys-subsystem-net-devices-wlan0.conf.device: Job sys-subsystem-net-devices-wlan0.conf.device/start failed with result 'timeout'.
</span></span></code></pre></div><p>我找了半天,都没有发现有什么相关的报错,但是在我的不懈努力下,找到了<a href="https://bbs.archlinux.org/viewtopic.php?id=241795">这篇文章</a></p>
<p>虽然这也不是什么解决的方法,但是我从中得到了启发,于是我</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-plain" data-lang="plain"><span style="display:flex;"><span># rm /etc/systemd/system/multi-user.target.wants/[email protected]
</span></span></code></pre></div><p>接着重新建立启用就行</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-plain" data-lang="plain"><span style="display:flex;"><span>#systemctl enable wpa_supplicant@wlan0
</span></span></code></pre></div><p>重启系统后,就会发现已经连上无线网路了</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-plain" data-lang="plain"><span style="display:flex;"><span># ip a
</span></span><span style="display:flex;"><span>1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
</span></span><span style="display:flex;"><span> link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
</span></span><span style="display:flex;"><span> inet 127.0.0.1/8 scope host lo
</span></span><span style="display:flex;"><span> valid_lft forever preferred_lft forever
</span></span><span style="display:flex;"><span> inet6 ::1/128 scope host
</span></span><span style="display:flex;"><span> valid_lft forever preferred_lft forever
</span></span><span style="display:flex;"><span>2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP group default qlen 1000
</span></span><span style="display:flex;"><span> link/ether *MASKED* brd ff:ff:ff:ff:ff:ff
</span></span><span style="display:flex;"><span> inet 192.168.0.1/24 brd 192.168.0.255 scope global eth0
</span></span><span style="display:flex;"><span> valid_lft forever preferred_lft forever
</span></span><span style="display:flex;"><span> inet6 *MASKED* scope global dynamic noprefixroute
</span></span><span style="display:flex;"><span> valid_lft 6866sec preferred_lft 6866sec
</span></span><span style="display:flex;"><span> inet6 *MASKED* scope global dynamic noprefixroute
</span></span><span style="display:flex;"><span> valid_lft 6866sec preferred_lft 6866sec
</span></span><span style="display:flex;"><span> inet6 *MASKED* scope global mngtmpaddr noprefixroute
</span></span><span style="display:flex;"><span> valid_lft forever preferred_lft forever
</span></span><span style="display:flex;"><span> inet6 *MASKED* scope global dynamic mngtmpaddr noprefixroute
</span></span><span style="display:flex;"><span> valid_lft 184874sec preferred_lft 98474sec
</span></span><span style="display:flex;"><span> inet6 fe80::dea6:32ff:fe0f:c62c/64 scope link
</span></span><span style="display:flex;"><span> valid_lft forever preferred_lft forever
</span></span><span style="display:flex;"><span>3: wlan0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000
</span></span><span style="display:flex;"><span> link/ether *MASKED* brd ff:ff:ff:ff:ff:ff
</span></span><span style="display:flex;"><span> inet 192.168.0.2/24 brd 192.168.0.255 scope global dynamic wlan0
</span></span><span style="display:flex;"><span> valid_lft 7200sec preferred_lft 7200sec
</span></span><span style="display:flex;"><span> inet6 *MASKED* scope global dynamic noprefixroute
</span></span><span style="display:flex;"><span> valid_lft 7197sec preferred_lft 7197sec
</span></span><span style="display:flex;"><span> inet6 *MASKED* scope global dynamic noprefixroute
</span></span><span style="display:flex;"><span> valid_lft 7197sec preferred_lft 7197sec
</span></span><span style="display:flex;"><span> inet6 *MASKED* scope global mngtmpaddr noprefixroute
</span></span><span style="display:flex;"><span> valid_lft forever preferred_lft forever
</span></span><span style="display:flex;"><span> inet6 *MASKED* scope global dynamic mngtmpaddr noprefixroute
</span></span><span style="display:flex;"><span> valid_lft 184874sec preferred_lft 98474sec
</span></span><span style="display:flex;"><span> inet6 fe80::dea6:32ff:fe0f:c62d/64 scope link
</span></span><span style="display:flex;"><span> valid_lft forever preferred_lft forever
</span></span></code></pre></div><h2 id="离线模式配置网络">离线模式配置网络</h2>
<p>今天遇到一个情况,我本地没有有线网,但是无线网可用,那我肯定是需要配置这个无线网让它能连上</p>
<p>那照葫芦画瓢,我们把 wlan 的配置文件扔上去,配置一下服务就好了</p>
<p>首先 <code>fdisk -l</code> 确定一下树莓派是哪个分区</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-plain" data-lang="plain"><span style="display:flex;"><span># fdisk -l
</span></span><span style="display:flex;"><span>Disk /dev/sdb: 29.72 GiB, 31914983424 bytes, 62333952 sectors
</span></span><span style="display:flex;"><span>Units: sectors of 1 * 512 = 512 bytes
</span></span><span style="display:flex;"><span>Sector size (logical/physical): 512 bytes / 512 bytes
</span></span><span style="display:flex;"><span>I/O size (minimum/optimal): 512 bytes / 512 bytes
</span></span><span style="display:flex;"><span>Disklabel type: dos
</span></span><span style="display:flex;"><span>Disk identifier: 0xfcb88066
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>Device Boot Start End Sectors Size Id Type
</span></span><span style="display:flex;"><span>/dev/sdb1 2048 1050623 1048576 512M c W95 FAT32 (LBA)
</span></span><span style="display:flex;"><span>/dev/sdb2 1050624 62333951 61283328 29.2G 83 Linux
</span></span></code></pre></div><p>然后把树莓派的根目录挂载上来,这里是 <code>/dev/sdb2</code></p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-plain" data-lang="plain"><span style="display:flex;"><span># mount /dev/sdb2 /mnt
</span></span></code></pre></div><p>然后我们把 wlan 配置写到 <code>/mnt/etc/wpa_supplicant/wpa_supplicant-wlan0.conf</code> 里面去,这里还是用之前的那个例子</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-plain" data-lang="plain"><span style="display:flex;"><span>network={
</span></span><span style="display:flex;"><span> ssid="MyNetwork"
</span></span><span style="display:flex;"><span> #psk="SuperSecretPassphrase"
</span></span><span style="display:flex;"><span> psk=c370fef3b644bcf284350cdd6b74fae476847621731b64dd6829f4a54f33bd15
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>而后照葫芦画瓢,搞一个 service 出来</p>
<pre tabindex="0"><code class="language-conf" data-lang="conf"># /mnt/etc/systemd/system/[email protected]
[Unit]
Description=WPA supplicant daemon (interface-specific version)
Requires=sys-subsystem-net-devices-%i.device
After=sys-subsystem-net-devices-%i.device
Before=network.target
Wants=network.target
# NetworkManager users will probably want the dbus version instead.
[Service]
Type=simple
ExecStart=/usr/bin/wpa_supplicant -c/etc/wpa_supplicant/wpa_supplicant-%I.conf -i%I
[Install]
Alias=multi-user.target.wants/wpa_supplicant@%i.service
</code></pre><p>然后把这个 service 扔到开机启动项里面</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-plain" data-lang="plain"><span style="display:flex;"><span># ln -s /mnt/etc/systemd/system/[email protected] /mnt/etc/systemd/system/multi-user.target.wants/
</span></span></code></pre></div><p>记得把 wlan0 的配置文件放到 <code>/mnt/etc/systemd/network</code> 中,具体可以参见上面的那节</p>
<p>这样就基本完成了,插上卡通电开机<del>默念 Arch linux 大法好</del>,就会看到它连上 wlan 了。</p>
-
A+B Problem
https://leanhe.dev/posts/2021.04.01.1/
Thu, 01 Apr 2021 10:16:54 +0800
https://leanhe.dev/posts/2021.04.01.1/
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span>print(sum(map(int, input()<span style="color:#f92672">.</span>split())))
</span></span></code></pre></div><hr>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-rust" data-lang="rust"><span style="display:flex;"><span><span style="color:#66d9ef">use</span> std::io::{stdin, BufRead};
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">fn</span> <span style="color:#a6e22e">main</span>() {
</span></span><span style="display:flex;"><span> println!(<span style="color:#e6db74">"</span><span style="color:#e6db74">{}</span><span style="color:#e6db74">"</span>, stdin().lock().lines().next().unwrap().map(<span style="color:#f92672">|</span>x<span style="color:#f92672">|</span> x.trim_end().to_owned()).unwrap()
</span></span><span style="display:flex;"><span> .split_whitespace().into_iter().map(<span style="color:#f92672">|</span>x<span style="color:#f92672">|</span> x.parse::<span style="color:#f92672"><</span><span style="color:#66d9ef">i32</span><span style="color:#f92672">></span>().unwrap()).sum::<span style="color:#f92672"><</span><span style="color:#66d9ef">i32</span><span style="color:#f92672">></span>());
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><hr>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#f92672">import</span> java.io.*;
</span></span><span style="display:flex;"><span><span style="color:#f92672">import</span> java.util.*;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">public</span> <span style="color:#66d9ef">class</span> <span style="color:#a6e22e">Main</span> {
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">public</span> <span style="color:#66d9ef">static</span> <span style="color:#66d9ef">void</span> <span style="color:#a6e22e">main</span>(String<span style="color:#f92672">[]</span> args) {
</span></span><span style="display:flex;"><span> Scanner scanner <span style="color:#f92672">=</span> <span style="color:#66d9ef">new</span> Scanner(System.<span style="color:#a6e22e">in</span>);
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">int</span> a <span style="color:#f92672">=</span> scanner.<span style="color:#a6e22e">nextInt</span>();
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">int</span> b <span style="color:#f92672">=</span> scanner.<span style="color:#a6e22e">nextInt</span>();
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> System.<span style="color:#a6e22e">out</span>.<span style="color:#a6e22e">println</span>(a <span style="color:#f92672">+</span> b);
</span></span><span style="display:flex;"><span> scanner.<span style="color:#a6e22e">close</span>();
</span></span><span style="display:flex;"><span> }
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div>