2025-06-26 00:00:00
这篇文章简单记录自建DNS over HTTPS(DoH)服务的步骤。
100.100.100.100:53
satishweb/doh-server
Docker容器(监听100.100.100.100:1053
)# 停止并删除旧容器
docker stop doh && docker rm doh
# 启动DoH容器
docker run -d --restart unless-stopped \
--network host \
--name doh \
-e UPSTREAM_DNS_SERVER="udp:100.100.100.100:53" \
-e DOH_HTTP_PREFIX="/dns-query" \
-e DOH_SERVER_LISTEN="100.100.100.100:1053" \
-e DOH_SERVER_TIMEOUT="10" \
-e DOH_SERVER_TRIES="3" \
-e DOH_SERVER_VERBOSE="true" \
satishweb/doh-server
关键参数说明:
UPSTREAM_DNS_SERVER
:上游DNS地址(需可被容器访问)DOH_SERVER_LISTEN
:容器监听的本地地址和端口DOH_HTTP_PREFIX
:DoH请求路径(必须与Nginx配置一致)在Nginx站点配置中(如/etc/nginx/sites-available/default
)添加:
server {
listen 443 ssl;
server_name blog.kelu.org; # 替换为您的域名
# SSL证书配置(必需)
ssl_certificate /path/to/fullchain.pem;
ssl_certificate_key /path/to/privkey.pem;
# DoH代理配置
location = /dns-query {
allow 100.100.100.5; # 允许访问的客户端IP
deny all; # 禁止其他IP
proxy_pass http://100.100.100.100:1053; # 指向DoH容器
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
# 其他配置...
}
操作命令:
sudo nginx -t && sudo systemctl reload nginx # 测试并重载配置
直接访问地址,验证是否成功:
https://aa.bb.com/dns-query?name=baidu.com&type=A
手工生成配置文件 doh.mobileconfig
:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>PayloadContent</key>
<array>
<dict>
<key>DNSSettings</key>
<dict>
<key>DNSProtocol</key>
<string>HTTPS</string>
<key>ServerURL</key>
<string>https://blog.kelu.org/dns-query</string> <!-- 改为您的域名 -->
</dict>
<key>PayloadDescription</key>
<string>Configures DNS over HTTPS</string>
<key>PayloadDisplayName</key>
<string>DoH DNS Server</string>
<key>PayloadIdentifier</key>
<string>com.yourdomain.dns</string>
<key>PayloadType</key>
<string>com.apple.dnsSettings.managed</string>
<key>PayloadUUID</key>
<string>065AB183-5E34-4794-9BEB-B5327CF61F27</string> <!-- 用uuidgen生成 -->
<key>PayloadVersion</key>
<integer>1</integer>
</dict>
</array>
<key>PayloadDescription</key>
<string>Install to enable DNS over HTTPS</string>
<key>PayloadDisplayName</key>
<string>Custom DoH Configuration</string>
<key>PayloadIdentifier</key>
<string>com.yourdomain.dohprofile</string>
<key>PayloadUUID</key>
<string>030E6D6F-69A2-4515-9D77-99342CB9AE76</string> <!-- 用uuidgen生成 -->
<key>PayloadVersion</key>
<integer>1</integer>
</dict>
</plist>
ServerURL
为您的HTTPS地址
- 使用 uuidgen
命令生成新的 PayloadUUID
安装配置:
双击 .mobileconfig
文件导入macOS
提醒打开系统设定,在设备管理里可以看到:
双击后安装即可。
测试DoH服务:
curl -H 'content-type: application/dns-message' \
"https://blog.kelu.org/dns-query?dns=q80BAAABAAAAAAAAA3d3dwdleGFtcGxlA2NvbQAAAQAB"
正常应返回加密的DNS响应。
客户端检查:
scutil --dns | grep 'nameserver\[0\]'
docker logs doh
日志大概长这个样:
2025-06-25 00:00:00
wstunnel 是一个基于 WebSocket 的隧道工具,主要优势包括:
相关资料参考官方文档:https://github.com/erebe/wstunnel
# 创建专用目录
mkdir ~/wstunnel && cd ~/wstunnel
# 下载最新版
chmod +x wstunnel
/wstunnel server wss://[::]:1022 --restrict-to 127.0.0.1:22
监听 1022 端口,转发到22端口。
vi /etc/systemd/system/wstunnel.service
创建一个简单的服务:
# /etc/systemd/system/wstunnel.service
[Unit]
Description=wstunnel
After=network.target
[Service]
User=root
ExecStart=/root/wstunnel/wstunnel server wss://[::]:1022 --restrict-to 127.0.0.1:22
ExecReload=/bin/kill -SIGUSR1 $MAINPID
Restart=on-failure
[Install]
WantedBy=multi-user.target
# 重载 systemd
sudo systemctl daemon-reload
# 启动服务
sudo systemctl start wstunnel
# 设置开机自启
sudo systemctl enable wstunnel
# 检查状态
sudo systemctl status wstunnel
我是 M2 芯片,下载 wstunnel_10.4.3_darwin_arm64.tar.gz
解压到合适的位置,赋权:
chmod +x wstunnel
监听本地2222端口,ssh的目的IP和端口是127.0.0.1:22,后面是转发的服务器IP和1022端口
./wstunnel client -L tcp://2222:127.0.0.1:22 wss://<服务器IP>:1022
ssh -p 2222 root@localhost
2025-06-24 00:00:00
Stunnel 是一个自由的跨平台软件,用于提供全局的TLS/SSL服务。针对本身无法进行TLS或SSL通信的客户端及服务器,Stunnel可提供安全的加密连接。
Stunnel可在许多操作系统下运行,包括Unix-like系统,以及Windows。
Stunnel 基于OpenSSL,要求已经安装了OpenSSL。Stunnel是开源的,支持所有SSL或TLS库所支持的。
在某些网络环境(如严格防火墙限制)中,直接SSH连接可能被阻断。通过 stunnel 建立 TLS 加密隧道:
brew install stunnel
)apt install stunnel4
)创建 ~/Workspace/bin/stunnel-ssh.conf
:
# 全局配置
pid = /Users/kelu/run/stunnel.pid
foreground = yes
output = /Users/kelu/log/stunnel.log
debug = info
fips = no
# 客户端服务定义
[ssh-forward]
client = yes
accept = 127.0.0.1:2222 # 本地监听端口
connect = 你的服务器IP:22 # 替换为实际服务器IP
# SSL/TLS 配置
sslVersion = TLSv1.2
ciphers = AESGCM:ALL:!DH:!EXPORT:!RC4:+HIGH:!MEDIUM:!LOW:!aNULL:!eNULL
# 证书验证
verifyPeer = yes
CAfile = /etc/letsencrypt/live/blog.abc.com/fullchain.pem
checkHost = blog.abc.com # 证书域名验证
# 连接优化
socket = l:TCP_NODELAY=1
socket = r:TCP_NODELAY=1
TIMEOUTidle = 86400 # 超时设置
options = NO_TICKET
options = ALLOW_NO_DHE_KEX
启动隧道
stunnel ~/Workspace/bin/stunnel-ssh.conf
创建 /etc/stunnel/stunnel.conf
:
# 全局配置
pid = /var/run/stunnel.pid
foreground = no
output = /var/log/stunnel4/stunnel.log
debug = info
fips = no
# SSH代理服务
[ssh-forward]
accept = 0.0.0.0:22 # 监听所有接口的22端口
connect = 127.0.0.1:22 # 转发到本地SSH
# protocol = proxy
# 证书配置
cert = /etc/letsencrypt/live/blog.abc.com/fullchain.pem
key = /etc/letsencrypt/live/blog.abc.com/privkey.pem
# 加密设置
sslVersion = TLSv1.2
ciphers = AESGCM:ALL:!DH:!EXPORT:!RC4:+HIGH:!MEDIUM:!LOW:!aNULL:!eNULL
sessionCacheSize = 1000
sessionCacheTimeout = 300
服务器配置中注释了
protocol = proxy
,这是因为我这里使用了stunnel客户端直接连服务端,如果使用 proxy 会导致 Stunnel 在转发流量时添加额外的代理头(包含源地址和目的地址等信息),而 SSH协议无法处理这些头信息,从而导致连接失败。注意:PROXY协议通常用于需要传递客户端真实IP的场景(如HTTP反向代理),但SSH这类原生TCP协议不支持该扩展。移除后即可恢复正常通信。
重启服务:
sudo systemctl restart stunnel4
启动本地stunnel隧道
stunnel ~/Workspace/bin/stunnel-ssh.conf
通过代理连接SSH
ssh -p 2222 [email protected]
此时流量路径:
SSH客户端 → 本地2222端口 → TLS加密隧道 → 服务器22端口 → SSH守护进程
配置项 | 客户端作用 | 服务端作用 |
---|---|---|
accept |
创建本地监听端口 | 指定服务监听端口 |
connect |
目标服务器地址 | 本地SSH服务地址 |
CAfile/cert |
验证服务器证书 | 提供有效TLS证书 |
checkHost |
验证证书域名匹配 | - |
TCP_NODELAY |
禁用Nagle算法降低延迟 | 同上 |
options |
优化TLS协议参数 | 优化TLS协议参数 |
TLS深度包检测(DPI) 系统,可以通过识别 Stunnel 的 TLS 指纹特征进行阻断。
2025-06-11 00:00:00
最近整了个WebShell容器方案。用Docker Compose跑起来倒是方便,但作为tmux重度用户,实际体验嘛,凑合能用。
直接上干活配置,存为docker-compose.yml
就能用:
version: '3'
services:
webshell:
image: bwsw/webshell
container_name: webshell
network_mode: bridge
restart: "no"
environment:
- SSH_PORT=22 # 容器内SSH端口
- USERNAME=root # 默认登录用户
- DEFAULT_IP="100.100.100.100" # 记得改成公网IP
ports:
- "6666:80"
启动:
docker-compose up -d
访问 IP:6666 即可。
http://服务器IP:6666
就能操作临时排查问题确实方便,但是对tmux用户的致命伤,tmux attach 进入后更容易遇到终端渲染抽风。
2025-05-06 00:00:00
最近在执行一个用于计算月度时间统计的脚本时,遇到了一个有趣的错误。这个脚本本应计算当月已经过去的小时数,但在每月的8日和9日却会神秘地失败。错误信息十分具有误导性,让人一时难以找到问题所在:
./tmp.sh: line 20: 08: value too great for base (error token is "08")
(standard_in) 1: syntax error
(standard_in) 1: syntax error
出错的脚本片段如下:
timecheck=$(date "+%Y-%m-%d_%H:%M:%S")
# 固定使用30天720小时计算
month_days=30
total_hours=720
# 获取当前日期、小时和分钟
current_day=$(date +%d)
current_hour=$(date +%H)
current_minute=$(date +%M)
# 计算已过去的小时数(当前日的小时 + 已过去的天数*24)
hours_passed=$(( (current_day - 1) * 24 + current_hour ))
错误发生在第20行,也就是计算hours_passed
的那一行。
这个问题看似简单的计算为何会出错?原因在于Shell中数字解析的一个隐藏陷阱:在Bash等Shell环境中,以0开头的数字默认会被解析为八进制(base-8)数字。
在八进制系统中,只允许使用0-7这八个数字。而在每月的8日和9日,date +%d
命令返回的是”08”和”09”,这在八进制中是非法的,因此导致了”value too great for base”的错误。
这是一个典型的Shell编程陷阱,特别容易在处理日期和时间时遇到,因为日期和时间格式通常会包含前导零。
针对这个问题,有两种有效的解决方案:
# 获取不带前导零的日期和时间
current_day=$(date +%-d)
current_hour=$(date +%-H)
current_minute=$(date +%-M)
通过在格式说明符前添加-
符号(如%-d
而不是%d
),可以让date
命令返回没有前导零的数字,从而避免八进制解析的问题。
如果你的系统不支持无前导零的日期格式(例如某些macOS版本),可以使用以下方法强制以十进制解析:
# 明确指定使用十进制解析
current_day=$((10#$(date +%d)))
current_hour=$((10#$(date +%H)))
current_minute=$((10#$(date +%M)))
通过添加10#
前缀,我们明确告诉Shell使用十进制(base-10)来解析这些数字,无论它们是否有前导零。