我做了一个本地版瑞思迈呼吸机数据分析工具:BreatheLens

最近我做了一个新项目,叫 BreatheLens

项目地址:

1
https://github.com/luodaoyi/BreatheLens

它是一个专门用来分析 ResMed 瑞思迈 CPAP / APAP 呼吸机 SD 卡数据 的本地工具。目标很直接:把原始治疗数据整理成更容易看懂的图表、表格和建议。

很多人手里其实并不缺数据,缺的是一个足够直接、足够轻量、又不逼你折腾半天的查看方式。BreatheLens 就是朝这个方向做的。

我写了一个证书监控系统,叫 Certwarden

我最近整理了一个自己之前写的项目,叫 Certwarden

它不是一个“再造轮子式”的玩具项目,而是一个我觉得在真实运维场景里真的能用得上的东西:

一个面向团队、平台和托管场景的多租户 SSL/TLS 证书监控系统。

如果你手上只有一两个域名,证书快过期了,自己记一下也能扛。

但只要数量一上来,事情就会立刻变味:

  • 哪些域名的证书快过期了
  • 哪些证书其实已经异常了
  • 哪些域名属于哪个客户、哪个租户
  • 谁该收到通知
  • 有没有一个页面能直接给别人看当前状态

很多团队不是没有意识到证书重要,而是压根没有一套顺手的方式把这件事持续管起来。

所以我写 Certwarden,解决的就是这个问题。

给 OpenAI Codex 接上 Telegram 通知:从手动下二进制,到一条 npx 搞定

我最近把一个小工具重新整理了一遍:go-codex-notify

它做的事情很简单,就是给 OpenAI Codex 的 notify 钩子接一个 Telegram 通知。Codex 任务跑完,或者跑到某个阶段,你人不在电脑前,也能立刻收到一条消息。

这个东西本身不复杂,但它解决的是一个很实际的问题:AI 在后台干活的时候,你不想一直盯着终端。

以前这类小工具最大的问题,不是功能做不出来,而是用起来太别扭。你得自己下载二进制、选平台、放路径、改配置、处理环境变量,最后还得解释给别人怎么装。对作者来说只是几个步骤,对用户来说就是直接放弃。

所以我这次重点做的不是“让它能发消息”,而是“让它更像一个正常人会愿意用的工具”。

Bilibili 批量取消关注油猴脚本:自动刷新继续下一页

最近想把 B 站关注列表快速清理一下,手动一页一页点太慢,所以写了个油猴脚本。

脚本下载:bilibili-unfollow-simple-refresh.user.js

这个版本走的是 B 站网页接口,不是模拟鼠标疯狂点按钮,整体会稳一些;同时保留了一个很简单的右下角小面板,方便开始和停止。

它的逻辑也很直接:

  1. 读取当前页关注列表
  2. 逐个取消关注
  3. 当前页处理完后自动刷新页面
  4. 刷新后自动继续下一页
  5. 如果点了停止,就在当前页处理完之后停下来

功能说明

  • 支持批量取消当前账号的关注
  • 支持自动刷新后继续处理下一页
  • 支持失败重试
  • 支持基础防频控延时
  • 支持跳过互粉 / 跳过悄悄关注(默认关闭)
  • 页面右下角会显示一个简单状态面板

使用方法

1. 安装 Tampermonkey

浏览器先装好 Tampermonkey 扩展。

tinyfecVPN 全自动部署

这里提供一个“够用且可回滚”的 tinyfecVPN 一键部署脚本:自动安装依赖、拉取/更新源码(含 submodule)、编译 NOLIMIT(无限制)版本、安装二进制到 /usr/local/bin/tinyvpn,并生成/启用 systemd 服务(Server/Client 分开)。脚本全程交互式输入参数,直接写进 ExecStart,重启不丢;部署后默认删除源码目录(带路径保护),机器上只留下 tinyvpn + systemd unit。

利用iptables转发服务器流量

这里提供一个“够用且可回滚”的最小 iptables 转发脚本:安装时交互式输入转发目标 IP、需要排除的不转发端口(可多个),并选择入口网卡;其余 TCP/UDP/ICMP 流量统一 DNAT 到后端。由 systemd 托管,重启不丢;一键卸载,出错可干净撤回。

依赖说明

管理脚本会检测 iptables/ip/sysctl/systemctl,缺失时可自动用 apt/dnf/yum 安装(也可提前设置 AUTO_INSTALL=1 静默安装)。在容器环境仍需 CAP_NET_ADMIN 等权限,否则无法生效。

用Caddy搭建Docker加速服务

用Caddy搭建Docker加速服务, 不用跑docker什么的,直接用就可以了

 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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97

# Edit this domain to yours
DOMAIN="example.com"

cat << EOF > /etc/caddy/Caddyfile
hub.docker.${DOMAIN} {
  encode gzip
  reverse_proxy * https://registry-1.docker.io {
    header_up Host registry-1.docker.io
    header_up X-Real-IP {http.request.remote.host}
    header_up X-Forwarded-For {http.request.remote.host}
    header_up X-Forwarded-Port {http.request.port}
    header_up X-Forwarded-Proto {http.request.scheme}
    header_down Set-Cookie registry-1.docker.io docker.${DOMAIN}
    header_down Www-Authenticate "Bearer realm=\"https://auth.docker.${DOMAIN}/token\",service=\"registry.docker.io\""
    header_down Location "https://production.cloudflare.docker.com" "https://production.cloudflare.docker.${DOMAIN}"
  }
}

auth.docker.${DOMAIN} {
  encode gzip
  reverse_proxy * https://auth.docker.io {
    header_up Host auth.docker.io
    header_up X-Real-IP {http.request.remote.host}
    header_up X-Forwarded-For {http.request.remote.host}
    header_up X-Forwarded-Port {http.request.port}
    header_up X-Forwarded-Proto {http.request.scheme}
    header_down Set-Cookie auth.docker.io docker.${DOMAIN}
  }
}

production.cloudflare.docker.${DOMAIN} {
  encode gzip
  reverse_proxy * https://production.cloudflare.docker.com {
    header_up Host production.cloudflare.docker.com
    header_up X-Real-IP {http.request.remote.host}
    header_up X-Forwarded-For {http.request.remote.host}
    header_up X-Forwarded-Port {http.request.port}
    header_up X-Forwarded-Proto {http.request.scheme}
    header_down Set-Cookie production.cloudflare.docker.com docker.${DOMAIN}
  }
}

hub.quay.${DOMAIN} {
  encode gzip
  reverse_proxy * https://quay.io {
    header_up Host quay.io
    header_up X-Real-IP {http.request.remote.host}
    header_up X-Forwarded-For {http.request.remote.host}
    header_up X-Forwarded-Port {http.request.port}
    header_up X-Forwarded-Proto {http.request.scheme}
    header_down Set-Cookie quay.io quay.${DOMAIN}
    header_down Www-Authenticate "Bearer realm=\"https://hub.quay.${DOMAIN}/v2/auth\",service=\"quay.io\""
  }
}

hub.gcr.${DOMAIN} {
  encode gzip
  reverse_proxy * https://gcr.io {
    header_up Host gcr.io
    header_up X-Real-IP {http.request.remote.host}
    header_up X-Forwarded-For {http.request.remote.host}
    header_up X-Forwarded-Port {http.request.port}
    header_up X-Forwarded-Proto {http.request.scheme}
    header_down Set-Cookie k8s.gcr.io gcr.${DOMAIN}
    header_down Www-Authenticate "Bearer realm=\"https://hub.gcr.${DOMAIN}/v2/token\",service=\"gcr.io\""
  }
}

hub.k8s.${DOMAIN} {
  encode gzip
  reverse_proxy * https://k8s.gcr.io {
    header_up Host k8s.gcr.io
    header_up X-Real-IP {http.request.remote.host}
    header_up X-Forwarded-For {http.request.remote.host}
    header_up X-Forwarded-Port {http.request.port}
    header_up X-Forwarded-Proto {http.request.scheme}
    header_down Set-Cookie k8s.gcr.io gcr.${DOMAIN}
    header_down Www-Authenticate "Bearer realm=\"https://hub.k8s.${DOMAIN}/v2/token\",service=\"k8s.gcr.io\""
    header_down Location "https://storage.googleapis.com" "https://storage.googleapis.${DOMAIN}"
  }
}

storage.googleapis.${DOMAIN} {
  encode gzip
  reverse_proxy * https://storage.googleapis.com {
    header_up Host storage.googleapis.com
    header_up X-Real-IP {http.request.remote.host}
    header_up X-Forwarded-For {http.request.remote.host}
    header_up X-Forwarded-Port {http.request.port}
    header_up X-Forwarded-Proto {http.request.scheme}
    header_down Set-Cookie storage.googleapis.com storage.googleapis.${DOMAIN}
  }
}
EOF

systemctl restart caddy

禁用docker的ufw,禁止docker无视ufw规则

默认情况下,创建容器如果绑定了端口,则 docker 会自动修改 iptables 打开这个端口。然而 UFW 并不会显示这个规则,这就导致了不管使用 UFW 做什么限制,docker 绑定的这个端口都是开放的。

问题所在

默认情况下,创建容器如果绑定了端口,则 docker 会自动修改 iptables 打开这个端口。然而 UFW(uncomplicated firewall) 并不会显示这个规则,这就导致了不管使用 UFW 做什么限制,docker 绑定的这个端口都是开放的。

Caddy设置仅允许cloudfalre的ip访问,防止被穿过waf

此配置的主要用途是确保没有人可以绕过Cloudflare或其他安全反向代理提供商对网站发起拒绝服务攻击(DoS)。类似功能可以通过real_ip和其他插件实现,但本文的重点是简化设置,不需要安装插件。

需要注意的是,Cloudflare的IP范围并非静态的,尽管变化频率非常低,仍需要手动更新。有人仍然可以通过Cloudflare Workers作为第三方在Cloudflare IP范围内发起请求,不过要使用这种方式进行大规模拒绝服务攻击难度较大。

Openwrt 路由器安装哪吒监控Agent

最近想把路由器也接入nezha监控 于是乎研究下了下发现挺简单的

步骤如下

  1. 下载agent 可执行文件
  2. 编写init.d脚本设置开机启动

以下操作全部都在openwrt的ssh中执行

下载

1
2
3
4
5
6
7
8
9
cd /tmp/upload/
wget https://github.com/nezhahq/agent/releases/download/v0.19.8/nezha-agent_linux_arm64.zip
unzip nezha-agent_linux_arm64.zip
mv nezha-agent /usr/sbin/nezha-agent
chmod +x /usr/sbin/nezha-agent

# 检查下是否成功
root@QWRT:/tmp/upload# nezha-agent -v
0.19.8

配置脚本并且设置开机启动

 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
31
32
33
34
35
36
37
38
39
vi /etc/init.d/nezha-service


输入以下内容:

#!/bin/sh /etc/rc.common

START=99
USE_PROCD=1

start_service() {
 procd_open_instance
 procd_set_param command /usr/sbin/nezha-agent -s 自己的服务器:443 -p 自己的密码 --tls --report-delay 4 --skip-conn --skip-procs  --ip-report-period 600  --disable-command-execute
 procd_set_param respawn
 procd_close_instance
}

stop_service() {
    killall nezha-agent
}

restart() {
 stop
 sleep 2
 start
}


#设置权限,否则会报错
chmod a+x  /etc/init.d/nezha-service

#设置开机启动
/etc/init.d/nezha-service enable

#启动服务
/etc/init.d/nezha-service start

#后续要关闭开启启动的话
/etc/init.d/nezha-service disable

ok了就这么简单

NaiveProxy 简易配置

20250719 更新增加一键安装脚本,支持一键安装和卸载

20250324 更新

naiveproxy不可以代理udp

(更新)一键安装/卸载/升级脚本

1
2
# 一键安装"
wget http://luodaoyi.com/naiveproxy-deploy.sh && sudo bash naiveproxy-deploy.sh --install

安装golang

 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
31
32
33
34
35
36
37
38
39
sudo apt update
sudo apt install wget -y

# 获取最新版本
export GO_VER=$(curl -s https://go.dev/dl/?mode=json | jq -r '.[0].version')
echo $GO_VER

## 清空目录
rm -rf /usr/local/go && mkdir -p /usr/local/go

# amd机器
wget https://go.dev/dl/${GO_VER}.linux-amd64.tar.gz 
sudo tar -zxvf ${GO_VER}.linux-amd64.tar.gz -C /usr/local/

# 甲骨文之类的arm机器
wget https://go.dev/dl/${GO_VER}.linux-arm64.tar.gz
sudo tar -zxvf ${GO_VER}.linux-arm64.tar.gz -C /usr/local/

# 配置path
cat > /etc/profile.d/go.sh << \EOF
export GOROOT=/usr/local/go
export PATH=$GOROOT/bin:$PATH
EOF

source /etc/profile.d/go.sh

# 加入环境变量
cat >> ~/.bashrc << \EOF
export GOPATH=$HOME/.gopath
export PATH=$GOPATH/bin:$PATH
export GO111MODULE=on
#export GOPROXY=https://goproxy.cn
EOF

source ~/.bashrc && mkdir -p $GOPATH && echo $GOPATH

# 看看正常不
go version
go env

编译安装

 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
# 开始编译
sudo apt-get install libnss3 debian-keyring debian-archive-keyring apt-transport-https

mkdir ~/src &&  cd ~/src/

# 用xcaddy构建
go install github.com/caddyserver/xcaddy/cmd/xcaddy@latest

# 开始构建
xcaddy build \
    --with github.com/caddyserver/forwardproxy@caddy2=github.com/klzgrad/forwardproxy@naive \
    --with github.com/caddy-dns/cloudflare@latest \
    --with github.com/caddy-dns/dnspod@latest \
    --with github.com/caddy-dns/alidns@latest 


sudo mv caddy /usr/bin/
caddy version
sudo setcap cap_net_bind_service=+ep /usr/bin/caddy  # 设置bind权限,可443

# 检查正常不
caddy version 

# 查看caddy已经安装的第三方模块
caddy list-modules --packages

配置服务

 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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
# 开始配置

mkdir -p /etc/caddy/ && touch /etc/caddy/Caddyfile

cat > /etc/caddy/Caddyfile << \EOF
{
    admin off
    order forward_proxy before reverse_proxy
}

:443, luodaoyi.com {
    tls [email protected] 
    request_body {
            max_size 1GB
    }
    forward_proxy {
            basic_auth asura asura123
            hide_ip
            hide_via
            probe_resistance
    }
    # 这里的端口是你反代的地址 随便你填 没的话填域名也可以
    # reverse_proxy www.bing.com
    reverse_proxy 127.0.0.1:33000
}

EOF

# 封装成服务 开机启动
groupadd --system caddy

useradd --system \
    --gid caddy \
    --create-home \
    --home-dir /var/lib/caddy \
    --shell /usr/sbin/nologin \
    --comment "Caddy web server" \
    caddy

cat > /etc/systemd/system/caddy.service << \EOF
[Unit]
Description=Caddy
Documentation=https://caddyserver.com/docs/
After=network.target network-online.target
Requires=network-online.target


[Service]
User=caddy
Group=caddy
ExecStart=/usr/bin/caddy run --environ --config /etc/caddy/Caddyfile
ExecReload=/usr/bin/caddy reload --config /etc/caddy/Caddyfile
TimeoutStopSec=5s
LimitNOFILE=1048576
LimitNPROC=512
PrivateTmp=true
ProtectSystem=full
AmbientCapabilities=CAP_NET_BIND_SERVICE


[Install]
WantedBy=multi-user.target
EOF


# 测试正常不正常
sudo systemctl daemon-reload && sudo systemctl enable caddy  
sudo systemctl start caddy  && sudo systemctl status caddy
 

检查指纹

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12

 # 检查指纹
# 下载jarm
wget https://raw.githubusercontent.com/salesforce/jarm/master/jarm.py

# 查看网站jarm指纹 
python3 jarm.py 你的域名

# 网络空间资产搜索引擎:
# 打开网址 https://fofa.info  
# 搜索框输入:  jarm="xxxxx"
# 如果结果有几百万个 那就没问题了,要是几十个就有问题,说明有特征了,重新搞吧

c++ 调试输出简单封装

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include <windows.h>
#include <fmt/printf.h>
#include <string>
#include <type_traits>

template <typename CharType, typename... Args>
void GlobalOutputDebugString(const CharType* format, Args... args) {
    // 使用 fmt::sprintf 格式化字符串
    std::basic_string<CharType> formatted = fmt::sprintf(format, args...);

    // 根据字符类型调用相应的 OutputDebugString 函数
    if constexpr (std::is_same_v<CharType, char>) {
        OutputDebugStringA(formatted.c_str());
    } else if constexpr (std::is_same_v<CharType, wchar_t>) {
        OutputDebugStringW(formatted.c_str());
    }
}

// 这个宏将根据项目设置自动选择正确的字符类型。
// 如果项目设置为使用 Unicode 字符集,那么 _T 和 __FUNCTIONT__ 将解析为 L 和 __FUNCTIONW__。
// 如果项目设置为使用多字节字符集,那么 _T 和 __FUNCTIONT__ 将解析为无前缀和 __FUNCTION__。
#define LOG_DEBUG(format, ...) GlobalOutputDebugString(_T("[%d] ") __FUNCTIONT__ _T("(%d) ") format, GetCurrentThreadId(), __LINE__, ##__VA_ARGS__)

Realm端口转发工具简单使用

Realm 是Rust语言开发的流量转发工具,Realm 比 Gost占用资源更小。

支持多组服务器转发,同时也支持tcp和udp,还有域名解析便捷。

据我的测试结果来看,对于RDP的转发 realm 比市面上别的转发工具延迟都要明显低一些。

  1. 下载最新的可执行文件到本地:

Github 以最新版本为主,根据自己的系统版本下载即可,

群晖dsm增加ddns提供商 (HE.net)

只需要在 /etc.defaults/ddns_provider.conf 文件中加入以下内容:

1
2
3
[HE_DDNS]
        modulepath=DynDNS
        queryurl=https://dyn.dns.he.net/nic/update?hostname=__HOSTNAME__&myip=__MYIP__

然后在DDNS配置中选择HE_DDNS,假设主机名是 abc.example.org ,在主机名和用户名都填写 abc.example.org , 密码处填写HE.NET中生成的TOKEN即可

完整优雅的卸载腾讯云云服务器安全监控组件

腾讯云的服务器会自动安装云监控、云镜等安全服务,方便在控制台查看产品运行状态等

可以看一下服务器的定时任务:

1
2
3
4
5
$ crontab -l

*/1 * * * * /usr/local/qcloud/stargate/admin/start.sh > /dev/null 2>&1 &

0 0 * * * /usr/local/qcloud/YunJing/YDCrontab.sh > /dev/null 2>&1 &

从来不看腾讯云的云监控控制台,要这东西有何用,卸载