Total Pageviews

Showing posts with label linux. Show all posts
Showing posts with label linux. Show all posts

Tuesday, 22 September 2026

通过BGP广播自己的自治域IP段 实现站群服务器,什么是原生IP、广播IP、任播IP?跨境电商必懂的网络IP知识:路由追踪、路由宣告、路由收敛、ASN、自治域


ipinfo查询IP信息:https://ipinfo.io
BGP相关信息:https://bgp.he.net/ip/2a0e:1a84::1
路由追踪:https://tools.ipip.net/traceroute.php?v=6

相关指令

安装bird:apt install bird
ipv4:

/etc/bird/bird.conf

router id 104.207.141.86;

protocol bgp vultr
{
    local as 4288000333;
    source address 104.207.141.86;
    import none;
    export all;
    graceful restart on;
    multihop 2;
    neighbor 169.254.169.254 as 64515;
    password "vultr123";
}
protocol static
{
    route  198.51.100.0/24 via 104.207.141.86;
}

protocol device
{
    scan time 5;
}


birdc c #重载配置文件
birdc show proto all vultr #查看BGP状态
ip route add local 198.51.100.0/24 dev enp1s0 #添加路由
sysctl net.ipv4.ip_nonlocal_bind=1 #允许套接字绑定非本地地址


ipv6:

/etc/bird/bird6.conf

router id 104.207.141.86;

protocol bgp vultr
{
    local as 4288000333;
    source address 2001:19f0:5c01:0c8b:5400:04ff:fed6:8895;
    import none;
    export all;
    graceful restart on;
    multihop 2;
    neighbor 2001:19f0:ffff::1 as 64515;
    password "vultr123";
}
protocol static
{
    route  2a0e:1a84::/32 via 2001:19f0:5c01:0c8b:5400:04ff:fed6:8895;
}

protocol device
{
    scan time 5;
}


birdc6 c #重载配置文件
birdc6 show proto all vultr #查看BGP状态
ip route add local 2a0e:1a84::/32 dev enp1s0 #添加路由
sysctl net.ipv6.ip_nonlocal_bind=1 #允许套接字绑定非本地地址

ndppd:

安装ndppd:apt install ndppd

/etc/ndppd.conf

route-ttl 30000

proxy enp1s0{
   router no
   timeout 500
   ttl 30000
   rule 2001:19f0:5c01:c8b::/64{
       static
   }
}


Xray模板:

{
  "api": {
    "services": [
      "HandlerService",
      "LoggerService",
      "StatsService"
    ],
    "tag": "api"
  },
  "inbounds": [
    {
      "listen": "127.0.0.1",
      "port": 62789,
      "protocol": "dokodemo-door",
      "settings": {
        "address": "127.0.0.1"
      },
      "tag": "api"
    }
  ],
  "outbounds": [
    {
      "protocol": "freedom",
      "settings": {}
    },
    {
      "protocol": "freedom",
      "sendThrough": "2001:19f0:5c01:c8b::/64",
      "settings": {},
      "tag": "randomip"
    },
    {
      "protocol": "blackhole",
      "settings": {},
      "tag": "blocked"
    }
  ],
  "policy": {
    "levels": {
      "0": {
        "handshake": 10,
        "connIdle": 100,
        "uplinkOnly": 2,
        "downlinkOnly": 3,
        "statsUserUplink": true,
        "statsUserDownlink": true,
        "bufferSize": 10240
      }
    },
    "system": {
      "statsInboundDownlink": true,
      "statsInboundUplink": true
    }
  },
  "routing": {
    "rules": [
      {
        "inboundTag": [
          "api"
        ],
        "outboundTag": "api",
        "type": "field"
      },
      {
        "ip": [
          "geoip:private"
        ],
        "outboundTag": "blocked",
        "type": "field"
      },
      {
        "outboundTag": "blocked",
        "protocol": [
          "bittorrent"
        ],
        "type": "field"
      },
      {
        "outboundTag": "randomip",
        "inboundTag": [
          "inbound-51603"
        ],
        "type": "field"
      }
    ]
  },
  "stats": {}
}

通过BGP广播自己的ip段,实现一台vps拥有无限ip地址的站群服务器,看完就知道什么是原生ip,什么是广播ip,什么又是任播ip
通常情况下你购买的vps只有一个公网ip地址,如果你需要多个ip就得购买多台vps,费钱又费事,有些商家允许你加钱多绑定一两个公网ip,但如果你想要绑定几百个甚至几千个那就不行了,需要用BGP解决。

不管你是使用机场节点还是购买vps自己搭建节点,都会经常看到bgp这个词,机场会宣传自己是BGP节点,VPS会宣传自己是BGP线路。为了让大家快速了解相关概念,这里再简单介绍一下,bgp译为边界网关协议,他的主要作用是在不同自治域之间传递路由信息,自治域简称AS,每个自治域都有唯一的编号,简称asn,中国电信和中国联通都有自己的自治域,电信的asn是4134,联通的asn是4837,假设我家办了电信的宽带,接入了电信的网络,我会通过电信分配给我的公网ip3.3.3.3访问互联网,当我访问ipinfo这个网站时,他会检测我的ip地址属于哪个asn,并在asn一栏中显示对应的自治域编号以及宽带运营商等信息

假设你家办了联通的宽带,接入了联通的网络,那么你ip对应的asn就是联通的4837,此时你想和我联机玩游戏,由于电信和联通属于不同的自治域,每个自治域都可以视为一个独立的网络,你可以简单理解为电信和联通是两个不同的局域网,你没法直接访问到我,所以需要将两个网络连接起来,其中用到的路由协议就是BGP,连接之后你就可以将数据发送给我了,整个互联网世界就是通过BGP将所有自治域连接在一起,BGP出问题的话可能导致整个国家断网,可见BGP有多么重要

假设我在vultr这个自治域里买了一台美国的vps,他会给vps分配一个美国的ip地址,我用这台vps搭了节点,那么我通过节点访问ipinfo的时候,它检测到的asn就是vultr的20473,并且显示ip的地理位置在美国,vultr家的vps按小时计费,更换ip比较方便,上手门槛低导致ip被人滥用,很多互联网公司拉黑了他家的ip,比如奈飞就把vultr整个as全都封禁了,作为一名合格的跨境人,当然不能使用这么脏的ip了,有跨境电商需求的用户都希望能找到比较干净的ip防止被平台风控,而很多vps商家或多或少都存在ip被滥用的情况,尤其是比较廉价的VPS,你在购买之前并不清楚分配给你的ip质量如何,这是很多人比较苦恼的问题

为了解决这个问题,有没有可能我自己申请一个asn,然后通过bgp接入其他asn,再租一段ip地址,这样的话我就可以在自己的asn里搭建节点,并给节点分配自己租的ip地址,这样就不用担心ip不干净了,asn也是干净的没有被封禁,并且你想给一台vps分配多少个ip都可以,毕竟这是你的地盘,要实现这个目的的门槛并不高,普通个人身份就可以申请asn,一年也就几百块钱,租用ip地址的话稍微贵一点,一段/48的ipv6地址每个月20刀,一段/24的ipv4地址每个月需要两百刀,并不是我们普通用户能负担的,不过即使你现在不需要BGP也希望你能看下去,会牵扯到很多有意思的内容

asn你可以找提供LIR服务的商家注册,网上有很多,交钱提供个人资料就行了,有些商家还会送一段/48的ipv6地址,如果你觉得麻烦的话也可以不用注册asn,等会再说,ip地址也可以自行寻找提供ip租赁的商家,我这里有一段/32的ipv6地址,是找这个商家嫖来的,这并不是一期广告视频,只是我刚好想做这个内容,秉持着能嫖就不花钱的理念,搜了一下自己的邮箱,看到去年10月份他们给我发过推广邀请,尝试找他们嫖一段ip来做演示,为了防止他们忘记我是干啥的,我还贴心的附上了频道链接,本来还想嫖一段ipv4,他们表示没货了,不过ipv4和ipv6的操作是一样的,所以就给大家演示ipv6,感谢商家提供的ip做演示,有需要的朋友可以进入这个链接找他们购买,有问题可以咨询网站里的在线客服

有了asn和ip地址后,并不意味着我们就能直接接入其他asn了,需要找个地方做ip transit,也就是能让你建立BGP连接的地方,将自己asn里的ip段通过bgp广播出去,transit一般都是要收费的,比如你想接入cn2 gia,那就得花钱找走gia线路的商家给你做transit,然后就可以使用kvm或者OpenVZ之类的虚拟化技术创建VPS做超售奸商了
ip transit也有免费的,比如本期介绍的vultr就提供了免费的transit服务,虽然走不了cn2
gia但好过没有,vultr相信大家都比较熟悉了,很多人的第一个自建节点就是被我这样的affman忽悠买了他家的vps,如果你没有注册过vultr的话可以进入这个链接注册,绑定支付方式之后可以免费试用100美金用于购买vps,有效期为14天

登陆vultr之后先进入network下的bgp页面,现在我们要做的就是将自己的asn通过bgp连接到vultr的asn,点击开始,启用这个选项,如果你有自己的asn,就启用这个选项,然后填入你的asn即可,由于asn会暴露个人身份信息,视频演示我就不启用了,不启用的话vulrt就会给我们分配一个私有asn,操作过程没有什么区别,所以即使你没有asn,租一段ip之后也能直接玩BGP了,设置一个密码,建立BGP连接的时候要用,注意保密,在这里输入你租用的ip段,选择LOA授权文件,授权文件可以找租给你ip的商家获取,这里选择无路由,随便给个描述,点击请求BGP设置,点击start,此时需要向ip的所有者验证ip段是否可以给你用,会发送邮件到这个邮箱去,如果你用的是自己的asn,验证邮件也会发给你注册asn时填写的邮箱,点击发送邮件,此时需要联系商家让他给你通过验证,验证通过之后这里就会显示刚才添加的ip段了

接着你需要创建一台vps用来建立bgp连接,创建非常简单,点击右上角的加号,选择部署新服务器,要想便宜的话选择共享cpu, 然后按你的需要选择一个数据中心,操作系统选择ubuntu22.04,可以选择最低配置,关闭自动备份,ipv6要选中, 最后点击部署就行了

来到compute页面,这是我之前创建的美国vps,系统是ubuntu22.04,这里有个红色的警示符号,提示需要重启机器才能使BGP生效,点击重启服务器,重启之后来到BGP页面,点击这里获取BGP配置,选择这个ipv6配置,需要先给服务器安装bird,建议先使用ufw disable关闭防火墙,使用apt install bird安装bird,安装之后使用vim编辑器打开这个配置文件,将里面的内容清空,把这段配置复制进去,除此之外还要加上这两段,这些用到的代码我会放在视频下方的说明栏,注意这里要改成你租来的ip段,这个就是你vps本身的ipv6地址,保存并退出,接着使用这条指令重载配置文件,然后使用这条指令查看BGP的状态,这里显示Established就表示成功和vultr建立了BGP连接,然后我们会将自己的ip段通过BGP发送给vultr,相当于告诉vultr,如果有人要访问这个ip段,就往我这里路由,这个行为叫做路由宣告,也就是大家常说的ip广播,如果此时我要访问这个ip段,现在只有vultr知道这个ip段在哪,电信还不知道,所以会访问失败,vultr收到路由信息后,会将信息再传递给他的BGP邻居,告诉他们,如果你要访问这个ip段,就可以从我这里过去,以此类推,直到全球的BGP路由都知道怎么到你这个ip段,也就意味着路由收敛完成了,此时我再访问这个ip段,电信就会根据自己的路由规则选择合适的路线将数据发送给我们的自治域

现在只是把ip段广播出去了,我们还需要将ip绑定到某一台具体的vps,网络才能通,你可以绑定某个具体的ip,但我们作为站群服务器,希望将所有ip都绑定到同一台vps上,跟着操作把这个ip段添加到本地路由表,注意这里要改成你的网卡接口名称,通过ip a查看,将其复制到vps上执行,接着再执行这条指令,让套接字支持绑定非本地地址,此时就算是配置好了,访问这个ip段里的任何地址都将访问到这台vps,同时这台vps也可以用ip段里的任何地址访问别人,实现站群服务器的效果
随便ping网段内的地址,没问题,接着从外部ping试试,同样没有问题,任何网段里的地址都将指向你的vps
我刚嫖到这个ip段的时候查询ipinfo,由于没有挂在任何asn上,所以不会显示asn信息,当我将IP广播出去之后,等待48小时左右,现在再次访问ipinfo,输入我们的ip段,可以看到已经有asn信息了,as号是vultr家的20473,如果之前你创建BGP的时候填的是你自己的asn,那么这里将会显示你的asn信息,可以看到vultr的asn类型是hosting,如果你是个人asn,一般情况下类型会显示business,下面的company是ip所有者的信息,也就是ip在哪租的,类型现在变成了business,本来是显示isp,具体原因不明,我想补充的是,这些类型本身都是没有的,你在注册asn的时候也不会让你选择是hosting还是isp类型,ipinfo是一个提供ip数据信息的商业公司,asn类型是他们自己通过一些手段判断该自治域提供什么服务,然后打上对应的标签,有一定的参考价值,但不绝对,需要具体业务具体分析

另外可以看到这里的ip归属地是NL荷兰,而我们vps的机房位置是在美国,这种机房位置和ip归属地不在同一地区的ip被大家称之为广播ip,反之如果机房位置和ip归属地是同一地区,就把它称之为原生ip,这其实是一个错误的概念,如果我在荷兰的vps上进行bgp路由宣告,那我是不是就是原生ip了呢?显然是不对的,多数情况下原生ip和广播ip的称呼只是用来区分能否解锁奈飞之类的流媒体平台,但实际上你要清楚,对很多流媒体平台而言只有两种ip,一种是在黑名单里的ip,另一种是其他ip

而且你如果现在去ipinfo查我这个ip段的归属地,已经变成美国了,因为像ipinfo这些平台是通过traceroute路由跟踪来判断ip归属地,当监测到这个ip会被路由到美国的数据中心,ipinfo就会更新他们的ip数据库,从而显示正确的ip归属地,比如我现在进行路由跟踪,很明显被路由到了vultr美国的数据中心,这里的ip归属地显示荷兰肯定是不对的,还没有更新ip数据库,更新过程是比较漫长的,可能需要十天半个月,我这边是发邮件催了ipinfo,更新也花了三天的时间,现在你在其他平台测试这个ip,可能有些网站还是显示归属地为荷兰,那就是他们没有更新这个ip的归属地信息,如果一个ip段能常年稳定在某个地区的数据中心,那么大多数平台都能获取到ip的正确归属地,如果经常在不同地区的数据中心进行广播,那不同平台检测到的ip归属地就会出现各种地区,这就是为什么明明是美国的vps,奈飞却解锁荷兰地区

另外这个ip本来可以解锁奈飞荷兰地区,由于我没有使用自己的asn,ip相当于直接挂在vultr的asn里,而奈飞把vultr整个asn的所有ip段全都拉黑了,导致这个本来可以解锁的ip段,由于asn的原因也变得不能解锁了,连自制剧都看不了,所以如果你对ip的纯净度有要求,建议还是自己注册一个asn,可以看到流媒体检测这里还有三个不同的地区,检测到了美国与荷兰大家还能理解,但为什么多了个日本东京?

因为vultr除了支持让你的ip在某一个数据中心进行路由宣告,同时也支持在多个数据中心进行宣告,实现类似cloudflare1.1.1.1的anycast任播效果,比如我再创建一台日本东京的vps,进行和美国vps同样的BGP操作,现在日本和美国的vps绑定了同一个ip段,此时我在不同的地区对同一个ip地址进行路由追踪,新加坡就会访问离他近的日本vps,加拿大就会访问离他近的美国vps,所以检测脚本出现了解锁东京地区的流媒体

接下来就可以参考之前讲过的站群服务器教程实现不同端口使用不同的落地ip,由于之前的教程需要先绑定ip才能使用,这次我们换个方法,最新版的xray更新了一个功能,同一个节点,每次连接都可以使用不同的ipv6落地,简直就是爬虫者的福音,据说灵感来着某个姓不的博主

搭建xui面板的过程就直接跳过了,先将xray更新到最新版的1.8.10,接着创建两个节点,随便什么协议都可以,一个节点使用33333端口,一个节点使用44444端口,将其导入到v2rayN中,选中其中一个节点,访问这个网址,这个网址可以看到用的是哪个落地ip访问当前网页,显示了当前vps的ipv6地址,结尾是8895,选中另一个节点,刷新页面,同样还是8895,接着我打算把端口33333的节点改成随机落地ip,这个配置模板我会提供给大家,将这里改成你的ip段,把这里改成你的节点端口,也就是33333,全选复制,进入xui的面板设置,将原来的xray模板替换掉,保存配置,然后重启面板,现在相当于使用33333端口的节点就会使用随机落地ip,切换到33333节点,刷新网页,可以看到ip地址发生了变化,前面32个比特位就是我们ipv6的前缀,后面则是完全随机的,切回44444节点,ip又会固定变成vps原来的ipv6地址8895,再切回来,ip又变成随机ip,结尾是97af,这个在做一些爬虫的话非常方便,当然前提是被爬的网站要支持ipv6,刷新页面,ip没有发生改变,因为这条tcp连接没有断开,开个无痕窗口访问,可以看到结尾就变成另一个随机ip了,每次新的tcp连接都会使用新的随机落地ip

另外由于直连vultr的vps速度比较慢,用他家vps搭建的节点晚高峰肯定会非常卡,而且他家的ip特别容易被阻断连接,建议给节点做中转,可以将速度提升到专线水平

如果你没有租用ip,也可以直接拿本机的ipv6地址玩随机落地,可以看到vps默认提供的ipv6地址是这个,前缀长度为64位,虽然后64位的地址都是你的,但如果现在直接ping的话,由于vps没有绑定这个ipv6地址,外部是无法ping通的,可以看到全都丢包了,需要进行相关设置,前面关于bgp的设置不用管,从执行这两行开始,这里改成你vps的ipv6前缀地址,这里输入你的网卡接口名称,然后复制这两行到vps上执行,接着安装ndp代理工具,创建这个配置文件,将这段配置粘贴上去,注意这里要改成你的ipv6前缀,以及网卡接口名称,保存退出,接着重启ndp代理工具,此时再来ping该前缀下的任何ip地址,就可以通了,说明该前缀下的任何ipv6地址可以正常访问到你的vps,接着还是同样的操作,把xray模板里的ipv6前缀替换掉,全选复制,将xui的模板替换掉,这次我把这里改成44444,也就是使用44444端口的节点访问网站就用随机的落地ip,其它端口的节点保持正常,保存配置并重启面板,切换到44444节点,刷新网页,可以看到前64位是固定的前缀,后面是完全随机的,接着开个无痕窗口使用新的tcp连接来访问,每次都是新的落地ip,如果使用其它端口的节点,就会保持默认的ip,不会使用随机落地。

Monday, 7 September 2026

Hermes Agent 备份教程:Rclone+R2+Cron

 

Hermes Agent 的配置、会话、记忆、技能和凭据大多放在 ~/.hermes。VPS 一旦被封、误删或磁盘损坏,重新安装程序不难,真正麻烦的是把这套长期积累的状态找回来。

下面这套方案用 Rclone 把 ~/.hermes 打包、加密并上传到 Cloudflare R2,再交给 Linux 系统的 Cron 每天执行。它不依赖 Hermes 自己的定时任务:即使 Hermes Agent 临时故障,系统 Cron 仍能继续运行备份。

先说两个修正:不要把加密密码直接写进脚本,也不要用 pkill -f "hermes start" 粗暴结束进程。前者会泄露密码,后者可能杀掉错误进程,而且原方案没有可靠地把 Hermes 重启。

一、准备 Cloudflare R2

进入 Cloudflare 控制台,打开 R2 对象存储,完成下面三件事:

  1. 记录 Cloudflare Account ID。
  2. 创建一个私有存储桶,例如 hermes-backup-bucket。
  3. 在“管理 R2 API 令牌”中创建令牌,授予目标存储桶的对象读取、写入和删除权限。

保存好生成的 Access Key ID 和 Secret Access Key。Secret 只会完整显示一次,不要发到聊天窗口,也不要写入公开仓库。

二、安装并配置 Rclone

官方安装脚本适合 Debian、Ubuntu 等常见 Linux 发行版:

curl -fsSL https://rclone.org/install.sh | sudo bash
rclone version

如果不想执行网络脚本,也可以通过系统包管理器安装,但仓库里的版本可能偏旧。

启动交互式配置:

rclone config

按下面的值填写。Rclone 不同版本的菜单编号可能变化,所以应看选项名称,不要死记数字。

  1. 选择 n 新建 remote,名称填写 cf_r2。
  2. 存储类型选择 s3。
  3. Provider 选择 Cloudflare。
  4. 填写 Access Key ID 和 Secret Access Key。
  5. Endpoint 填写 https://<ACCOUNT_ID>.r2.cloudflarestorage.com,把占位符换成真实 Account ID。
  6. Region 保持 auto 或留空;ACL 保持 private。
  7. 保存配置并输入 q 退出。

测试连接:

rclone lsd cf_r2:
rclone lsf cf_r2:hermes-backup-bucket

第一条命令能列出存储桶,第二条没有报错,就说明认证和桶权限正常。

三、单独保存加密密码

创建只允许当前用户读取的密码文件。下面的示例字符串必须替换,建议使用密码管理器生成至少 24 位随机密码。

mkdir -p "$HOME/.config/hermes-backup"
umask 077
printf '%s\n' '请替换为你自己的长随机密码' \
  > "$HOME/.config/hermes-backup/passphrase"
chmod 600 "$HOME/.config/hermes-backup/passphrase"

这个密码文件不能放进 ~/.hermes,否则恢复时会陷入“没有密码就打不开包含密码的备份”这个死循环。请把密码再保存一份到本地密码管理器或离线介质。

四、创建安全版备份脚本

脚本建议放在当前用户目录,而不是直接写进 /opt。这样不需要让普通备份任务持有 root 权限。

mkdir -p "$HOME/bin"
nano "$HOME/bin/hermes-r2-backup.sh"

粘贴以下完整脚本:

#!/usr/bin/env bash
set -Eeuo pipefail
umask 077

PATH="$HOME/.local/bin:/usr/local/bin:/usr/bin:/bin"
REMOTE_NAME="cf_r2"
BUCKET_PATH="hermes-backup-bucket/hermes-agent"
RETENTION_DAYS=30
HERMES_DIR="${HERMES_HOME:-$HOME/.hermes}"
PASSWORD_FILE="$HOME/.config/hermes-backup/passphrase"
LOCK_FILE="${TMPDIR:-/tmp}/hermes-r2-backup.lock"

command -v rclone >/dev/null || { echo "rclone 未安装"; exit 1; }
command -v openssl >/dev/null || { echo "openssl 未安装"; exit 1; }
[[ -d "$HERMES_DIR" ]] || { echo "目录不存在: $HERMES_DIR"; exit 1; }
[[ -s "$PASSWORD_FILE" ]] || { echo "密码文件不存在: $PASSWORD_FILE"; exit 1; }

exec 9>"$LOCK_FILE"
flock -n 9 || { echo "已有备份任务正在运行,本次退出"; exit 0; }

DATE_TAG="$(date -u +%Y%m%d_%H%M%S)"
TMP_DIR="$(mktemp -d "${TMPDIR:-/tmp}/hermes-backup.XXXXXX")"
BACKUP_FILE="$TMP_DIR/hermes_${DATE_TAG}.tar.gz.enc"
REMOTE_DIR="${REMOTE_NAME}:${BUCKET_PATH}"
GATEWAY_WAS_ACTIVE=0

cleanup() {
  rc=$?
  rm -rf -- "$TMP_DIR"
  if [[ "$GATEWAY_WAS_ACTIVE" -eq 1 ]]; then
    hermes gateway start >/dev/null 2>&1 || true
  fi
  exit "$rc"
}
trap cleanup EXIT INT TERM

# 官方 gateway 服务运行时先正常停止,避免复制写入中的 SQLite 数据库。
if command -v systemctl >/dev/null \
  && systemctl --user is-active --quiet hermes-gateway.service; then
  GATEWAY_WAS_ACTIVE=1
  hermes gateway stop
fi

# -C 很重要:归档内保存的是 .hermes/...,恢复时不会多出 home/user 目录。
tar -C "$(dirname "$HERMES_DIR")" -czf - "$(basename "$HERMES_DIR")" \
  | openssl enc -aes-256-cbc -salt -pbkdf2 -iter 200000 \
      -pass "file:$PASSWORD_FILE" -out "$BACKUP_FILE"

# copyto 明确指定远端文件名,避免路径歧义。
rclone copyto "$BACKUP_FILE" "$REMOTE_DIR/$(basename "$BACKUP_FILE")" \
  --s3-no-check-bucket

# 回读远端对象;失败时脚本返回非零,不会把“上传失败”当成成功。
rclone lsf "$REMOTE_DIR" --files-only | grep -Fx "$(basename "$BACKUP_FILE")" >/dev/null

# 删除超过保留期的旧文件,并清理空目录。
rclone delete "$REMOTE_DIR" --min-age "${RETENTION_DAYS}d"
rclone rmdirs "$REMOTE_DIR" --leave-root

printf 'Backup %s finished: %s\n' "$DATE_TAG" "$(basename "$BACKUP_FILE")"

保存后赋予执行权限:

chmod 700 "$HOME/bin/hermes-r2-backup.sh"

先手动执行一次:

"$HOME/bin/hermes-r2-backup.sh"
rclone lsl cf_r2:hermes-backup-bucket/hermes-agent

看到以 hermes_年月日_时间.tar.gz.enc 命名的对象,才算完成第一次验证。

五、设置每日 Cron

编辑当前 Hermes 用户的 crontab,不要用 root 的 crontab,否则 $HOME、Rclone 配置和 Hermes 目录都会指向错误用户。

crontab -e

每天凌晨 2 点运行:

0 2 * * * "$HOME/bin/hermes-r2-backup.sh" >> "$HOME/.hermes/hermes_backup.log" 2>&1

每 12 小时运行一次:

0 */12 * * * "$HOME/bin/hermes-r2-backup.sh" >> "$HOME/.hermes/hermes_backup.log" 2>&1

检查任务和日志:

crontab -l
tail -n 100 "$HOME/.hermes/hermes_backup.log"

这里用的是 Linux 系统 Cron,不是 hermes cron。备份属于灾难恢复基础设施,应该尽量独立于被备份的应用。

六、新 VPS 上完整恢复

新机器先安装 Hermes Agent、Rclone 和 OpenSSL,再重新配置同名的 cf_r2 remote。不要直接运行 hermes setup 覆盖旧配置。

curl -fsSL https://hermes-agent.nousresearch.com/install.sh | bash
curl -fsSL https://rclone.org/install.sh | sudo bash
rclone config

创建密码文件,并填入备份时使用的同一个密码:

mkdir -p "$HOME/.config/hermes-backup"
umask 077
printf '%s\n' '你的原加密密码' \
  > "$HOME/.config/hermes-backup/passphrase"
chmod 600 "$HOME/.config/hermes-backup/passphrase"

自动找出时间戳最新的备份并下载:

REMOTE_DIR="cf_r2:hermes-backup-bucket/hermes-agent"
LATEST="$(rclone lsf "$REMOTE_DIR" --files-only | sort | tail -n 1)"
[[ -n "$LATEST" ]] || { echo "没有找到备份"; exit 1; }
rclone copyto "$REMOTE_DIR/$LATEST" "$HOME/$LATEST"

解密到临时目录并检查结构:

RESTORE_DIR="$(mktemp -d)"
openssl enc -d -aes-256-cbc -pbkdf2 -iter 200000 \
  -pass "file:$HOME/.config/hermes-backup/passphrase" \
  -in "$HOME/$LATEST" -out "$RESTORE_DIR/hermes.tar.gz"

tar -xzf "$RESTORE_DIR/hermes.tar.gz" -C "$RESTORE_DIR"
test -f "$RESTORE_DIR/.hermes/config.yaml"
ls -la "$RESTORE_DIR/.hermes"

确认目录无误后再替换。先保留当前目录,出错时还能回滚:

hermes gateway stop || true
[[ -d "$HOME/.hermes" ]] \
  && mv "$HOME/.hermes" "$HOME/.hermes.before-restore-$(date +%s)"
mv "$RESTORE_DIR/.hermes" "$HOME/.hermes"
chmod -R go-rwx "$HOME/.hermes"

hermes doctor
hermes gateway start
hermes gateway status

恢复后检查模型凭据、Telegram/Discord 等频道、Cron 任务和技能是否正常。系统 crontab 与 Rclone 配置不在 ~/.hermes 中,所以新机上仍需重新配置。

七、上线前必须做的四个测试

  1. 上传测试:手动运行脚本并在 R2 中确认对象大小不是 0。
  2. 解密测试:下载一份备份,在临时目录解密并确认能看到 .hermes/config.yaml。
  3. Cron 测试:临时把计划改为几分钟后执行,确认非交互环境下也能找到 rclone 和 Hermes。
  4. 恢复演练:至少在另一台测试机完整恢复一次。没有做过恢复演练的备份,只能算“可能有用”。

R2 端还可以设置生命周期规则作为第二道保留策略。无论是否启用云端自动清理,都建议每月下载一份到本地硬盘。R2 解决的是 VPS 单点故障,本地副本则防止账号、令牌或云存储本身出现问题。

参考资料

Sunday, 23 August 2026

3款httpd程序-asmhttpd, bashttpd, libmicrohttpd

 A minimalist HTTP server for Linux, written in x86_64 assembly。

asmhttpd - The tiniest webserver ever written.

HOW TO BUILD:

	Just run "make". You will need NASM on your system.

HOW TO RUN:

	./asmhttpd /path/to/your/webroot
	sudo ./asmhttpd /path/to/your/webroot

If run as root, it will listen on port 80. Otherwise, it will use port 8080.

HOW TINY IS IT?

The entire text and data fit on a single 4K page:

  {0}[calvin ~] cat /proc/$(pgrep -n asmhttpd)/maps
  00401000-00402000 r-xp 00001000 00:16 2483272 asmhttpd

Each client allocates an additional 4K page and a thread while connected.

The code is written in a monolithic "branching tree" style with no functions,
and uses registers for all local variables. RAM is only used for buffering the
HTTP request, and for building structures necessary for system calls.

Because there is no stack, and the targets of all branch instructions are
constants, traditional buffer overflow exploits are impossible.
from  https://github.com/jcalvinowens/asmhttpd
-------
A web server written in bash。
 

bashttpd is a simple, configurable web server written in bash

Requirements

  1. bash, any recent version should work
  2. socat or netcat to handle the underlying sockets.
  3. A healthy dose of insanity

Examples

  socat TCP4-LISTEN:8080 EXEC:/usr/local/bin/bashttpd

Or

  netcat -lp 8080 -e ./bashttpd

Note that in the socat example above, the web server will immediately exit once the first connection closes. If you wish to serve to more than one client - like most servers do, then use the variant:

 socat TCP4-LISTEN:8080,fork EXEC:/usr/local/bin/bashttpd

This way, a new process is spawned for each incoming connection.

Getting started

  1. Running bashttpd for the first time will generate a default configuration file, bashttpd.conf
  2. Review bashttpd.conf and configure it as you want.
  3. Run bashttpd using netcat or socat, as listed above.

Features

  1. Serves text and HTML files
  2. Shows directory listings
  3. Allows for configuration based on the client-specified URI

Limitations

  1. Does not support authentication
  2. Doesn't strictly adhere to the HTTP spec.

Security

  1. Only rudimentary input handling. We would not running this on a public machine.

HTTP protocol support

403: Returned when a directory is not listable, or a file is not readable 400: Returned when the first word of the first line is not GET 200: Returned with valid content Content-type: Bashttpd uses /usr/bin/file to determine the MIME type to sent to the browser 1.0: The server doesn't support Host: headers or other HTTP/1.1 features - it barely supports HTTP/1.0!

As always, your patches/pull requests are welcome!

from  https://github.com/tootallnate/bashttpd

-----

 

GNU Libmicrohttpd


GNU libmicrohttpd is a small C library that makes it easy to run an HTTP server as part of another application. GNU Libmicrohttpd is free software and part of the GNU project. Key features that distinguish GNU Libmicrohttpd from other projects are:

  • C library: fast and small
  • API is simple, expressive and fully reentrant
  • Implementation is HTTP 1.1 compliant
  • HTTP server can listen on multiple ports
  • Various threading modes: run in application thread, internal thread, thread pool, and thread-per-connection
  • Three different sockets polling modes: select(), poll(), and epoll
  • Minimised number of sys-calls to avoid extra user/kernel mode switches
  • Supported platforms include GNU/Linux, FreeBSD, OpenBSD, NetBSD, Android, Darwin (macOS), W32, OpenIndiana/Solaris, and z/OS
  • Support for IPv6
  • Support for SHOUTcast
  • Support for incremental processing of POST data (optional)
  • Support for basic and digest authentication (optional)
  • Support for TLS (requires libgnutls, optional)
  • Binary is only about 32k (without TLS support and other optional features)

GNU libmicrohttpd was started because the author needed an easy way to add a concurrent HTTP server to other projects. Existing alternatives were either non-free, not reentrant, standalone, of terrible code quality, or a combination thereof. Do not use GNU libmicrohttpd if you are looking for a standalone HTTP server, there are many other projects out there that provide that kind of functionality already. However, if you want to be able to serve WWW pages from within your C or C++ application, check it out.

GNU libmicrohttpd is maintained by Evgeny Grin (Karlson2k) and another co-maintainer.

Downloading libmicrohttpd

There are currently two main versions of the library available. GNU libmicrohttpd 1.x is the stable version, and GNU libmicrohttpd 2.x is experimental and remains under heavy development. GNU libmicrohttpd 2.x can currently only be obtained by cloning our Git repository at git://git.gnunet.org/libmicrohttpd2.git.
Source Code
Libmicrohttpd is available from the main GNU FTP server via HTTP(S) and FTP. It can also be found on the GNU mirrors; please use a mirror if possible.
Debian .deb package
The debian package can be downloaded from the official debian archive. The respective packages for libmicrohttpd are libmicrohttpd and for development libmicrohttpd-dev.
Tar Package
The latest version can be found on GNU mirrors. If the mirror does not work, you should be able to find them on the main FTP server.

Latest release is libmicrohttpd-latest.tar.gz.

Windows
Latest Windows binary is libmicrohttpd-latest-w32-bin.zip.

Documentation

In addition to the brief documentation on this webpage, we have various other forms of documentation available:

microhttpd.h
This include file documents most of the API in detail.
Manual
A manual for Libmicrohttpd is available online, as is documentation for most GNU software. You may also find more information about Libmicrohttpd by running info libmicrohttpd or man libmicrohttpd, or by looking at /usr/share/doc/libmicrohttpd/, /usr/local/doc/libmicrohttpd/, or similar directories on your system.
Tutorial
The GNU Libmicrohttpd tutorial is available as one document in PDF and HTML formats.
Security audit 2025
Ada Logics with support from the Sovereign Tech Agency performed a security audit of the source code of the GNU libmicrohttpd 2.x codebase in August 2025. All discovered issues were addressed. The full report is available here.

Mailing lists

Libmicrohttpd uses the libmicrohttpd mailinglist to discuss all aspects of Libmicrohttpd, including support, development, and enhancement requests, as well as bug reports.

Announcements about Libmicrohttpd and most other GNU software are made on info-gnu (archive).

Security reports that should not be made immediately public can be sent directly to the maintainers. If there is no response to an urgent issue, you can escalate to the general security mailing list for advice.

Getting involved

Development of Libmicrohttpd, and GNU in general, is a volunteer effort, and you can contribute. For information, please read How to help GNU. If you'd like to get involved, it's a good idea to join the discussion mailing list (see above).

Development
Known bugs and open feature requests are tracked in our bugtracker. You need to sign up for a reporter account. Please make sure you report bugs under libmicrohttpd and not under any of the other projects.
Git access

You can access the current development version of libmicrohttpd using

$ git clone https://git.gnunet.org/libmicrohttpd.git

Quick Introduction

Dependencies
GNU Libmicrohttpd can be used without any dependencies; however, for TLS (HTTPS) support it require libgnutls. Furthermore, the testcases use libcurl. Some extended testcases also use zzuf and socat (to simulate clients that violate the HTTP protocols). You can compile and use GNU Libmicrohttpd without installing libgnutls, libcurl, zzuf or socat. 
 
 
Threading modes
The example above uses the simplest threading mode, MHD_USE_THREAD_PER_CONNECTION. In this mode, MHD starts one thread to listen on the port for new connections and then spawns a new thread to handle each connection. This mode is great if the HTTP server has hardly any state that is shared between connections (no synchronization issues!) and may need to perform blocking operations (such as extensive IO or running of code) to handle an individual connection.

The second threading mode, MHD_USE_SELECT_INTERNALLY, uses only a single thread to handle listening on the port and processing of requests. This mode is preferable if spawning a thread for each connection would be costly. If the HTTP server is able to quickly produce responses without much computational overhead for each connection, this mode can be a great choice. Note that MHD will still start a single thread for itself – this way, the main program can continue with its operations after calling MHD_daemon_start. Naturally, if the HTTP server needs to interact with shared state in the main application, synchronization will be required. If such synchronization in code providing a response results in blocking, all HTTP server operations on all connections will stall. This mode is a bad choice if response data (for responses generated using the MHD_create_response_from_callback function) cannot always be provided instantly. The reason is that the code generating responses should not block (since that would block all other connections) and on the other hand, if response data is not available immediately, MHD will start to busy wait on it. Use the first mode if you want to block on providing response data in the callback, or the last mode if you want to use a more event-driven mode with one big select loop.

The third mode combines a thread pool with the MHD_USE_SELECT_INTERNALLY mode, which can benefit implementations that require scalability. As said before, by default this mode only uses a single thread. When combined with the thread pool option, it is possible to handle multiple connections with multiple threads. The number of threads is specified using the MHD_OPTION_THREAD_POOL_SIZE; any value greater than one for this option will activate the use of the thread pool. In contrast to the MHD_USE_THREAD_PER_CONNECTION mode (where each thread handles one and only one connection), threads in the pool can handle a large number of concurrent connections. Using MHD_USE_SELECT_INTERNALLY in combination with a thread pool is typically the most scalable (but also hardest to debug) mode of operation for MHD.

The fourth threading mode (used when no specific flag is given), uses no threads. Instead, the main application must (periodically) request file descriptor sets from MHD, perform a select call and then call MHD_run. MHD_run will then process HTTP requests as usual and return. MHD_run is guaranteed to not block; however, access handlers and response processing callbacks that it invokes may block. This mode is useful if a single-threaded implementation is desired and in particular if the main application already uses a select loop for its processing. If the application is not ready to provide a response, it can just return zero for the number of bytes read and use its file descriptors in the external select loop to wake up and continue once the data is ready – MHD will unlist the socket from the write set if the application failed to provide response data (this only happens in this mode).

The testcases provided include examples for using each of the threading modes.

Generating responses
MHD provides various functions to create struct MHD_Response objects. A response consists of a set of HTTP headers and a (possibly empty) body. The three main ways to create a response are either by specifying a given (fixed-size) body (MHD_create_response_from_data), by providing a function of type MHD_ContentReaderCallback which provides portions of the response as needed or by providing an open file descriptor (MHD_create_response_from_fd). The first response construction is great for small and in particular static webpages that fit into memory. The second response type should be used for response objects where the size is initially not known or where the response maybe too large to fit into memory. Finally, using a file descriptor can be used on Linux systems to use the highly efficient sendfile call for the file transfer.

A response is used by calling MHD_queue_response which sends the response back to the client on the specified connection. Once created, a response object can be used any number of times. Internally, each response uses a reference counter. The response is freed once the reference counter reaches zero. The HTTP server should call MHD_destroy_response when a response object is no longer needed, that is, the server will not call MHD_queue_response again using this response object. Note that this does not mean that the response will be immediately destroyed – destruction may be delayed until sending of the response is complete on all connections that have the response in the queue.

Queueing responses
Clients should never create a "100 CONTINUE" response. MHD handles "100 CONTINUE" internally and only allows clients to queue a single response per connection. Furthermore, clients must not queue a response before the request has been fully received (except in the case of rejecting PUT or POST operations in HTTP 1.1). If a client attempts to queue multiple responses or attempts to queue a response early, MHD_queue_response will fail (and return MHD_NO).

The callback function for the respective URL will be called at least twice. The first call happens after the server has received the headers. The client should use the last void** argument to store internal context for the session. The first call to the callback function is mostly for this type of initialization and for internal access checks. At least, the callback function should "remember" that the first call with just the headers has happened. Queueing a response during the first call (for a given connection) should only be used for errors – if the client queues a response during this first call, a "100 CONTINUE" response will be suppressed, the request body will not be read and the connection will be closed after sending the response. After the first call, the callback function will be called with upload data. Until *upload_data_size is zero, the callback may not queue a response, any such attempt will fail. The callback function should update *upload_data_size to indicate how many bytes were processed. Depending on available buffer space, incremental processing of the upload maybe required. Once all of the upload data has been processed, MHD will call the callback a second time with *upload_data_size being zero. At this point, the callback should queue a "normal" response. If queueing a response is not possible, the callback may either block or simply not queue a response depending on the threading mode that is used. If the callback does not queue a response at this point, MHD will either (eventually) timeout the connection or keep calling it.

Parsing of POST requests
MHD includes a set of three functions for parsing and processing data received in POST requests. The functions allow incremental parsing and processing of POST data. Only a tiny fraction of the overall POST data needs to fit into memory. As a result, applications using MHD can support POST requests of arbitrary size. POST data is processed by providing MHD with a callback function that is called on portions of the received values. The POST parser itself is invoked repeatedly whenever more input bytes become available. MHD supports both uri- and multipart/form-encoded POST data.
Memory Management
The application can determine the size of buffers that MHD should use for handling of HTTP requests and parsing of POST data. This way, MHD users can trade-off processing time and memory utilization. Applications can limit the overall number of connections MHD will accept, as well as the total amount of memory used per connection. MHD will gracefully handle all out-of-memory situations (by closing the connection and cleaning up any remaining state). 
 
from  https://www.gnu.org/software/libmicrohttpd

 

 

 

Thursday, 20 August 2026

Tuesday, 18 August 2026

SSHM - SSH Manager

 SSHM is a beautiful command-line tool that transforms how you manage and connect to your SSH hosts. Built with Go and featuring an intuitive TUI interface, it makes SSH connection management effortless and enjoyable.

Go Release License Platform

A modern, interactive SSH Manager for your terminal 🔥

SSHM is a beautiful command-line tool that transforms how you manage and connect to your SSH hosts. Built with Go and featuring an intuitive TUI interface, it makes SSH connection management effortless and enjoyable.

Demo SSHM Terminal
🖱️ Click on the image to view in full size

✨ Features

🚀 Core Capabilities

  • 🎨 Beautiful TUI Interface - Navigate your SSH hosts with an elegant, interactive terminal UI
  • ⚡ Quick Connect - Connect to any host instantly through the TUI or the CLI with sshm <host>
  • 🔄 Port Forwarding - Easy setup for Local, Remote, and Dynamic (SOCKS) forwarding with history persistence
  • 📝 Easy Management - Add, edit, move, and manage SSH configurations seamlessly
  • 🏷️ Tag Support - Organize your hosts with custom tags for better categorization; use the special hidden tag to exclude hosts from the list while keeping them connectable
  • 🔍 Smart Search - Find hosts quickly with built-in filtering and search
  • 📝 Real-time Status - Live SSH connectivity indicators with asynchronous ping checks and color-coded status
  • 🔔 Smart Updates - Automatic version checking with update notifications
  • 📈 Connection History - Track your SSH connections with last login timestamps

🛠️ Technical Features

  • 🔒 Secure - Works directly with your existing ~/.ssh/config file
  • 📁 Custom Config Support - Use any SSH configuration file with the -c flag
  • 📂 SSH Include Support - Full support for SSH Include directives to organize configurations across multiple files
  • ⚙️ SSH Options Support - Add any SSH configuration option through intuitive forms
  • 🔄 Automatic Conversion - Seamlessly converts between command-line and config formats
  • 🔄 Automatic Backups - Backup configurations automatically before changes
  • ✅ Validation - Prevent configuration errors with built-in validation
  • 🔗 ProxyJump/ProxyCommand Support - Secure connection tunneling through bastion hosts
  • ⌨️ Keyboard Shortcuts - Power user navigation with vim-like shortcuts
  • 🌐 Cross-platform - Supports Linux, macOS (Intel & Apple Silicon), and Windows
  • ⚡ Lightweight - Single binary with no dependencies, zero configuration required

🚀 Quick Start

Installation

Homebrew (Recommended for macOS):

brew install Gu1llaum-3/sshm/sshm

Unix/Linux/macOS (One-line install):

curl -sSL https://raw.githubusercontent.com/Gu1llaum-3/sshm/main/install/unix.sh | bash

Windows (PowerShell):

irm https://raw.githubusercontent.com/Gu1llaum-3/sshm/main/install/windows.ps1 | iex

Alternative methods:

Linux/macOS:

# Download specific release
wget https://github.com/Gu1llaum-3/sshm/releases/latest/download/sshm-linux-amd64.tar.gz

# Extract and install
tar -xzf sshm-linux-amd64.tar.gz
sudo mv sshm-linux-amd64 /usr/local/bin/sshm

Windows:

# Download and extract
Invoke-WebRequest -Uri "https://github.com/Gu1llaum-3/sshm/releases/latest/download/sshm-windows-amd64.zip" -OutFile "sshm-windows-amd64.zip"
Expand-Archive sshm-windows-amd64.zip -DestinationPath C:\tools\
# Add C:\tools to your PATH environment variable

📖 Usage

Interactive Mode

Launch SSHM without arguments to enter the beautiful TUI interface:

sshm

Navigation:

  • ↑/↓ or j/k - Navigate hosts
  • Enter - Connect to selected host
  • a - Add new host
  • e - Edit selected host
  • d - Delete selected host
  • m - Move host to another config file (requires SSH Include directives)
  • f - Port forwarding setup
  • H - Toggle hidden hosts visibility
  • q - Quit
  • / - Search/filter hosts

Real-time Status Indicators:

  • 🟢 Online - Host is reachable via SSH
  • 🟡 Connecting - Currently checking host connectivity
  • 🔴 Offline - Host is unreachable or SSH connection failed
  • ⚫ Unknown - Connectivity status not yet determined

Sorting & Filtering:

  • s - Switch between sorting modes (name ↔ last login)
  • n - Sort by name (alphabetical)
  • r - Sort by recent (last login time)
  • Tab - Cycle between filtering modes
  • Filter by name (default) - Search through host names
  • Filter by last login - Sort and filter by most recently used connections

The interactive forms will guide you through configuration:

  • Hostname/IP - Server address
  • Username - SSH user
  • Port - SSH port (default: 22)
  • Identity File - Private key path
  • ProxyJump - Jump server for connection tunneling
  • ProxyCommand - Jump command for connection tunneling
  • SSH Options - Additional SSH options in -o format (e.g., -o Compression=yes -o ServerAliveInterval=60)
  • Tags - Comma-separated tags for organization

Port Forwarding

SSHM provides an intuitive interface for setting up SSH port forwarding. Press f while selecting a host to open the port forwarding setup:

Forward Types:

  • Local (-L) - Forward a local port to a remote host/port through the SSH connection

    • Example: Access a remote database on localhost:5432 via local port 15432
    • Use case: ssh -L 15432:localhost:5432 server → Database accessible on localhost:15432
  • Remote (-R) - Forward a remote port back to a local host/port

    • Example: Expose local web server on remote host's port 8080
    • Use case: ssh -R 8080:localhost:3000 server → Local app accessible from remote host's port 8080
    • ⚠️ Requirements for external access:
      • SSH Server Config: Add GatewayPorts yes to /etc/ssh/sshd_config and restart SSH service
      • Firewall: Open the remote port in the server's firewall (ufw allow 8080 or equivalent)
      • Port Availability: Ensure the remote port is not already in use
      • Bind Address: Use 0.0.0.0 for external access, 127.0.0.1 for local-only
  • Dynamic (-D) - Create a SOCKS proxy for secure browsing

    • Example: Route web traffic through the SSH connection
    • Use case: ssh -D 1080 server → Configure browser to use localhost:1080 as SOCKS proxy
    • ⚠️ Configuration requirements:
      • Browser Setup: Configure SOCKS v5 proxy in browser settings
      • DNS: Enable "Proxy DNS when using SOCKS v5" for full privacy
      • Applications: Only SOCKS-aware applications will use the proxy
      • Bind Address: Use 127.0.0.1 for security (local access only)

Port Forwarding Interface:

  • Choose forward type with ←/→ arrow keys
  • Configure ports and addresses with guided forms
  • Optional bind address configuration (defaults to 127.0.0.1)
  • Real-time validation of port numbers and addresses
  • Port forwarding history - Save frequently used configurations for quick reuse
  • Connect automatically with configured forwarding options

Troubleshooting Port Forwarding:

Remote Forwarding Issues:

# Error: "remote port forwarding failed for listen port X"
# Solutions:
1. Check if port is already in use: ssh server "netstat -tln | grep :X"
2. Use a different port that's available
3. Enable GatewayPorts in SSH config for external access

SSH Server Configuration for Remote Forwarding:

# Edit SSH daemon config on the server:
sudo nano /etc/ssh/sshd_config

# Add or uncomment:
GatewayPorts yes

# Restart SSH service:
sudo systemctl restart sshd  # Ubuntu/Debian/CentOS 7+
# OR
sudo service ssh restart     # Older systems

Firewall Configuration:

# Ubuntu/Debian (UFW):
sudo ufw allow [port_number]

# CentOS/RHEL/Rocky (firewalld):
sudo firewall-cmd --add-port=[port_number]/tcp --permanent
sudo firewall-cmd --reload

# Check if port is accessible:
telnet [server_ip] [port_number]

Dynamic Forwarding (SOCKS) Browser Setup:

Firefox: about:preferences → Network Settings
- Manual proxy configuration
- SOCKS Host: localhost, Port: [your_port]
- SOCKS v5: ✓
- Proxy DNS when using SOCKS v5: ✓

Chrome: Launch with proxy
chrome --proxy-server="socks5://localhost:[your_port]"

CLI Usage

SSHM provides both command-line operations and an interactive TUI interface:

# Launch interactive TUI mode for browsing and connecting to hosts
sshm

# Connect directly to a specific host (with history tracking)
sshm my-server

# Execute a command on a remote host
sshm my-server uptime

# Execute command with arguments
sshm my-server ls -la /var/log

# Force TTY allocation for interactive commands
sshm -t my-server sudo systemctl restart nginx

# Launch TUI with custom SSH config file
sshm -c /path/to/custom/ssh_config

# Connect directly with custom SSH config file
sshm my-server -c /path/to/custom/ssh_config

# Add a new host using interactive form
sshm add

# Add a new host with pre-filled hostname
sshm add hostname

# Add a new host with custom SSH config file
sshm add hostname -c /path/to/custom/ssh_config

# Edit an existing host configuration
sshm edit my-server

# Edit host with custom SSH config file
sshm edit my-server -c /path/to/custom/ssh_config

# Move a host to another SSH config file (requires Include directives)
sshm move my-server

# Move host with custom SSH config file (requires Include directives)
sshm move my-server -c /path/to/custom/ssh_config

# Search for hosts (interactive filter)
sshm search

# Print machine-readable info (JSON) for scripting
sshm info prod-server
sshm info prod-server --pretty

# With a custom SSH config file
sshm -c /path/to/custom/ssh_config info prod-server

# Pipe to jq
sshm info prod-server | jq -r '.result.target.hostname'
sshm info prod-server | jq -r '.result.target.user'

# Show version information
sshm --version

# Disable automatic update check (useful on air-gapped machines)
sshm --no-update-check

# Show help and available commands
sshm --help

Host Info (JSON)

sshm info <hostname> prints a single JSON object to stdout so you can script against it with jq.

# Extract fields
sshm info prod-server | jq -r '.result.target.hostname'
sshm info prod-server | jq -r '.result.target.port'

# Check not-found (exit code 2)
sshm info does-not-exist | jq -r '.error.code'

Shell Completion

SSHM supports shell completion for host names, making it easy to connect to hosts without typing full names:

sshm <TAB>           # Lists all available hosts
sshm pro<TAB>        # Completes to hosts starting with "pro" (e.g., prod-server)

Setup Instructions:

Bash:

# Enable for current session
source <(sshm completion bash)

# Enable permanently (add to ~/.bashrc)
echo 'source <(sshm completion bash)' >> ~/.bashrc

Zsh:

# Enable for current session
source <(sshm completion zsh)

# Enable permanently (add to ~/.zshrc)
echo 'source <(sshm completion zsh)' >> ~/.zshrc

Fish:

# Enable for current session
sshm completion fish | source

# Enable permanently
sshm completion fish > ~/.config/fish/completions/sshm.fish

PowerShell:

# Enable for current session
sshm completion powershell | Out-String | Invoke-Expression

# Enable permanently (add to your PowerShell profile)
Add-Content $PROFILE 'sshm completion powershell | Out-String | Invoke-Expression'

Direct Host Connection

SSHM supports direct connection to hosts via the command line, making it easy to integrate into your existing workflow:

# Connect directly to any configured host
sshm production-server
sshm db-staging
sshm web-01

# All direct connections are tracked in your history
# Use the TUI to see your most recently connected hosts

Features of Direct Connection:

  • Instant connection - No TUI navigation required
  • History tracking - All connections are recorded with timestamps
  • Error handling - Clear messages if host doesn't exist or configuration issues
  • Config file support - Works with custom config files using -c flag

Remote Command Execution

Execute commands on remote hosts without opening an interactive shell:

# Execute a single command
sshm prod-server uptime

# Execute command with arguments
sshm prod-server ls -la /var/log

# Check disk usage
sshm prod-server df -h

# View logs (pipe to local commands)
sshm prod-server 'cat /var/log/nginx/access.log' | grep 404

# Force TTY allocation for interactive commands (sudo, vim, etc.)
sshm -t prod-server sudo systemctl restart nginx

Features:

  • Exit code propagation - Remote command exit codes are passed through
  • TTY support - Use -t flag for commands requiring terminal interaction
  • Pipe-friendly - Output can be piped to local commands for processing
  • History tracking - Command executions are recorded in connection history

Backup Configuration

SSHM automatically creates backups of your SSH configuration files before making any changes to ensure your configurations are safe.

Backup Location:

  • Unix/Linux/macOS: ~/.config/sshm/backups/ (or $XDG_CONFIG_HOME/sshm/backups/ if set)
  • Windows: %APPDATA%\sshm\backups\ (fallback: %USERPROFILE%\.config\sshm\backups\)

Key Features:

  • Automatic backup before any modification
  • One backup per file (overwrites previous backup)
  • Stored separately to avoid SSH Include conflicts
  • Easy manual recovery if needed

Additional Storage:

  • Connection History: Stored in the same config directory for persistent tracking
  • Port Forwarding History: Saved configurations for quick reuse of common forwarding setups

Quick Recovery:

# Unix/Linux/macOS
cp ~/.config/sshm/backups/config.backup ~/.ssh/config

# Windows
copy "%APPDATA%\sshm\backups\config.backup" "%USERPROFILE%\.ssh\config"

Configuration File Options

By default, SSHM uses the standard SSH configuration file at ~/.ssh/config. You can specify a different configuration file using the -c flag:

# Use custom config file in TUI mode
sshm -c /path/to/custom/ssh_config

# Use custom config file with commands
sshm add hostname -c /path/to/custom/ssh_config
sshm edit hostname -c /path/to/custom/ssh_config
sshm move hostname -c /path/to/custom/ssh_config

Advanced Features

Host Movement Between Config Files

SSHM provides a powerful move command to relocate SSH hosts between different configuration files. This feature requires SSH Include directives to be present in your SSH configuration.

# Move a host to another config file (requires Include directives)
sshm move my-server

# Move with custom config file (requires Include directives)
sshm move my-server -c /path/to/custom/ssh_config

⚠️ Important Requirements:

  • SSH Include directives must be present in your SSH config file (either ~/.ssh/config or the file specified with -c)
  • The config file must contain Include statements referencing other SSH configuration files
  • Without Include directives, the move command will display an error message

Features:

  • Interactive file selector - Choose destination config file from Include directives
  • Include support - Works seamlessly with SSH Include directives structure
  • Atomic operations - Safe host movement with automatic backups
  • Validation - Prevents conflicts and ensures configuration integrity
  • Error handling - Clear messages when Include files are needed but not found

Use Cases:

  • Reorganize hosts from main config to specialized include files
  • Move development hosts to separate environment-specific configs
  • Consolidate configurations for better organization

Example Setup Required: Your main SSH config file must contain Include directives like:

# ~/.ssh/config
Include ~/.ssh/config.d/*
Include work-servers.conf
Include projects/*.conf

Host personal-server
    HostName personal.example.com
    User myuser

Real-time Connectivity Status

SSHM features asynchronous SSH connectivity checking that provides visual indicators of host availability:

Status Indicators:

  • 🟢 Online - SSH connection successful (shows response time)
  • 🟡 Connecting - Currently testing connectivity
  • 🔴 Offline - SSH connection failed or host unreachable
  • ⚫ Unknown - Status not yet determined

Features:

  • Non-blocking checks - Status updates happen in the background
  • Response time tracking - See connection latency for online hosts
  • Automatic refresh - Status indicators update continuously
  • Error details - Detailed error information for failed connections

Automatic Update Checking

SSHM includes built-in version checking that notifies you of available updates:

Features:

  • Background checking - Version check happens asynchronously, never blocking startup
  • Release notifications - Clear indicators when updates are available
  • Pre-release detection - Identifies beta and development versions
  • GitHub integration - Direct links to release pages
  • Non-intrusive - Updates don't interrupt your workflow
  • Configurable - Can be disabled for air-gapped or offline environments

Update notifications appear:

  • In the main TUI interface as a subtle notification
  • Only when a newer stable version is available

Disabling update checks:

Via the CLI flag (one-time):

sshm --no-update-check

Via ~/.config/sshm/config.json (persistent):

{
  "check_for_updates": false
}

Port Forwarding History

SSHM remembers your port forwarding configurations for easy reuse:

Features:

  • Automatic saving - Successful forwarding setups are saved automatically
  • Quick reuse - Previously used configurations appear as suggestions
  • Per-host history - Forwarding history is tracked per SSH host
  • All forward types - Supports Local (-L), Remote (-R), and Dynamic (-D) forwarding history
  • Persistent storage - History survives application restarts

Platform-Specific Notes

Windows:

  • SSHM works with the built-in OpenSSH client (Windows 10/11)
  • Configuration file location: %USERPROFILE%\.ssh\config
  • Compatible with WSL SSH configurations
  • Supports the same SSH options as Unix systems

Unix/Linux/macOS:

  • Standard SSH configuration file: ~/.ssh/config
  • Full compatibility with OpenSSH features
  • Preserves file permissions automatically

🏗️ Configuration

SSHM works directly with your standard SSH configuration file (~/.ssh/config). It adds special comment tags for enhanced functionality while maintaining full compatibility with standard SSH tools.

SSH Include Support

SSHM fully supports SSH Include directives, allowing you to organize your SSH configurations across multiple files. This is particularly useful for managing large numbers of hosts or organizing configurations by environment, project, or team.

Include Examples:

# Main ~/.ssh/config file
Host personal-server
    HostName personal.example.com
    User myuser

# Include work-related configurations
Include work-servers.conf

# Include all configurations from a directory
Include projects/*

# Include with relative paths
Include ~/.ssh/configs/production.conf

Organization Examples:

work-servers.conf:

# Tags: work, production
Host prod-web-01
    HostName 10.0.1.10
    User deploy
    ProxyJump bastion.company.com

# Tags: work, staging  
Host staging-api
    HostName staging-api.company.com
    User developer

projects/client-alpha.conf:

# Tags: client, development
Host client-alpha-dev
    HostName dev.client-alpha.com
    User admin
    Port 2222

Example configuration: Include ~/.ssh/conf.d/*

# Tags: production, web, frontend
Host web-prod-01
    HostName 192.168.1.10
    User deploy
    Port 22
    IdentityFile ~/.ssh/production_key
    Compression yes
    ServerAliveInterval 60

# Tags: development, database
Host db-dev
    HostName dev-db.company.com
    User admin
    Port 2222
    IdentityFile ~/.ssh/dev_key
    StrictHostKeyChecking no
    UserKnownHostsFile /dev/null

# Tags: production, backend
Host backend-prod
    HostName 10.0.1.50
    User app
    Port 22
    ProxyJump bastion.company.com
    ProxyCommand ssh -W %h:%p Jumphost
    IdentityFile ~/.ssh/production_key
    Compression yes
    ServerAliveInterval 300
    BatchMode yes

Supported SSH Options

SSHM supports all standard SSH configuration options:

Built-in Fields:

  • HostName - Server hostname or IP address
  • User - Username for SSH connection
  • Port - SSH port number
  • IdentityFile - Path to private key file
  • ProxyJump - Jump server for connection tunneling (e.g., user@jumphost:port)
  • ProxyCommand - Jump command for connection tunneling (e.g, ssh -W %h:%p Jumphost)
  • Tags - Custom tags (SSHM extension); the special tag hidden hides the host from the TUI and sshm search while keeping it connectable via sshm <host>

Additional SSH Options: You can add any valid SSH option using the "SSH Options" field in the interactive forms. Enter them in command-line format (e.g., -o Compression=yes -o ServerAliveInterval=60) and SSHM will automatically convert them to the proper SSH config format.

Common SSH Options:

  • Compression - Enable/disable compression (yes/no)
  • ServerAliveInterval - Interval in seconds for keepalive messages
  • ServerAliveCountMax - Maximum number of keepalive messages
  • StrictHostKeyChecking - Host key verification (yes/no/ask)
  • UserKnownHostsFile - Path to known hosts file
  • BatchMode - Disable interactive prompts (yes/no)
  • ConnectTimeout - Connection timeout in seconds
  • ControlMaster - Connection multiplexing (yes/no/auto)
  • ControlPath - Path for control socket
  • ControlPersist - Keep connection alive duration
  • ForwardAgent - Forward SSH agent (yes/no)
  • LocalForward - Local port forwarding (e.g., 8080:localhost:80)
  • RemoteForward - Remote port forwarding
  • DynamicForward - SOCKS proxy port forwarding

Example usage in forms:

SSH Options: -o Compression=yes -o ServerAliveInterval=60 -o StrictHostKeyChecking=no

This will be automatically converted to:

    Compression yes
    ServerAliveInterval 60
    StrictHostKeyChecking no

Application Configuration

SSHM supports a configuration file to customize its behavior, including key bindings and update checking.

Configuration File Location:

  • Linux/macOS: ~/.config/sshm/config.json
  • Windows: %APPDATA%\sshm\config.json

Example Configuration:

{
  "check_for_updates": false,
  "key_bindings": {
    "quit_keys": ["q", "ctrl+c"],
    "disable_esc_quit": true
  }
}

Available Options:

  • check_for_updates: Boolean to enable or disable the automatic update check at startup. Default: true. Set to false on air-gapped or offline machines to avoid connection delays.
  • quit_keys: Array of keys that will quit the application. Default: ["q", "ctrl+c"]
  • disable_esc_quit: Boolean flag to disable ESC key from quitting the application. Default: false

For Vim Users: If you frequently press ESC accidentally causing the application to quit, set disable_esc_quit to true. This will disable ESC as a quit key while preserving all other functionality.

For Air-gapped Machines: If SSHM is slow to start due to DNS timeouts when reaching GitHub, set check_for_updates to false. You can also use the --no-update-check CLI flag for a one-time override without editing the config file.

Default Configuration: If no configuration file exists, SSHM will automatically create one with default settings that maintain backward compatibility.

🛠️ Development

Prerequisites

  • Go 1.23+
  • Git

Build from Source

# Clone the repository
git clone https://github.com/Gu1llaum-3/sshm.git
cd sshm

# Build the binary
go build -o sshm .

# Run
./sshm

Project Structure

sshm/
├── main.go             # Application entry point
├── cmd/                # CLI commands (Cobra)
│   ├── root.go         # Root command and interactive mode
│   ├── add.go          # Add host command
│   ├── edit.go         # Edit host command
│   ├── move.go         # Move host command
│   └── search.go       # Search command
├── internal/
│   ├── config/         # SSH configuration management
│   │   └── ssh.go      # Config parsing and manipulation
│   ├── connectivity/   # SSH connectivity checking
│   │   └── ping.go     # Asynchronous SSH ping functionality
│   ├── history/        # Connection history tracking
│   │   ├── history.go  # History management and last login tracking
│   │   └── port_forward_test.go # Port forwarding history tests
│   ├── version/        # Version checking and updates
│   │   ├── version.go  # GitHub release checking and version comparison
│   │   └── version_test.go # Version parsing and comparison tests
│   ├── ui/             # Terminal UI components (Bubble Tea)
│   │   ├── tui.go      # Main TUI interface and program setup
│   │   ├── model.go    # Core TUI model and state
│   │   ├── update.go   # Message handling and state updates
│   │   ├── view.go     # UI rendering and layout
│   │   ├── table.go    # Host list table component with status indicators
│   │   ├── add_form.go # Add host form interface
│   │   ├── edit_form.go# Edit host form interface
│   │   ├── move_form.go# Move host form interface
│   │   ├── port_forward_form.go # Port forwarding setup with history
│   │   ├── styles.go   # Lip Gloss styling definitions
│   │   ├── sort.go     # Sorting and filtering logic
│   │   └── utils.go    # UI utility functions
│   └── validation/     # Input validation
│       └── ssh.go      # SSH config validation
├── images/             # Documentation assets
│   ├── logo.png        # Project logo
│   └── sshm.gif        # Demo animation
├── install/            # Installation scripts
│   ├── unix.sh         # Unix/Linux/macOS installer
│   └── README.md       # Installation guide
├── .github/            # GitHub configuration
│   ├── copilot-instructions.md # Development guidelines
│   └── workflows/      # CI/CD pipelines
│       └── build.yml   # Multi-platform builds
├── go.mod              # Go module definition
├── go.sum              # Go module checksums
├── LICENSE             # MIT license
└── README.md           # Project documentation

Dependencies

📦 Releases

Automated releases are built for multiple platforms:

Platform Architecture Download
Linux AMD64 sshm-linux-amd64.tar.gz
Linux ARM64 sshm-linux-arm64.tar.gz
macOS Intel sshm-darwin-amd64.tar.gz
macOS Apple Silicon sshm-darwin-arm64.tar.gz
Windows AMD64 sshm-windows-amd64.zip
Windows ARM64 sshm-windows-arm64.zip

🤝 Contributing

Contributions are welcome! Please feel free to submit a Pull Request. For major changes, please open an issue first to discuss what you would like to change.

Development Workflow

  1. Fork the repository
  2. Create a feature branch (git checkout -b feature/amazing-feature)
  3. Commit your changes (git commit -m 'Add amazing feature')
  4. Push to the branch (git push origin feature/amazing-feature)
  5. Open a Pull Request

📝 License

This project is licensed under the MIT License - see the LICENSE file for details.

🙏 Acknowledgments

  • Charm for the amazing TUI libraries
  • Cobra for the excellent CLI framework
  • @yimeng for contributing SSH Include directive support
  • @ldreux for contributing multi-word search functionality
  • @qingfengzxr for contributing custom key bindings support
  • The Go community for building such fantastic tools

from   https://github.com/Gu1llaum-3/sshm