MoreRSS

site iconLala | 荒岛修改

一个应用分享、教程类的博客,主要是那些需要自部署的。
请复制 RSS 到你的阅读器,或快速订阅到 :

Inoreader Feedly Follow Feedbin Local Reader

Lala | 荒岛的 RSS 预览

Termix:开源的一体化服务器管理平台

2025-10-07 06:33:29

Termix是一个开源、永久免费、自托管的一体化服务器管理平台。它提供了一个基于网页的解决方案,通过一个直观的界面管理你的服务器和基础设施。Termix提供SSH终端访问、SSH隧道功能以及远程文件编辑,还会陆续添加更多工具。

目前已实现的功能:

SSH 终端访问 – 功能完整的终端,支持分屏(最多 4 个面板)和标签系统
SSH 隧道管理 – 创建和管理 SSH 隧道,支持自动重连和健康监控
远程文件编辑器 – 直接在远程服务器编辑文件,支持语法高亮和文件管理功能(上传、删除、重命名等)
SSH 主机管理器 – 保存、组织和管理 SSH 连接,支持标签和文件夹
服务器统计 – 查看任意 SSH 服务器的 CPU、内存和硬盘使用情况
用户认证 – 安全的用户管理,支持管理员控制、OIDC 和双因素认证(TOTP)
现代化界面 – 使用 React、Tailwind CSS 和 Shadcn 构建的简洁界面
语言支持 – 内置中英文支持

计划功能:

增强管理员控制 – 提供更精细的用户和管理员权限控制、共享主机等功能
主题定制 – 修改所有工具的主题风格
增强终端支持 – 添加更多终端协议,如 VNC 和 RDP(有类似 Apache Guacamole 的 RDP 集成经验者请通过创建 issue 联系我)
移动端支持 – 支持移动应用或 Termix 网站移动版,让你在手机上管理服务器

安装Docker和NGINX:

apt -y update
apt -y install curl nginx python3-certbot-nginx
curl -fsSL https://get.docker.com -o get-docker.sh
sh get-docker.sh

创建目录/compose文件:

mkdir /opt/termix && cd /opt/termix

写入如下配置:

services:
  termix:
    image: ghcr.io/lukegus/termix:latest
    container_name: termix
    restart: unless-stopped
    environment:
      PORT: "8080"
    ports:
      - "127.0.0.1:8080:8080"
    volumes:
      - termix-data:/app/data
volumes:
  termix-data:
    driver: local

启动:

docker compose up -d

配置NGINX反向代理:

nano /etc/nginx/sites-available/termix

写入如下配置:

upstream termix {    
    server 127.0.0.1:8080;
}

server {
    listen 80;
    server_name termix.example.com;
    client_max_body_size 0;

    access_log /var/log/nginx/termix_access.log;
    error_log /var/log/nginx/termix_error.log;

    location / {
        proxy_pass http://termix;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}

启用站点:

ln -s /etc/nginx/sites-available/termix /etc/nginx/sites-enabled/termix

签发SSL证书:

certbot --nginx --email [email protected] --agree-tos --no-eff-email

目前的版本有个问题,Termix在容器里面也是用的NGINX,但是容器里面的这个NGINX没有配置接收客户端的X-Forwarded-For标头,这会导致Termix获取不到客户端的真实IP,不过这问题下个版本应该就修了

访问termix.example.com创建管理员账号,登录进去后,如果你是自用的话建议把这个用户注册的功能关闭掉:

效果:

这个文件管理的功能非常牛逼,可以预览图片、听音乐,还能播放视频:

这个文件管理目前有个问题,上传大文件的时候会提示上传失败,控制台会报一个超时错误,不知道什么时候能够修复=-=我在反代的NGINX配置了下面的内容也不行:

proxy_connect_timeout 600s;
proxy_send_timeout 600s;
proxy_read_timeout 600s;

好像是程序硬编码超时时间为30秒了。。但最终文件还是上传成功了就是。。

Campfire:一款基于Web的聊天应用程序

2025-10-06 10:31:25

Campfire是一款基于Web的聊天应用程序。它支持许多功能,目前包括:

多个聊天房间(具有访问控制)
私聊
文件上传与预览支持
搜索
通知(使用Web Push)
@提及
支持机器人集成

我搭建了一个试了下体验还不错,给小团体用的话足够了,完全公开当成公共实例的话,我个人认为还少了以下几个关键功能:

禁止用户(开发中)
找回密码(开发中)
表情包支持,目前只支持几个emoji。
更细粒度的权限控制,目前的权限控制比较单一,只能控制房间是完全公开还是私有。

这项目非常值得期待,背后是Basecamp的团队在开发!

考虑到官方提供的Docker部署方案需要占用80/443端口,这里我记录下用NGINX反代部署的方案。

安装NGINX、Node.js、NPM:

apt -y update
apt -y install curl nginx python3-certbot-nginx nodejs npm

安装Docker:

curl -fsSL https://get.docker.com -o get-docker.sh
sh get-docker.sh

安装web-push:

npm install -g web-push

使用web-push生成vapid-key:

web-push generate-vapid-keys

会回显类似的内容,复制下来保存,后续部署需要用到:

Public Key:
BKnuy5nGRaA2uuuonErTAUpH4Moil5w7J3RqWgtklf46h5YIpY-rxiR8GQ8z-3YOgZ1RfeWu1AheG8EeHhgx_04
Private Key:
vvqVf2ZDnMqY4N-xXEOwgqw1ntslfEb0Eq8aUHwfBsE

由于官方没有提供最新的预构建docker image,需要我们自己build一下。这里介绍一个小技巧,对于这种目录里有Dockerfile的项目,不需要把整个项目clone到本地再构建,直接用下面的命令就能构建了:

docker build -t campfire:latest https://github.com/basecamp/once-campfire.git#main

创建目录/compose文件:

mkdir -p /opt/campfire && cd /opt/campfire && nano docker-compose.yml

写入如下配置:

services:
  campfire:
    image: campfire:latest
    container_name: campfire
    restart: unless-stopped
    environment:
      - DISABLE_SSL=true
      - SECRET_KEY_BASE= # 使用openssl rand -hex 32生成
      - VAPID_PUBLIC_KEY= # 粘贴web-push生成的公钥
      - VAPID_PRIVATE_KEY= # 粘贴web-push生成的私钥
    ports:
      - "127.0.0.1:4000:80"
    volumes:
      - campfire_data:/rails/storage
volumes:
    campfire_data:

启动:

docker compose up -d

创建NGINX站点配置文件:

nano /etc/nginx/sites-available/campfire

写入如下内容:

upstream campfire {    
    server 127.0.0.1:4000;
}

server {
    listen 80;
    server_name campfire.example.com;
    client_max_body_size 10m; # 限制文件上传大小,根据自己的需要修改

    access_log /var/log/nginx/campfire_access.log;
    error_log /var/log/nginx/campfire_error.log;

    location /cable {
        proxy_pass http://campfire;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_connect_timeout 60s;
        proxy_send_timeout 60s;
        proxy_read_timeout 60s;
    }

    location / {
        proxy_pass http://campfire;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_connect_timeout 60s;
        proxy_send_timeout 60s;
        proxy_read_timeout 60s;
    }
}

启用站点:

ln -s /etc/nginx/sites-available/campfire /etc/nginx/sites-enabled/campfire

签发SSL证书:

certbot --nginx --email [email protected] --agree-tos --no-eff-email

访问campfire.example.com注册你的账号,第一个注册的账号默认为管理员:

效果:

OVH Debian13安装Incus开独立IPv6小鸡

2025-10-03 11:57:50

前言

写这篇文章花了我几天的时间,我把过程中遇到的坑都解决了,相信我这篇文章最少能带你入门incus。。。毕竟incus还是非常强大的,功能太多了,一时半会也折腾不完。。。

文章标题已经特地注明了是OVH的机器,为什么要特地注明一下,其原因有二:

1.OVH网络比较特殊,尤其是IPv6,稍后详细说明。

2.这台服务器的Debian13系统是通过OVH控制面板安装的,即系统内默认使用的是netplan+systemd-networkd而非Debian默认的ifupdown。

所以这篇文章的配置我只能保证目前适用于OVH,文章可能会写的有点长,因为我想在开始前先吐槽一些东西。。。

吐槽OVH

OVH的工单支持(客服)这帮人真的是带薪摸鱼。虽然我知道OVH的工单支持一直都是垃圾,但是没想到可以拉到如此地步。。。这事情还得从10个多月前说起,起因是因为我发现了一个OVH控制面板的BUG:

我通过多个方法验证过,现在新购的OVH独立服务器,其实都是分配了/56 IPv6地址的,而且是全段路由,不需要用NDP代理,即使是Kimsufi这个系列的独立服务器也是如此。(并不是OVH面板上显示的/128。)这也就意味着/56的IPv6都可以把OVH面板上那个ff:ff:ff:ff结尾的IPv6当作网关使用,我当时在服务器上这么配置一点问题都没有。然而就在我从OVH面板购买了一个附加IPv4后,事情就变的没那么简单了。

我当时使用的是PVE桥接网络,OVH出于安全考虑,严格要求所有附加IPv4都必须申请一个虚拟MAC地址才能用在桥接网络上。(其实很多商家都是这样做的,比如Hetzner)这本来没什么问题对吧,我也成功将这个附加IPv4配置到虚拟机上用了。但是后面我就发现除了这台配置了虚拟MAC地址的虚拟机外,其他使用随机MAC地址的虚拟机IPv6都不通了。

到目前为止你还可以说它是一个预期行为对吧,在没有申请虚拟MAC地址前,服务器没有启用MAC地址过滤,所以那些使用随机MAC地址的虚拟机都能联网。现在是启用了MAC地址过滤的状态,那就只有OVH允许的MAC地址才能联网。但问题来了,OVH没有给IPv6也配备申请虚拟MAC地址的功能啊,这意味着如果你还想在虚拟机上用IPv6的话就得为每个虚拟机都购买一个附加IPv4,因为只有附加IPv4才能申请虚拟MAC地址。。你就说这操作sb不sb吧。。原本我能够开非常多的IPv6 Only虚拟机,但现在不行了,要用IPv6就必须买附加IPv4。。

更离谱的还在后面,即便我后续把这个虚拟MAC地址删除了,哪怕连这个附加IPv4服务取消了都无法关闭MAC地址过滤,我这台服务器的网络永远回不到最初的样子了。按正常的逻辑来说,MAC地址过滤是因为申请了虚拟MAC地址才启用的,现在我把虚拟MAC地址删除了,那MAC地址过滤也应该关闭才对吧,然而OVH不是这样的,这个MAC地址过滤只要开了就没地方关了。。我在OVH面板找不到任何关闭的地方,我还看了它们的API文档,压根就没有MAC地址过滤的API。。

我甚至多次测试过,每当我在OVH面板给一个附加IPv4添加或者删除虚拟MAC地址的时候,那些使用随机MAC地址的虚拟机都能够短暂联网。所以根据这个现象和我的经验判断:肯定是OVH在我删除了虚拟MAC地址后,它们的上游路由器或者是什么设备依旧在过滤MAC地址,导致我的虚拟机无法联网。

因此这摆明就是OVH的BUG,当时我就给OVH发了一个工单,结果等了几天的时间,过来处理的这个B一上来就是答非所问,敷衍了事,不解决我的问题,然后我也懒得鸟他了,我告诉你们的面板有BUG,你爱修不修,然后我就把工单关了。其实这BUG对我来说也没多大影响,我还有其他变通的方法可以继续开IPv6 Only虚拟机(我后面会说具体的方法),只是觉得已经有全段路由了,还去用那些方法有点膈应。。

时隔10个多月,我突然想起来我还有这样一台吃灰的机器,顺便我就想再发一个工单,看看换个客服能不能给我解决了,结果你猜怎么着?这个吊毛比上次那个更离谱,这是它回复我的内容,我竟然无言以对:

他好像说了又好像什么都没说,他好像说的很对又好像说的都不对,我还能说啥?能有这样的员工真是OVH的福份啊=-=你要从OVH官方的角度来说,确实Kimsufi系列官方一直只承认/128的IPv6,但现在实际都是/56。再者退一步,现在是虚拟MAC地址和MAC地址过滤的问题,和有多少可用IP也没关系吧。

我估计OVH真正核心的开发人员到现在都还不知道这个BUG,我在这里提醒一下用OVH服务器的人别踩我这个坑。如果你有使用IPv6的需求,同时又要使用OVH的附加IPv4,其实有几个变通的办法:

1.考虑将桥接网络改成路由网络,路由网络不需要虚拟MAC地址。(至于路由网络怎么配置这篇文章不做介绍,有机会单独用另外一篇文章说明)

2.已经申请了虚拟MAC地址了那IPv6还可以用NDP代理。类似的工具很多比如NDPPD,还有Linux内核本身就支持NDP代理,只是内核不支持代理整个子网,只能代理单个地址。

3.富哥专属:就如我之前说的为每台虚拟机都购买一个附加IPv4并且申请虚拟MAC地址。我甚至都怀疑OVH是不是故意的了=-=

这个问题我已经说的差不多了,但是接下来我还想吐槽OVH屎上雕花的技术那真是一绝。还是这台服务器,里面有4块HDD,其中有1块刚交付的时候我就发现是有问题的,我当时主动发了个工单要求把这块硬盘换掉,他们也很爽快,说换就换了。

没过多久哈,又有一块盘有问题了,我的邮箱也一直收到smart的报警邮件,但是因为这台服务器一直吃灰,我也懒得管就一直没处理,直到最近因为我想拿来用,索性我就把这台服务器的系统给重装了(通过OVH面板重装的)结果重装进度就一直卡着不动,取消也取消不了。就当我准备再次发工单的时候,我突然发现工单系统里面有一个我重装系统时,他们OVH自动发的一个硬件检查的工单,然后你猜怎么着?过了大约5小时,我发现不但系统重装好了,那块有问题的硬盘也被他们换掉了。一时间我竟不知所措:

我心想按OVH的尿性,工单都懒得回的,它竟然还会主动给我把坏掉的硬盘换了?我当真恰了一惊=-=

咱就是说OVH你能不能先把你那屎山代码堆起来的面板优化一下啊,能不能把精力放在重要的功能上啊,这种装系统时的硬件检查不能说完全没用吧,但基本没用吧。。有这个闲工夫搞这些花里胡哨的,不如先把屎山清理下???或者把IPv6也能申请虚拟MAC地址的功能给实装下?看看别人多少年前提的issue了

我说个题外话啊,其实OVH整体给人的感觉就是乱七八糟的,面板布局乱,产品线也乱,工单系统更不用说了。然后还搞他妈一大堆分站,什么US区、IE区、CA区,哎哟看了脑壳都是大的,新人要注册个账号不看攻略连个网址都找不对。。与之能够相提并论的,我到目前为止也就只发现个甲骨文,甲骨文那个网页一看也是屎山。。

安装incus

以上吐槽仅代表我个人的观点,如有错误欢迎指正=-=,好了步入本文的正题。。。安装incus,推荐使用zabbly的存储库来安装。

导入存储库密钥:

mkdir -p /etc/apt/keyrings/
curl -fsSL https://pkgs.zabbly.com/key.asc -o /etc/apt/keyrings/zabbly.asc

添加稳定版本的存储库:

nano /etc/apt/sources.list.d/zabbly-incus-stable.sources

写入如下内容:

Enabled: yes
Types: deb
URIs: https://pkgs.zabbly.com/incus/stable
Suites: trixie
Components: main
Architectures: amd64
Signed-By: /etc/apt/keyrings/zabbly.asc

安装inucs:

apt update
apt install incus btrfs-progs

初始化:

incus admin init

下面的步骤除了“存储池大小”要根据自己的服务器硬盘容量来配置,其他的都可以按回车保持默认:

Would you like to use clustering? (yes/no) [default=no]: 
Do you want to configure a new storage pool? (yes/no) [default=yes]: 
Name of the new storage pool [default=default]: 
Name of the storage backend to use (btrfs, dir, lvm, lvmcluster) [default=btrfs]:
Where should this storage pool store its data? [default=/var/lib/incus/storage-pools/default]: 
Would you like to create a new local network bridge? (yes/no) [default=yes]: 
What should the new bridge be called? [default=incusbr0]: 
What IPv4 address should be used? (CIDR subnet notation, “auto” or “none”) [default=auto]: 
What IPv6 address should be used? (CIDR subnet notation, “auto” or “none”) [default=auto]: 
Would you like the server to be available over the network? (yes/no) [default=no]: 
Would you like stale cached images to be updated automatically? (yes/no) [default=yes]: 
Would you like a YAML "init" preseed to be printed? (yes/no) [default=no]:

安装Web UI:

apt install incus-ui-canonical

配置Web UI监听地址:

incus config set core.https_address :8443

现在就可以通过:https://serverip:8443访问到Web UI。

这个Web UI的鉴权方式有点特殊,用的是证书认证。首次打开后,先在页面上生成证书(crt):

生成证书(crt)的时候会让你输入一个密码,这个密码可以不输入直接跳过:

然后把证书(crt)传到服务器内,例如我传到服务器的/home/debian路径下,执行如下命令信任证书:

incus config trust add-certificate /home/debian/incus-ui.crt

回到Web UI下载证书(pfx),打开浏览器的证书管理界面:

chrome://certificate-manager/

点击如图所示选项:

点击导入:

点击下一步:

选择刚下载的pfx文件:

如果一切正常,现在就能登录到Web UI了:

incus网络配置

现在来配置网络。我们要知道inucs实际上有一个开箱即用的网络,也就是在刚才初始化的时候就配置好的,一般是incusbr0。这个网络具有NAT IPv4和NAT IPv6。NAT IPv4通过DHCPv4分发地址,NAT IPv6通过SLAAC分发地址。如果你对这个配置满意,那么不需要做任何修改直接就能拿来用。

这里有一个小坑,如果你的服务器安装了docker,docker会阻止incusbr0的通信,导致后续创建的虚拟机没有网络,在这种情况下必须修改docker配置:

nano /etc/docker/daemon.json

加入如下内容:

{
  "ip-forward-no-drop": true
}

重启服务器生效:

systemctl reboot

这样你就可以将docker和incus共存在一台服务器上了。

我现在想的是保留这个网络的NAT IPv4不做更改,将NAT IPv6改成独立IPv6,并且通过有状态(stateful)DHCPv6来分发地址。

简单说一下为什么用有状态(stateful)DHCPv6。首当其冲的原因是因为OVH这个睿智BUG,导致我只能用NDP代理,其次我又不想用NDPPD,因为这个软件有时候会有问题,有些IPv6邻居请求它代理不到,而且就算代理到了,它还有一个到期时间,如果到期后没人往你这个IPv6发送数据,这个IPv6就失联了,你还需要去PING一下才能代理到。然后我看了下systemd-networkd的文档,发现它是支持NDP代理的,但是它和Linux内核一样,只支持代理单个地址不支持子网,我们知道SLAAC分配地址是需要一个/64子网的,并且是根据机器MAC地址来生成最终的IPv6地址的,在这种情况下我不能保证/64子网下的每一个IPv6都能用,所以自然就不能用SLAAC。

在文章开头的时候就说了,这台Debian服务器使用的是netplan+systemd-networkd来管理网络,netplan本身是不支持配置NDP代理的,但是netplan使用的后端systemd-networkd是支持的,所以接下来我们可以创建一个覆盖配置(override),来增加我们需要的NDP代理功能。

你可以通过ip a命令先确定主机使用的接口名,这里假设主机的接口名是eno1,那么创建一个带有主机接口名的目录:

mkdir /etc/systemd/network/10-netplan-eno1.network.d

接着在目录内新建一个conf:

nano /etc/systemd/network/10-netplan-eno1.network.d/proxy.conf

写入如下配置,可根据自己的需要增加或者删除IPv6地址:

[Network]
IPv6ProxyNDP=true
IPv6ProxyNDPAddress=2607:5300:60:8401::1
IPv6ProxyNDPAddress=2607:5300:60:8401::2
IPv6ProxyNDPAddress=2607:5300:60:8401::3
IPv6ProxyNDPAddress=2607:5300:60:8401::4
IPv6ProxyNDPAddress=2607:5300:60:8401::5
IPv6ProxyNDPAddress=2607:5300:60:8401::6
IPv6ProxyNDPAddress=2607:5300:60:8401::7
IPv6ProxyNDPAddress=2607:5300:60:8401::8
IPv6ProxyNDPAddress=2607:5300:60:8401::9
IPv6ProxyNDPAddress=2607:5300:60:8401::a
IPv6ProxyNDPAddress=2607:5300:60:8401::b
IPv6ProxyNDPAddress=2607:5300:60:8401::c
IPv6ProxyNDPAddress=2607:5300:60:8401::d
IPv6ProxyNDPAddress=2607:5300:60:8401::e
IPv6ProxyNDPAddress=2607:5300:60:8401::f
IPv6ProxyNDPAddress=2607:5300:60:8401::10

使配置生效:

netplan generate
netplan apply

验证NDP代理是否启用:

sysctl -a | grep proxy_ndp

如果正常的话应该在eno1接口看到proxy_ndp值为1:

net.ipv6.conf.all.proxy_ndp = 0
net.ipv6.conf.default.proxy_ndp = 0
net.ipv6.conf.docker0.proxy_ndp = 0
net.ipv6.conf.eno1.proxy_ndp = 1
net.ipv6.conf.eno2.proxy_ndp = 0
net.ipv6.conf.incusbr0.proxy_ndp = 0
net.ipv6.conf.lo.proxy_ndp = 0
net.ipv6.conf.tap5ac79f9f.proxy_ndp = 0
net.ipv6.conf.vethb2d8638f.proxy_ndp = 0

验证NDP代理是否生效:

ip -6 neighbor show proxy

如果正常的话应该能够回显在systemd-networkd内配置的IPv6地址:

回到Web UI,编辑这个incusbr0网络,配置IPv6地址:

还是在这个页面往下翻,把IPv6 NAT关闭,把IPv6 DHCP打开,把IPv6 DHCP expiry改成一个特别大的值,例如15年(稍后我会说明原因)。把IPv6 DHCP ranges配置为你启用NDP代理的IPv6地址范围,例如:2607:5300:60:8401::2-2607:5300:60:8401::10。把IPv6 DHCP stateful打开,所有配置如下图所示:

也可以复制下面的YAML配置进行修改:

project: default
name: incusbr0
description: ''
type: bridge
config:
  ipv4.address: 10.38.58.1/24
  ipv4.nat: 'true'
  ipv6.address: 2607:5300:60:8401::1/64
  ipv6.dhcp: 'true'
  ipv6.dhcp.expiry: '473353890'
  ipv6.dhcp.ranges: 2607:5300:60:8401::2-2607:5300:60:8401::10
  ipv6.dhcp.stateful: 'true'
  ipv6.nat: 'false'

incus Web UI支持YAML配置:

incus网络安全

接下来我想简单说一下关于incus网络安全的配置。只使用上面这个配置的话,实例确实拥有了访问网络的能力,如果这只是一个测试环境,或者说这些实例只有你自己一个人用的话,那没什么问题。但是如果这是一个生产环境,这些实例创建了之后还要给其他人用,那么上面的这个配置就不够了,因为连最基本的MAC地址过滤都没有,你怎么能够保证网络内没有人搞坏事,比如MAC地址欺骗,又或者有坏B把实例的IP瞎坤8乱改,导致和别的实例IP冲突?这些都是要解决的问题。

好在incus的桥接网络原生支持这些安全功能,所以我们可以很简单的进行配置,首先在主机加载br_netfilter模块(ipv6_filtering所需):

modprobe br_netfilter
lsmod | grep br_netfilter # 检查模块是否加载成功

配置开机自动加载br_netfilter模块:

nano /etc/modules-load.d/modules.conf

写入如下内容:

br_netfilter

接着在Web UI找到Profiles–>Default–>Configuration,启用YAML配置:

在network: incusbr0加入如下配置,这样就可以防止MAC地址欺骗、IPv4地址欺骗、IPv6地址欺骗:

name: default
description: Default Incus profile
devices:
  eth0:
    network: incusbr0
    security.ipv4_filtering: 'true'
    security.ipv6_filtering: 'true'
    security.mac_filtering: 'true'
    type: nic
...

你还可以加入如下配置用于阻止实例到实例的流量,也就是网络隔离:

security.port_isolation: 'true'

到这里我就要解释一下为什么之前要把IPv6 DHCP expiry(DHCP租约)改成一个特别大的值,原因是目前incus的ipv6_filtering功能有BUG,它往nftables里面添加的规则有问题,这些规则会阻止正常的dhcp6 reply流量。

incus是通过dnsmasq来实现dhcp功能的,默认情况下只配置了1小时的租约时间,这会导致虚拟机在租约过期后丢失IPv6地址。我已经在incus的论坛发过帖子了,开发者说会尽快找到原因并解决,所以这里临时想到这个办法解决

其实这个问题真的很玄学,目前连incus的作者都不知道问题到底在哪=-=,有大佬知道的,也可以去incus论坛交流下。。。当然实际上哈,dnsmasq对有状态(stateful)DHCPv6的支持本身就不好,如果环境允许,最好还是用SLAAC。这也是incus目前针对IPv6地址分配的默认方案。

cloud-init配置

接下来讲一下incus与cloud-init的配置。我们要知道cloud-init要想正常工作的话,要满足2个基本条件:

1.实例内的系统安装了cloud-init软件包。实例指:容器(LXC)或虚拟机(KVM)

2.虚拟化平台(例如incus、Proxmox VE)自身要支持cloud-init的配置。

这两个条件incus都是满足的,而且可以做到开箱即用。incus官方提供的系统镜像(模板),无论是容器(LXC)还是虚拟机(KVN)都已经预装了cloud-init软件包。

现在我们来到incus Web UI的Profiles–>Default–>Cloud-init页面:

可以看到这里有3个配置项Network config、User data、Vendor data。这分别代表什么意思呢?让我简单介绍一下。

Network config顾名思义就是用来配置实例网络的,如果你不需要修改实例的网络配置可以留空。这里我还是给出一个配置示例,默认情况下cloud-init仅在第一个接口启用dhcp,如果你给实例添加了第二个接口,要让第二个接口也启用dhcp,可以使用下面的配置:

network:
  version: 2
  ethernets:
    eth0:
      dhcp4: true
      dhcp6: true
    eth1:
      dhcp4: true
      dhcp6: true

重点在于Vendor data(供应商数据)、User data(用户数据),这两者都是给cloud-init提供cloud-config-data的,但主要区别如下:

1.Vendor data(供应商数据)一般用于默认配置,而User data(用户数据)用于特定实例的配置。

2.Vendor data(供应商数据)默认情况下,仅在实例首次启动时运行。

打个比方,现在你是一个VPS商家,你作为一个VPS商家就相当于是一个供应商,你在给你的系统镜像做定制化内容的时候,应该使用Vendor data(供应商数据)。而你的客户购买了你的VPS,他们就是你的用户,用户如果要给自己的VPS系统做定制化内容,应该使用User data(用户数据)。

假设你想给系统镜像修改默认时区、预装一些软件包、启用ssh的root用户登录,这些配置应该写在Vendor data(供应商数据)内:

#cloud-config
timezone: Asia/Shanghai
packages:
  - sudo
  - git
  - wget
  - curl
  - htop
  - nano
  - openssh-server
runcmd:
  - sed -i 's/#PermitRootLogin.*/PermitRootLogin yes/g' /etc/ssh/sshd_config
  - sed -i 's/PasswordAuthentication.*/PasswordAuthentication yes/g' /etc/ssh/sshd_config
  - systemctl reload sshd

假设用户想为自己的VPS系统配置ssh密钥登录,他们应该将ssh公钥写在User data(用户数据)内:

#cloud-config
users:
  - name: imlala
    groups: sudo
    shell: /bin/bash
    sudo: ['ALL=(ALL) NOPASSWD:ALL']
    ssh_authorized_keys:
      - ssh-ed25519 AAAAC2NzaX1lZDI1NTE5AAAXIPZTKfl2X7TWJZmvok/+6qHzKceuGpXzst+nN77NMu05

注意配置的格式,Vendor data(供应商数据)和User data(用户数据)开头必须指定#cloud-config,而Network config无须指定。你可以在实例内使用如下命令对配置进行检查,如果有错误会提示你:

cloud-init schema --system --annotate

现在想象一下,如果一开始你把某个客户(用户)的ssh公钥错误的配置在了Vendor data(供应商数据)内,那拥有这个ssh私钥的人不是就可以登录你所有的VPS了?又或者说你故意在Vendor data(供应商数据)内配置一个自己的ssh公钥,那这就等于放了个后门,这是很可怕的,所以一定要谨慎配置。

另外,我们还需要注意在incus中,实例创建的时候必须指定一个Profiles,一个实例的默认配置是由Profiles提供的。按正常逻辑来说,我们只应该在Profiles内配置cloud-init的Vendor data(供应商数据),在特定的实例中单独配置User data(用户数据)。

如果你在Profiles同时配置了Vendor data(供应商数据)、User data(用户数据),那么即便遵循了上面的配置指南,也无法真正做到供应商和用户配置的分离。这是因为Profiles会同时把Vendor data(供应商数据)、User data(用户数据)都当作默认配置提供给实例。所以最好的做法是在Profiles内保持User data(用户数据)为空。

当然如果这个incus只有你自己一个人用,上面举例的这些配置写到Vendor data(供应商数据)还是User data(用户数据)都没什么区别,具体还是要看自己的实际使用环境。

创建一台Linux实例

在做好上面的配置后,现在就可以尝试创建一台实例了,这里以创建Debian13为例:

选择系统镜像,Variant选择cloud,Type这里选择all,这种镜像是同时支持容器(LXC)、虚拟机(KVM)的:

就如同之前说的,每个实例必须指定一个Profiles:

可以看到每个实例是可以单独配置cloud-init的,如果要覆盖Profiles的cloud-init配置就可以在这里进行设置:

由于这个incus只有我一个人用,为了方便起见,我不想每次创建虚拟机的时候都配置一遍ssh公钥,所以我直接把ssh公钥相关的内容写到了Profiles cloud-init的User data(用户数据)

配置实例的CPU、内存:

实例创建完成后,登录进去测试下:

添加Proxy device

Proxy device是incus一个非常实用的功能,你可以简单理解为这是一个端口转发的功能,可以暴露内网的服务到外网,特别适合那些没有公网IP的NAT网络。

假设我想把刚才创建的Debian13实例的SSH端口(22)暴露到外网,可以在Web UI这样配置:

YAML配置,其中10.38.58.3是实例的内网IP地址:

architecture: x86_64
config:
  image.architecture: amd64
...
devices:
  proxy-1:
    bind: host
    connect: tcp:10.38.58.3:22
    listen: tcp:0.0.0.0:50022
    nat: 'false'
    type: proxy
...

当连接主机的hostip:50022就相当于连接实例的10.38.58.3:22端口了:

实际上这东西还不仅仅只是端口转发,玩法很多,可以自己摸索一下。

创建Windows11虚拟机

说句实话,incus对KVM的支持不如Proxmox VE完善,尤其是当你要安装Windows系统的时候。实际体验肯定是不如Proxmox VE的。

我的建议是最好不要在incus上创建Windows虚拟机。如果你硬要用的话,这里介绍两种安装方式:

1.通过incus官方提供的distrobuilder工具重新打包Windows iso写入virtio驱动。

2.使用io.bus=usb挂载Windows和virtio驱动iso。这是incus 6.11版本加入的新功能

如果你的incus版本高于6.11我个人推荐第二种安装方式,毕竟第一种方式要安装很多不必要的软件包,而且这种方式有点hack,侵入性太大。第二种更原生,而且性能更好。

方式一:使用distrobuilder

第一种安装方式,在主机安装snap和需要用到的依赖包:

apt install snapd
apt install libwin-hivex-perl wimtools genisoimage

使用snap安装distrobuilder:

snap install distrobuilder --edge --classic

重新打包iso:

distrobuilder repack-windows --windows-arch=amd64 Win11_24H2_Chinese_Simplified_x64.iso Win11_24H2_Chinese_Simplified_x64_virtio.iso

把iso导入到默认的存储卷:

incus storage volume import default /root/Win11_24H2_Chinese_Simplified_x64_virtio.iso windows11-iso --type=iso

转到Web UI创建实例,选择自定义ISO:

选择刚才导入的ISO文件:

添加TPM设备:

启动虚拟机访问控制台应该就能看到Windows安装界面了,安装过程中一直下一步就完事了。

方式二:io.bus=usb挂载ISO

第二种安装方式,创建虚拟机只能使用命令操作,因为有些设置目前Web UI还不支持:

incus init win11 --empty --vm -c limits.cpu=4 -c limits.memory=8GiB -c image.os="Windows 11" -d root,size=80GiB -d root,io.bus=virtio-scsi -d root,io.cache=writeback
incus config device add win11 install disk source=/root/Win11_24H2_Chinese_Simplified_x64.iso boot.priority=10 io.bus=usb
incus config device add win11 virtio disk source=/root/virtio-win.iso io.bus=usb
incus config device add win11 incus_agent disk source=agent:config
incus config device add win11 vtpm tpm path=/dev/tpm0

这里有个坑,如果你待会用控制台安装系统的时候鼠标会产生偏移或者说不同步,使用下面的命令添加一个usb-tablet设备即可解决:

incus config set win11 raw.qemu -- "-device usb-tablet"

启动虚拟机:

incus start win11

访问Web UI打开控制台,如果你手速够快的话,按键盘任意按键应该能直接进到Windows的安装界面,手速不快的话就只能在这个界面等5分钟左右了,这不是错误哈,这是预期行为:

之后会出现下面这个界面,选择第一项回车确认:

在这个界面立即按键盘任意按键即可进入Windows的安装界面:

选择安装Windows11:

在这个界面你会发现找不到可以安装的硬盘,没关系这是正常的,因为我们指定的硬盘设备是virtio-scsi,Windows没有相关驱动,点Load Driver开始安装驱动:

这里需要安装的驱动很多,首先是硬盘驱动:

硬盘驱动安装完成后你就可以看到之前分配的80G硬盘显示出来了,但是不要慌,还是点Load Driver重复之前的步骤来安装其他驱动:

由于Windows这个傻吊系统不能批量安装驱动,你还需要重复这个步骤至少安装下面列出的驱动,这里我就不截图了,按照名字找到对应的路径即可:

1.NetKVM(网卡驱动)路径:NetKVM\w11\amd64
2.Balloom(内存气球)路径:Balloon\w11\amd64

系统安装完成后再把剩下的驱动补全,直接运行virtio-win-gt-x64.msi安装即可:

virtio-win-gt-x64.msi好像还装不全,我遇到还剩一个没装上,这时候需要检查丢失的驱动程序,具体操作如下:

1.转到计算机管理–>设备管理器,查看任何未知设备。
2.右键单击选择“更新驱动程序”。
3.选择“浏览我的计算机以查找驱动程序软件”。
4.选择virtio-win驱动程序CD。确保选中“包括子文件夹”。
5.找到驱动程序后,系统会自动安装”。

incus自身还有一个agent,在系统内通过命令行运行:

这个agent目前的功能非常有限,只支持显示操作系统版本、文件传输和非交互式的命令执行:

全部完成后,你可以删除不再需要用到的设备:

incus config device remove win11 install
incus config device remove win11 virtio

这篇文章还有一个坑没填,也就是我之前说的,dnsmasq对有状态DHCPv6的支持不好,有些系统比如Windows,它会分配多个IPv6地址:

并且我测试系统内IPv6是不通的。作为一个临时解决办法,查看dnsmasq的hosts配置:

cd /var/lib/incus/networks/incusbr0/dnsmasq.hosts/win11.eth0

这里会列出虚拟机绑定的IPv6地址:

10:66:6a:e0:86:b5,10.38.58.2,[2607:5300:60:8401::9],win11

编辑dnsmasq租约配置文件:

nano /var/lib/incus/networks/incusbr0/dnsmasq.leases

删除没有在win11.eth0列出的IPv6地址:

2232654908 34629226 2607:5300:60:8401::8 * 00:01:00:01:30:6e:85:ea:10:66:6a:d1:56:41
2232654908 34629226 2607:5300:60:8401::4 * 00:01:00:01:30:6e:85:ea:10:66:6a:d1:56:41

然后重启虚拟机测试,如果还不行就直接在虚拟机内关闭DHCP,改成静态IP。

我后面还测试了SLAAC,这个Windows系统是真的牛逼,SLAAC也不行。。它还是会分配多个IPv6地址,在用SLAAC的时候Windows会生成一个临时IPv6地址,这个在Windows里面叫做隐私保护地址。。然而在启用ipv6_filtering的情况下,nftables里面的规则是只允许那个根据MAC地址生成的IPv6地址流量的,这系统搞个隐私地址出来,这个隐私地址的流量都会被drop掉。。我后面懒得折腾了,反正我小鸡也不用Windows。。不了了之了。。。总结一下就是这三方在互相打架:incus的ipv6_filtering、dnsmasq的dhcp、windows系统,没有一个很完美的配置能够让它们互相兼容。。。

分享一些实用的Linux工具(2)

2025-09-27 16:07:48

继续分享一些实用的Linux工具。上一篇文章的链接

1.starship:用rust编写的跨平台、高性能的shell提示符工具。

starship最好配合Nerd字体使用。所以先在自己的本地环境安装一个Nerd Font字体,并设置你的终端使用这个字体,这里我以Tabby为例,我使用的字体是:CaskaydiaMono NFM SemiBold

Debian13安装:

apt install starship

通过Debian存储库安装的不是最新版,如果要用最新版建议用官方的这个安装脚本:

curl -sS https://starship.rs/install.sh | sh

其他系统的安装方式:https://starship.rs/guide/#%F0%9F%9A%80-installation

配置shell以使用starship,这里以bash为例,编辑bashrc:

nano ~/.bashrc

在文件的末尾写入如下内容:

eval "$(starship init bash)"

启动一个新的shell生效。(如果是远程服务器,ssh重新连接一下即可)

starship有提供一些presets(预设)可以直接拿来用,比如我目前用的是Catppuccin Powerline,执行下面的命令即可更改预设:

starship preset catppuccin-powerline -o ~/.config/starship.toml

然后在这个预设的基础上增加一些自己需要的功能:

nano ~/.config/starship.toml

我需要显示主机名,那么hostname相关的内容最好配置在username和directory之间:

[username]
...

[hostname]
disabled = false
ssh_only = false
style = "bg:red fg:crust"
format = '[ $hostname ]($style)'
trim_at = ".local"

[directory]
...

然后docker上下文的配置,我想让它多侦测几个文件名:

[docker_context]
symbol = ""
style = "bg:sapphire"
format = '[[ $symbol( $context) ](fg:crust bg:sapphire)]($style)'
detect_files = ['docker-compose.yml', 'docker-compose.yaml', 'Dockerfile', 'compose.yaml', 'compose.yml']

还需要在format字符串中的合适位置添加相关变量使其生效,比如hostname加在username下面:

format = """
...
$username\
$hostname\
...
$docker_context\
$conda\
...

注意:starship对docker上下文的支持是默认不显示名字为default的上下文的。

执行下面的命令查看当前的docker上下文:

docker context ls

一般会显示:

NAME        DESCRIPTION                               DOCKER ENDPOINT               ERROR
default *   Current DOCKER_HOST based configuration   unix:///var/run/docker.sock

如果要让starship支持这个default上下文,可以添加一个环境变量:

nano ~/.bashrc

写入如下配置:

export DOCKER_MACHINE_NAME="Default"

使其生效:

source ~/.bashrc

效果:

2.zoxide:rust编写的cd命令替代品,具有记忆功能。

Linux推荐使用脚本安装最新版:

curl -sSfL https://raw.githubusercontent.com/ajeetdsouza/zoxide/main/install.sh | sh

或者用包管理器,例如Debian(使用包管理器安装的通常不是最新版):

apt install zoxide

配置shell以使用zoxide,这里以bash为例,编辑bashrc:

nano ~/.bashrc

在文件的末尾写入如下内容:

eval "$(zoxide init bash)"

启动一个新的shell生效。(如果是远程服务器,ssh重新连接一下即可)

注意:如果同时使用了starship和zoxide,则必须将zoxide配置在bashrc的最后一行。

3.lsd:rust编写的ls命令替代品,添加了许多功能,如颜色、图标、树视图、更多格式选项等。

同starship一样,lsd也需要前提在本地环境安装nerd-font

Debian12+安装:

apt install lsd

效果:

4.batcat:具有git集成和语法高亮的cat命令替代品。

Debian11+安装:

apt install bat

请注意安装后的可执行文件名不是bat而是batcat,这是由于bat名称与其他软件包有冲突。

效果:

5.FD:一个简单、快速且用户友好的“find”命令替代方案。

Debian11+安装:

apt install fd-find

请注意安装后的可执行文件名不是fd而是fdfind,这是由于fd名称与其他软件包有冲突。

用法:

6.tealdeer:Rust编写的tldr实现。

tldr是社区维护的命令行工具帮助页面的集合,旨在成为传统手册页(man)的更简单、更平易近人的补充。说白了就是tldr更注重实用性。

Debian安装:

apt install tealdeer

首次使用需要先更新本地缓存:

tldr --update

测试一下,比如查看tar命令,传统的man手册可能是列出很多参数、选项,但tldr是直接给出一些实际使用的示例:

[可选]每次登录到shell时显示一条随机的帮助信息页面:

nano ~/.bashrc

写入如下配置:

tldr --quiet $(tldr --quiet --list | shuf -n1)

使其生效:

source ~/.bashrc

[总结]你可以用alias自定义相关命令的别名,比如我这里设置ls和cd的别名:

nano ~/.bashrc

写入如下配置:

alias ls='lsd'
alias cd='z'

使其生效:

source ~/.bashrc

分享一些实用的Linux工具

2025-09-18 22:31:09

我主要用Debian,这里我就拿Debian简单介绍下。

实际上其他的Linux发行版也都能用,甚至有些还是跨平台的,无非就是安装方法可能大同小异。

每个工具我也只简单说下基本的用法,更多用法可以参考工具自带的help命令或者man手册,这篇文章主要是分享信息,告诉大伙有这些优秀的工具=-=

1.inxi:一个功能强大、齐全的Linux命令行系统信息查看工具。它以简洁易读的格式提供系统硬件等信息的全面概述。

安装:

apt install inxi

使用下面的命令即可列出系统的全部概览信息:

inxi -e

2.axel:一个轻量级的多线程命令行下载工具,下载大文件比wget、curl等工具要快很多。

安装:

apt install axel

使用:

axel https://cdimage.debian.org/debian-cd/current/amd64/iso-dvd/debian-13.1.0-amd64-DVD-1.iso

3.btop++:一个替代top、htop的资源监视器。

安装:

apt install btop

使用:

btop

4.duf:磁盘使用/空闲实用程序 – 更好的“df”替代品。

安装Debian (12 and later):

apt install duf

使用:

duf

5.vnstat:一款基于控制台的网络流量监控器,适用于Linux和BSD,可记录所选接口的网络流量日志。并且收集的统计数据在系统重启后仍然有效,数据保留时长可由用户动态配置。

安装:

apt install vnstat

启动后端服务:

systemctl enable --now vnstat.service

使用:

vnstat

6.RustNet:一个用Rust构建的跨平台网络监控工具,支持深度数据包检测(DPI)。

安装依赖:

curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
apt install libpcap-dev

安装RustNet:

cargo install rustnet-monitor
ln -s ~/.cargo/bin/rustnet /usr/local/bin/rustnet

使用:

rustnet

7.command-not-found:这个工具提供了一个实用的功能,当你输入系统中未找到的命令时,它会建议你安装哪个软件包。如果你之前一直用Ubuntu系统的话应该不陌生,Ubuntu直接就自带了这个功能。

安装:

apt install command-not-found
apt update
update-command-not-found

之后你还需要启动一个新的bash会话(打开一个新的终端或ssh连接)如果还不生效,重启系统:

systemctl reboot

测试效果,当我输入一个系统内不存在的并且错误的命令,它不但可以指出你的命令是错的,还能告诉你哪个软件包里有这个命令:

这是我见过的最牛逼的SSH暴力破解

2025-09-12 11:04:35

第一次见到SSH爆破把机子内存干爆了的。。

起因:早上发现站打不开了,提示数据库连接失败。然后登上去检查数据库运行状态:

journalctl -u mariadb.service

发现被OOM了:

OOM就是机子内存耗尽的时候,内核会自动触发OOM-Killer杀进程释放内存。OOM发生时是肯定可以在dmesg里面找到详细日志的:

journalctl -k

我发现时间发生在11号21点46分(UTC时间)

我详细看了这个日志,比较纳闷的是这个日志里面为什么会有这么多的sshd进程,按道理来说那种全网扫描爆破ssh的,试个1-2次就不会继续了,哪怕就是故意来爆破你机子ssh的也不可能让ssh起这么多进程啊。。因为OOM只管杀进程释放内存,被杀的进程并不一定是真正的元凶,所以我想找到当时机子内存耗尽的根本原因。

接着我用下面的命令查看OOM前后5分钟的日志:

journalctl --since "2025-09-11 21:40:00" --until "2025-09-11 21:50:00"

结果就发现这个IP:148.72.158.192是真的牛逼,它这是一秒钟射了多少次啊?你管这叫爆破ssh?你搁这…搁这压力测试呢?

148.72.158.192你妈妈喊你回家吃饭,别在网上搞破坏了!

在经历过忘记root密码,以及被这样的ssh爆破后,我感觉是时候改改用密码登录的习惯了,重要的机子换端口+key登录确实很有必要=-=

为什么之前一直不太愿意用key登录,或者说习惯了用密码登录,是因为很多VPS交付的时候就是用的密码登录,甚至有些商家的面板根本不支持key登录。。手上机子一多,这样一个个改多麻烦,久而久之就养成了密码登录的习惯。。。