驱动文件由zadig/libwdi生成

需要先安装Visual Studio 2022, Windows SDK, Windows Driver Kit (WDK), 参考: 下载 Windows 驱动程序工具包 (WDK)

inf2cat, signtool 等工具建议在 Developer Command Prompt for VS 2022中操作

  1. 生成自签证书
1
New-SelfSignedCertificate -Subject "CN=<Name>" -CertStoreLocation "Cert:\CurrentUser\My" -KeyAlgorithm RSA -KeyLength 4096 -HashAlgorithm SHA256 -NotBefore (Get-Date) -NotAfter (Get-Date).AddYears(10) -TextExtension @("2.5.29.37={text}1.3.6.1.5.5.7.3.3", "2.5.29.17={text}email=<email>") -KeyUsage None

关于 New-SelfSignedCertificate,参考:New-SelfSignedCertificate (PKI) | Microsoft Learn

其中 2.5.29.37 是增强型密钥用法(enhanced key usage), 1.3.6.1.5.5.7.3.3 是代码签名(code signing). 2.5.29.17 是使用者可选名称(subjectAltName). New-SelfSignedCertificate 命令有一个 -DNSName 参数似乎不支持email这样细粒度级别的配置.

另外命令还支持 -Type 参数, 指定为 CodeSigningCert 时生成出来的证书, “增强型密钥用法”前面会有一个叹号, 不确定是为什么所以最后改成TextExt这种模式了.

-KeyUsage None 可以避免证书附带上默认用途.

  1. 导出为PFX文件
1
2
$password = ConvertTo-SecureString -String "<redacted>" -Force -AsPlainText
Export-PfxCertificate -Cert "Cert:\CurrentUser\My\<Thumbprint>" -FilePath "<filename>" -Password $password
  1. 生成catalog文件
1
inf2cat /v /driver:<dir> /os:XP_X86,XP_X64,Vista_X86,Vista_X64,7_X86,7_X64

注意,这里的/os 参考: Inf2Cat - Windows drivers | Microsoft Learn

一份更全的列表可以参考: Inf2Cat 工具

这个os字符串是根据zadig/libwdi生成出来的catalog文件的安全目录OSAttr属性推出来的,OSAttr列表参见:List of Microsoft Windows versions - Wikipedia

libwdi源码中使用的是 7_X86,7_X64,8_X86,8_X64,8_ARM,10_X86,10_X64,10_ARM 但用这个字符串签出来的文件不太对, 不是很确定原因.

若要使用_ARM参数, 需要保证安装了对应架构的构建工具, 否则inf2cat会报错参数错误.

  1. 对catalog文件签名
1
signtool sign /fd SHA256 /f "<pfx file path>" /p <redacted> /t http://timestamp.digicert.com <cat file path>
  1. 导出一个CA证书cer文件随安装包发行
1
Export-Certificate -Cert "Cert:\CurrentUser\My\<Thumbprint>" -FilePath "<filename>"
  1. 打包,制作自动安装脚本 install-drivers.bat

因为certutil需要用admin权限, 加一段自动提权UAC

1
2
3
4
5
6
7
8
9
10
11
@echo off
:: Check for admin rights
NET SESSION >nul 2>&1
IF %ERRORLEVEL% NEQ 0 (
echo Requesting Administrator privileges...
powershell -Command "Start-Process cmd -Verb RunAs -ArgumentList '/c %~fnx0'"
exit
)

C:\Windows\System32\certutil.exe -addstore -enterprise -f -v root "%~dp0\DriverSigner.cer"
C:\Windows\System32\InfDefaultInstall.exe "%~dp0\drivers.inf"

最近在给办公室配置网络, 要求访问国内网站时走国内DNS解析+国内运营商直出, 访问国外网站时走国外DNS解析+专线转发.

流量分流

整体思路上是根据IP段/CIDR进行分流, 由于主要访问国内网站, 整理了一份海外IP列表. 命中列表的IP请求走专线转发.

创建ipset: ipset create overseas hash:net 其中 overseas 是名字, 可以修改为别的.

添加IP段到ipset中: ipset add overseas 1.1.64.0/18

使用iptables标记流量, 这里的 2 是对流量添加的 fwmark, 可以选择任意值.

1
2
3
iptables -t mangle -A PREROUTING -m set --match-set overseas dst -j MARK --set-mark 2
iptables -t mangle -A OUTPUT -m set --match-set overseas dst -j MARK --set-mark 2
iptables -t nat -A POSTROUTING -m mark --mark 2 -j MASQUERADE

添加一个独立的路由表, 将默认路由修改为专线. 这里的 55 是 TableID, 可以选择任意值.

1
ip route add default via 10.10.0.1 table 55

添加ip rule, 将fwmark和路由表关联起来:

1
ip rule add fwmark 2 table 55

这样, 从本机和其他网络传递过来的包, 如果命中海外IP列表就会走专线转发. 注意, 如果要给其他网络提供功能(即作为路由器), 需要添加NAT, 即:

1
2
sysctl net.ipv4.ip_forward=1
iptables -t nat -A POSTROUTING -s 192.168.88.0/24 -o <通往互联网的网卡> -j MASQUERADE

这样发往互联网的数据包的源IP就是互联网网卡的IP, 而不是内网IP.

DNS分流

可选的方案有CoreDNS, Dnsmasq, Pihole, BIND9, PowerDNS等等. 在权衡之后选择了BIND9方案.

(Github上有一个SmartDNS项目也可以实现类似的功能, 但是多重考虑下使用了比较原始的方案)

  1. 安装BIND9
1
sudo apt update && sudo apt install bind9 bind9utils bind9-doc
  1. 编写配置文件 /etc/bind/named.conf.options

注意, 这个配置关闭了DNSSEC. 不然会出现类似 broken trust chain resolving 'github.com/A/IN': 8.8.8.8#53 的报错. (当然更好的解决办法是配置DNSSEC)

默认使用了腾讯云, 阿里云, 114的公共DNS. 日志输出到了syslog, 可以通过 journalctl 检索.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
options {
directory "/var/cache/bind";

dnssec-validation no;

listen-on-v6 { any; };

max-cache-size 512M; // Limit cache size to prevent memory overuse
max-cache-ttl 86400; // Maximum time to keep cached records (1 day)
max-ncache-ttl 3600; // Negative caching time (1 hour)

recursion yes;
allow-recursion { any; };

forwarders {
119.29.29.29; 223.5.5.5; 114.114.114.114;
};

forward only;
};

logging {
channel default_log {
# file "/var/log/named.log";
syslog daemon;
severity debug;
print-time yes;
};
category queries { default_log; };
};
  1. 编写Zone文件 /etc/bind/named.conf.whitelist-zones
1
2
3
4
5
6
7
8
9
10
11
zone "docker.com" {
type forward;
forward only;
forwarders { 8.8.8.8; 1.1.1.1; };
};
zone "github.com" {
type forward;
forward only;
forwarders { 8.8.8.8; 1.1.1.1; };
};
...

可以通过python脚本比较方便的生成zone文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
import subprocess
import traceback


with open("domains.txt") as f:
content = f.read()

lines = content.split('\n')
lines = list(set([line.strip() for line in lines if line and not line.strip().startswith('#')]))
print("{} domains imported".format(lines))

with open("/etc/bind/named.conf.whitelist-zones", "w") as f:
for line in lines:
f.write(f'''zone "{line}" {{
type forward;
forward only;
forwarders {{ 8.8.8.8; 1.1.1.1; }};
}};
''')

print("New config generated. Validating...")
subprocess.check_call(["named-checkconf", "/etc/bind/named.conf"])

print("Reloading DNS server...")
subprocess.check_call(["systemctl", "reload", "named"])

print("Reload complete. Showing latest logs... (Ctrl+C to close)")
subprocess.call(["journalctl", "-t", "named", "-f"])
  1. 重启BIND服务器以使配置生效
1
sudo systemctl restart named

如果配置没有问题的话可以看到named输出的日志:

1
2
3
4
5
6
7
Mar 05 13:36:19 office-server named[6387]: managed-keys-zone: loaded serial 7
Mar 05 13:36:19 office-server named[6387]: zone 255.in-addr.arpa/IN: loaded serial 1
Mar 05 13:36:19 office-server named[6387]: zone 0.in-addr.arpa/IN: loaded serial 1
Mar 05 13:36:19 office-server named[6387]: zone 127.in-addr.arpa/IN: loaded serial 1
Mar 05 13:36:19 office-server named[6387]: zone localhost/IN: loaded serial 2
Mar 05 13:36:19 office-server named[6387]: all zones loaded
Mar 05 13:36:19 office-server named[6387]: running

有解析请求时会输出日志:

1
Mar 05 13:37:16 office-server named[6387]: 05-Mar-2025 13:37:16.472 client @0x7a7f2c005368 <...>#25256 (main.vscode-cdn.net): query: main.vscode-cdn.net IN A + (...)

可以通过以下命令来测试DNS响应

1
dig +short @127.0.0.1 baidu.com

参考

dnsmasq ipset iptables 实现对流量进行分流 很棒的文章,思路很清晰一下就明白了

How To Configure BIND as a Private Network DNS Server on Ubuntu 20.04

巧用 DNS 实现国内外域名 ip 分流上网

Openwrt使用SmartDNS进行国内外DNS分流,提高访问速度,防止DNS污染和泄露。

最近川普币超级火, 在川普币出来的第二天我耐不住寂寞, 开始着手研究起了加密货币, 虽然亏了一些钱但是整个过程我觉得还是很好玩的, 值得记录一下.

在这之前我对加密货币完全不了解, 只听过说 Bitcoin, ETH. 很贵很值钱, 价格很容易变动, 好多人做杠杆倾家荡产, 也有人一夜暴富.

加密货币在很长时间以来一直称自己有匿名性, 去中心化交易不易追踪等等. 但对于一个初入加密货币市场的新人来说, 最开始一定是从一个中心化的地方(交易所)开始, 因为在最开始的时候你只有钱, 而没有币. 在cypto的世界里, 他们把由政府发行的钱成为 Fiat. 现实世界的经济体系称为 CeFi, 虚拟(去中心化)货币世界的经济体系称为 DeFi.

中心化交易所(CEX, Centralized Exchange)是受到一个或多个地区金融机构监管的交易所, 可以在这里通过钱买入币或者把币换成钱, 交易所也提供像去中心化交易所(DEX, Decentrailized Exchane)一样的币与币之间的买卖及兑换. 中心化交易所一般要求用户通过基本的KYC (Know Your Customer, 包括但不限于身份信息采集, 真人验证等等), 而去中心化交易所没有任何这方面的要求.

Onboarding完成后, 平台一般会提供新手教程一样的购买流程, 先引导用户绑定信用卡或借记卡并充值法币(比如美元), 然后会推荐购买300 USD的BTC或者ETH. 这里对于新手来说可能是一个坑点. 以Coinbase为例, 这种新手向的购买(有的平台叫Convert) 使用的价格相对当时的市场价偏低, 另外暗藏了一些手续费(比如Platform Fee, 有时候甚至高达9USD).

购买完成后, 此时虚拟货币就会以余额形式显示在平台上. 此处有一定的迷惑性, 平台上虚拟货币的余额并不是钱包的余额. 那什么是钱包呢?

钱包其实就是一个地址, 一个可公开的独立的标识符. 用户通过这个标识符就可以向其发送虚拟货币, 但是不能只根据这个钱包地址就从中提取余额.

这里一下子引入了好几个概念, 首先需要了解一下区块链是什么, 简单来说区块链就是一个由单位区块链接起来的链表. 以Bitcoin的区块链模式为例, 当前单位区块包含三个部分: 上一个区块的标识符, 当前区块正文, 当前区块的Proof-of-Work证明.

区块正文内容可以简单理解为转账记录, 比如 钱包A 向 钱包B 转账了 0.1 BTC. 钱包 C 向 钱包D 转账了 0.25 BTC. 比特币区块链上的矿工(Miner) 会监听这些交易内容, 将一定数量的交易记录打包成一个区块, 并计算一个符合要求的Hash (例如开头13位都是0的sha256值) 这个概率会很低, 整个比特币网络会控制在约10分钟产生一个新的区块 (因为sha256不可逆的特性只能通过穷举计算) 当某个矿工刚好完成了新区块的计算后, 他会将这个区块签名并广播到比特币网络中. 其他矿工会验证Hash是否正确(这个过程很快), 如果正确就将这个区块加入到自己本地的区块链末端, 并开始下一个区块的计算. 由于每一个区块都与前一个区块紧密相关, 因此在区块链中对前面的区块进行修改, 意味着后面的区块就全部失效了, 从而实现了区块链不可更改的特性.

由于每个矿工都会监听网络上的交易并尝试生产一个新的区块, 有时会在网络上产生多个区块, 一部分矿工本地的区块链与另一部分矿工本地的区块链不一样. 比特币区块链对此的解决方案是选择最长的. 例如矿工X本地存储的是 A->B->C->D, 矿工Y本地存储的是 A-B->C->E->F, 则矿工X在收到网络中其他矿工发送的新区块时, 发现收到的比本地更长, 就放弃掉本地的区块. 基于这种算法, 比特币网络可以实现区块链的最终一致性.

比特币网络上产生的交易如果想记录下来就必须有矿工投入工作, 为了奖励矿工的付出, 比特币网络会允许产生区块的矿工在自己生产出来的区块中添加一条转账记录, “凭空”创造出一些比特币用来奖励自己. 这个奖励的数量随着区块数量的增长逐渐递减, 进而保证比特币供应总量是固定的. 看到这里不得不佩服一下这些聪明人发明出来的东西.

注: 以上只是我对比特币区块链的浅显理解, 主要内容来自3b1b的这个视频: But how does bitcoin actually work? 强烈推荐看一下

除了PoW(Proof-of-Work)这种形式, 还有PoS(Proof-of-Stake)模式的区块链. 不同区块链使用的算法也不尽相同, 但核心都是保证 区块链历史不可修改, 需要有一些人来做一些操作记录下交易记录, 并提供一些奖励, 同时还有一定方法实现整个网络的最终一致性.

话说回来, 对于不参与挖矿的投资者来说(比如最开始作为新手的我), 需要了解两个最基本的概念: 币种 和 区块链. 币种就是虚拟货币的名字, 例如 BTC, ETH, SOL 等等. 区块链就是由不同算法区分出来的交易记录册. 在发送和接受虚拟货币的时候, 需要保证币种和链都完全匹配才能, 否则虚拟货币会丢失. 这也很好理解, 不同区块链的算法不同, 对地址、交易的解释可能也完全不同.

一些常见的虚拟货币: 币种是SOL, 区块链(也叫网络)是Solana; 币种 ETH, 区块链Ethereum. 币种 ETH, 区块链BASE. 币种 POL, 区块链 Polygon, 币种 BTC, 区块链 Bitcoin.

还有一些稳定币, 即发行方称可以1:1从法币进行兑换, 例如 USDT, USDC. 这些稳定币一般会在多个链上发行, 但需要注意在发送和接受虚拟货币时仍然要保证币种和链都完全匹配.

同一币种在同一网络内 两个钱包互相转账, 一般称作链内交易. 不同区块链有不同的算法规定手续费的多少. 也有一些区块链是免手续费的(例如Solana)

同一币种在不同网络内 不能直接转账, 需要通过 Bridge 进行交易. 例如将 USDT 从 ETH网络 发送到 Solana网络. 一个比较简单的理解方式是, bridge分别在两个网络内都有一个钱包地址, 用户向ETH网络内的钱包地址发送USDT, bridge验证无误后向Solana网络内的用户钱包地址发送USDT. 这个过程中, 发送网络内可能要计费一次, bridge本身可能要收手续费, bridge在接收网络内发送可能要计费一次.

不同币种间不能直接转账(当然不能…) 与现实世界的汇率相似, 在交易所(CEX或DEX)人们通过不同虚拟货币之间价值的认识, 通过买入和卖出的操作定义虚拟货币间的汇率. 与股票市场不同的是, 加密货币市场是24小时开放的(当然美股也有盘前和盘后交易), 而且因为人们对虚拟货币价值认识的偏差, 价格可能会短期内剧烈波动, 有时候甚至会超出预期. 交易所一般会收取手续费.

目前为止, 虚拟货币还是停留在CEX上. 对于需要频繁交易的人来说这倒没什么, 但如果希望在DEX上交易, 甚至将虚拟货币保存在自己手里, 则需要一个钱包. 可以通过类似Phantom这样的软件来实现. 钱包软件会提供一个或若干个地址, 对应所支持的不同币种在不同链上的钱包地址. 钱包地址其实是从钱包私钥生成出来的, 而钱包私钥是随机生成的, 取值空间非常大重叠概率极低 (以Solana网络举例, 私钥和地址长度均为32B, 即256bit) 钱包一般是离线软件, 即秘钥不会同步到任何地方. 为了方便用户恢复钱包, 比特币社区制定了一些早期标准并逐渐演化为由12个或更多个助记词组成的数组. 每个助记词对应私钥的一部分, 通过助记词即可恢复出私钥, 进而推导出公钥和钱包地址. 由于区块链去中心化的特质, 钱包丢失后没有任何其他人能恢复出私钥, 丢失即是永久的. 同样的, 由于私钥是对应钱包的唯一凭证, 暴露私钥意味着暴露钱包的控制权, 钱包里的虚拟货币将处于危险, 唯一的解决办法是及时转移到其他钱包.

(当然目前一些平台也推出了可以只通过密码就恢复的钱包, 这取决去其实现方式, 是中心化存储了钱包私钥还是通过KDF类似的方式衍生出钱包私钥, 在此就不深入研究了)

从CEX提取虚拟货币到钱包一般称为 Withdraw, 受到CEX平台的管制. 平台可能出于金融安全、税务追踪等原因要求验证目标钱包的所属人身份.

从钱包发送虚拟货币到CEX一般称为 Deposit. 一般不受管制, 但CEX平台可能会需要额外的确认及等待时间, 保证虚拟货币已经被平台侧接收到.

一些常用的工具:

CEX交易所: Binance, Coinbase

钱包: Phantom

DEX工具: Jupiter 可以直接在浏览器内连接到钱包.

  • 补充一点, 有的时候一些新的虚拟货币在CEX上没有上线时, 就只能通过DEX来交易.
  • Jupiter 是 Solana网络 上推荐的工具. 其他网络可能有不一样的工具.

市场分析与数据浏览:

CoinmarketCap 很全面的一个面板

DEX Tools Solana网络和其他网络上流行的币种信息

DEX Screener 类似DEXTools的工具

pump.fun Meme Coin相关的信息大全

SonarWatch 通过一个或多个的钱包地址来总览投资收益

DefiLama 各种虚拟货币及交易所相关的数据网站

SolScan Solana网络上交易、钱包等搜索网站. 由于区块链本身数据都是公开的, 其他区块链也有类似的工具. 你可以搜索到任意一个发生过的交易.

以上是整个过程我学习到的一些东西, 最后简单说说TRUMP币. TRUMP币是一个Meme Coin, 发行在Solana网络上. Meme Coin通常是没有任何实体支撑的虚拟货币, 几乎没有任何发行成本, 其价格高低完全取决于市场本身的热情和看法. 如果说比特币某种程度上还可以被称为价值投资的话, Meme Coin更像是令人热血膨胀的赌博. 在TRUMP币还没有被CEX列入名单中时, 只能先购买SOL再通过DEX交换为TRUMP Coin.

大部分Meme币最终都将一文不值, 加密货币有风险, Meme币风险更大, 投资需谨慎! DYOR(Do Your Own Research)!

问题现象:

Windows 11 + WSL2 + Ubuntu分发, 主机上启动VPN. WSL2内使用docker运行容器. curl http站点没问题, curl https站点显示 Connected to... 并且header已发送, 但是收不到响应.

推测是MTU导致的. 因为curl http的响应是301, body很小, header也很小, 但是curl https要返回的内容可能比较多所以丢包了.

分别在wsl2下安装tcpdump并监听ethdocker0接口, 在windows下安装wireshark并监听VPN网卡(tun模式).

在docker内发起http请求, 可以看到docker0接口正常, eth接口正常, wireshark能看到请求和响应.

在docker内发起https请求, 可以看到docker0接口有请求报文, eth接口只有握手报文, wireshark能看到请求报文.

通过 ip a 命令查看接口, 可以发现WSL2内eth接口mtu为1370, docker0的mtu为1500. 推测MTU不匹配且没开启PMTU导致丢包.

解决方法是配置docker使其mtu变为1370, 方法如下:

1
sudo vim /usr/lib/systemd/system/docker.service

在配置文件中, 将ExecStart=...这一行改为:

1
2
3
...
ExecStart=/usr/bin/dockerd --mtu 1370 -H fd:// --containerd=/run/containerd/containerd.sock
...

然后重新启动docker服务:

1
sudo systemctl daemon-reload && sudo systemctl restart docker

再访问https就正常了.

Win11这个右键之后还要【显示更多选项】实在是让人费解. 参考以下方法恢复至原来的右键菜单行为:

Win+X, A 打开终端 (powershell也可以)

1
2
3
reg.exe add "HKCU\Software\Classes\CLSID\{86ca1aa0-34aa-4e8b-a509-50c905bae2a2}\InprocServer32" /f /ve
taskkill /F /IM explorer.exe
explorer.exe

其中第二步和第三步也可以通过任务管理器来重启【Windows资源管理器】

参考

Restore old Right-click Context menu in Windows 11

注意:请务必做好数据备份,本文的前提是对Proxmox中的VM操作, 利用了PVE的备份和还原能力.

  1. 创建一个一样大小的硬盘并挂载到VM.

  2. 选择一个live系统,这里我们选择 Ubuntu Desktop 24.04.1 LTS

  3. 将live盘设置为启动盘,启动系统,选择 Try Ubuntu 进入 live 环境

  4. 由于我原来的LVM系统盘有三个分区 1M, 1G的/boot, 199G的lvm数据分区,所以需要先拷贝系统,再把boot部分的补上, 具体操作如下:

1
2
3
4
5
6
7
8
...
sda 8:0 0 200G 0 disk
|- sda1 8:1 0 1M 0 part
|- sda2 8:2 0 1G 0 part
|- sda3 8:3 0 199G 0 part
|- ubuntu--vg-ubuntu--lv 252:0 0 199G 0 lvm
sdb 8:16 0 200G 0 disk
...

切换至root用户 sudo su 由于是live系统不需要输入密码

创建几个mount点:

1
2
3
mkdir /mnt/src
mkdir /mnt/srcboot
mkdir /mnt/dst

初始化新磁盘(这里 /dev/sda是原来的磁盘, /dev/sdb 是新的磁盘)

1
2
3
4
5
6
7
8
9
gdisk /dev/sdb
输入 p 查看分区, 当前应该没有分区
输入 n 新建分区
分区类型选择 p (primary)
起始位置 2048 终止位置 4095 HexCode设置为 EF02 (BIOS boot partition)
输入 n 新建分区
分区类型选择 p (primary)
起始位置 4096 终止位置, HexCode保持默认 (8300, Linux filesystem)
输入 w 将改动写入到磁盘

如果原来的磁盘不是GPT格式可以尝试如下命令:

1
2
3
4
5
6
7
fdisk /dev/sdb
输入 p 查看分区, 当前应该没有分区
输入 n 新建分区
分区类型选择 p (primary)
起始位置, 大小 等等全部选择默认
新建完成分区后输入 a 将分区标记为引导分区
输入 w 将改动写入到磁盘

在新的磁盘上创建文件系统

1
2
mkfs -t ext4 /dev/sdb2
注意如果没有创建 BIOS boot 分区, 应该使用 /dev/sdb1

挂载磁盘

1
2
3
mount -o ro /dev/sda2 /mnt/srcboot
mount -o ro /dev/mapping/ubuntu--vg-ubuntu--lv /mnt/src
mount /dev/sdb1 /mnt/dst

安装pv工具以便查看复制进度

1
apt update && apt install pv

开始拷贝数据

1
2
tar -C /mnt/src --preserve-permissions --xattrs --xattrs-include=*.* --numeric-owner -c . | pv | tar -xf - -C /mnt/dst --preserve-permissions --xattrs --xattrs-include=*.* --numeric-owner
tar -C /mnt/srcboot --preserve-permissions --xattrs --xattrs-include=*.* --numeric-owner -c . | pv | tar -xf - -C /mnt/dst/boot --preserve-permissions --xattrs --xattrs-include=*.* --numeric-owner

其中 --perserve-permission 是为了保留权限, 对于 root 用户来说是默认的, 但是为了保险起见还是加上

--xattrs, --xattrs-include=*.* 启用额外属性支持, 并拷贝全部属性. 这样类似/usr/bin/ping等文件的 capability 也会被保存下来.

--numeric-owner 要求 tar 使用数字UID/GID 而不是用户名

给新磁盘文件系统下挂载分区

1
2
cd /mnt/dst
for i in /dev /dev/pts /proc /sys /run; do mount --bind $i .$i; done

修改 /etc/fstab,保证里面 /dev/disk/by-uuid/ 对应的磁盘和新磁盘一致. 通过 lsblk -o NAME,uuid 来查看磁盘的uuid

1
nano /mnt/dstetc/fstab

切换rootfs,安装grub. 再运行 update-grub 更新 grub 配置

1
2
3
4
cd /mnt/dst
chroot .
grub-install /dev/sdb
update-grub

退出 chroot, 关闭系统 shutdown now

  1. 弹出 live CD 和原来的磁盘 (在PVE中对应remove和detach)

  2. 重新启动新的系统,可能会遇到几个问题:

grub 菜单展示了 Ubuntu 但是回车进去报错 you need to load kernel first
这种情况应该是因为当前grub标签 (hd0,gpt2) 跟现实不匹配导致的. (也可能是前面忘记执行了update-grub)
在选择页面按下 c 进入 grub shell

1
2
3
4
5
6
7
8
9
ls -l 确认当前磁盘列表和磁盘的UUID
set prefix=(hd0,gpt2)/boot/grub 注意如果不是GPT格式应该用(hd0,msdos1)
set root=(hd0,gpt2)
insmod normal

insmod linux
linux /boot/vmlinuz root=UUID={磁盘的UUID}
initrd /boot/initrd.img
boot

此时应该就会进入系统了

  1. 如果前面忘了改 /etc/fstab 此时进入新系统后会卡在两个任务上:
1
2
Reached target ....
[ ***] Job dev-disk-by\x2duuid-.....device/start running (9s / 1min 30s)

这个没什么好办法,会卡两次,等三分钟就好了。进入系统后记得把 /etc/fstab 改了,运行 sudo grub-install && sudo update-grub 然后再重启应该就没问题了.

参考

Live-cloning a running Linux installation to a new machine

grub error: you need to load kernel first

How do I list my devices in GRUB?

[SOLVED] Creating a BIOS boot partition using gdisk

GPT fdisk - ArchWiki

How to Install Grub Bootloader in Linux

VMware workstation似乎有个bug, 当添加新的SCSI磁盘时VM内不会显示新的 /dev/ 块设备, 例如:

1
2
3
4
NAME   MAJ:MIN RM  SIZE RO TYPE MOUNTPOINTS
sda 8:0 0 50G 0 disk
├─sda1 8:1 0 1M 0 part
└─sda2 8:2 0 50G 0 part /

此时通过VMware控制台添加一个200G的磁盘, 再运行 lsblk 并不会有任何变化.

需要执行以下脚本”刷新”:

1
2
3
for h in $(ls /sys/class/scsi_host); do
echo '- - -' > /sys/class/scsi_host/$h/scan
done

再运行 lsblk 就可以看到磁盘了:

1
2
3
4
5
NAME   MAJ:MIN RM  SIZE RO TYPE MOUNTPOINTS
sda 8:0 0 50G 0 disk
├─sda1 8:1 0 1M 0 part
└─sda2 8:2 0 50G 0 part /
sdb 8:16 0 200G 0 disk

参考

[SOLVED] Virtual Hard Disk is added, but not showing using lsblk -d command

开发机从 Ubuntu Server 20.04.6 升级到 Ubuntu Server 24.04.1 LTS 版本, 没遇到什么大问题. 但是发现 podman 变成了 [installed,local] 版本, 没有被自动升级. 但是看了一下容器,镜像,Volume还在.

  1. 卸载当前版本

我之前用的是kubic社区的podman源, 需要先卸载 sudo apt remove podman 再删除掉apt能识别出来不再需要的依赖: sudo apt autoremove

此时如果直接安装新版本 podman 可能会有问题, 提示 /etc/containers/containers.conf 被包 containers-common 使用.

可以尝试: sudo apt remove containers-common buildah crun. 卸载前通过 sudo apt list --installed | grep installed,local 确认是否为本地版本.

还是不行的话就只能手动逐个卸载了:

1
2
3
sudo dpkg --remove crun
sudo dpkg --remove podman
sudo dpkg --remove containers-common

总之需要保证之前的版本都卸载掉. 不需要(也不要)运行 purge. 会丢失数据.

  1. 安装新版本

sudo apt install podman golang-github-containernetworking-plugin-dnsname

其中 golang-github-containernetworking-plugin-dnsname 这个不清楚为什么在 Ubuntu 24.04 源里名字这么奇怪, 根据文档来看应该是 podman-plugins 或者 podman-dnsname 才对. Podman 4.x 版本删除了 dnsname 这个插件, 导致通过 compose 拉起的容器组之间不能直接通过 service 名字做寻址.

这个名字是通过 sudo apt search dnsname 找到的.

如果不安装这个包, 可能会看到类似下面的错误:

1
WARN[0000] Error validating CNI config file /home/kiritow/.config/cni/net.d/wgop-net-wg0.conflist: [failed to find plugin "dnsname" in path [/usr/local/libexec/cni /usr/libexec/cni /usr/local/lib/cni /usr/lib/cni /opt/cni/bin]]

装完之后在 /usr/lib/cni 下应该会看到 dnsname 插件.

  1. 安装Docker-Compose

由于Docker Compose V2已经从pip包升级合并到docker命令了, 因此我们需要使用docker官网提供的docker-compose独立安装包: Install Compose standalone

注意: 之前提到的 Ubuntu 22.04 里面 containernetworking-plugins 的问题似乎已经解决了, 现在安装 podman 会正确的安装这个依赖包. 参考之前的文章

参考

containers/dnsname: name resolution for containers 这个插件现在已经archive了.

下面的参考没太大价值, 更多感觉还是要看 dnsname 这个插件的文档: Using the dnsname plugin with Podman

Podman network dns option not working with the DNS plugin enabled #20911

Impossible to override container’s DNS with network #17499

Small issues (and fixes) when moving to Ubuntu 23.04

Podman dnsname install issues