logo
0
0
WeChat Login
cnb<cnb@87878787.xyz>
docs: update README - complete project documentation

SSH Keep-Alive

SSH Keep-Alive

Android SSH 保活客户端

Download APK Android 7.0+ Rust License


简介

一个轻量级 Android SSH 保活工具,用于维持与远程服务器的 SSH 长连接。核心连接逻辑使用 Rust(russh 纯 Rust SSH 实现)通过 JNI 与 Android 应用层交互,提供高性能、安全的 SSH 连接能力。

专为 CNB (cnb.cool) 工作区保活 场景设计:CNB 容器 10 分钟无活动会被自动回收,本应用通过定时心跳命令保持 SSH 会话活跃,防止容器被回收。

功能特性

  • SSH 长连接 — 支持 none 认证(CNB)、密码认证、公钥认证
  • 定时心跳 — 可配置间隔(默认 30 秒)和自定义心跳命令
  • 自动重连 — 连接断开后指数退避自动重连(最多 10 次)
  • 连接诊断 — 内置分步诊断:URL 解析 → DNS → TCP → SSH 握手
  • 前台服务 — Android 后台保活,通知栏常驻状态
  • 日志复制 — 一键复制连接日志,方便排查问题
  • 多架构支持 — ARM64、ARM32、x86_64
  • 图标切换 — 三种图标风格(终端 / 盾牌 / 云)
  • CI/CD 自动构建 — 推送代码自动构建 APK + Docker 镜像并发布

下载安装

CNB Releases 下载最新 APK,直接安装即可。

使用方法

  1. 打开应用,在 SSH URL 输入框填入连接地址
    • CNB 工作区格式:cnb-ak5-xxx.xxx@cnb.space
    • 标准格式:user@hostuser@host:port
  2. 密码留空(CNB 使用 none 认证,无需密码)
  3. 心跳命令保持默认 echo keepalive 即可
  4. 心跳间隔默认 30 秒
  5. 点击 连接 按钮

连接成功后会显示 handle 值和保活状态,通知栏会出现常驻通知。

技术架构

┌─────────────────────────────────────────┐ │ Android App (Kotlin) │ │ MainActivity ←→ SshService ←→ SshNative│ └───────────────────┬─────────────────────┘ │ JNI (com.ssh.keepalive.SshNative) ┌───────────────────▼─────────────────────┐ │ Rust Library (russh) │ │ jni_bridge.rs → connection.rs │ │ → keepalive.rs │ └──────────────────────────────────────────┘

技术栈

层级技术说明
UI 层Kotlin + Material 3Android 界面
服务层Android Foreground Service后台保活
桥接层JNI (jni-rs)Kotlin ↔ Rust 通信
连接层Rust (russh 0.60)纯 Rust SSH2 实现
异步运行时TokioRust async runtime

项目结构

ssh/ ├── .cnb.yml # CI/CD 流水线(APK + Docker) ├── Dockerfile # Docker 镜像构建 ├── app/ │ ├── build.gradle.kts # 应用 Gradle 配置 │ ├── proguard-rules.pro # ProGuard 混淆规则 │ └── src/main/ │ ├── AndroidManifest.xml │ ├── java/com/ssh/keepalive/ │ │ ├── MainActivity.kt # 主界面 │ │ ├── SshService.kt # 前台服务 + 连接管理 │ │ └── SshNative.kt # JNI 声明 │ └── res/layout/ │ └── activity_main.xml # 界面布局 ├── rust/ssh-client/ │ ├── Cargo.toml # Rust 依赖(russh 0.60, jni, tokio) │ └── src/ │ ├── lib.rs # 库入口 │ ├── jni_bridge.rs # JNI 导出函数 │ ├── connection.rs # SSH 连接管理 │ └── keepalive.rs # 心跳保活逻辑 └── scripts/ └── build-android.sh # 本地构建脚本

构建与开发

前置条件

  • Rust (rustup)
  • Android SDK (API 34)
  • Android NDK r26d
  • Java 17
  • cargo-ndk

本地构建

# 完整构建 ./scripts/build-android.sh # 或分步构建 rustup target add aarch64-linux-android armv7-linux-androideabi x86_64-linux-android cargo install cargo-ndk cargo ndk -t arm64-v8a -t armeabi-v7a -t x86_64 build --release ./gradlew assembleRelease

CI/CD(CNB)

推送代码到 main 分支后自动触发:

  1. Pipeline 001 — APK 构建:安装 Rust 工具链 → 交叉编译 → Gradle 签名打包 → 发布 Release
  2. Pipeline 002 — Docker 构建:构建 Docker 镜像 → 推送到 docker.cnb.cool/87878787.xyz/ssh:latest

制品:

  • APK:Releases 页面
  • Docker 镜像:docker.cnb.cool/87878787.xyz/ssh:latest

支持的 SSH 认证方式

方式参数适用场景
none 认证密码留空CNB 工作区
密码认证填入密码常规 SSH 服务器
公钥认证指定密钥路径高安全场景

开发踩坑记录

以下是在开发过程中遇到并解决的关键问题,供参考。

1. ARM64 负数 JNI Handle(最关键)

Box::into_raw() 返回的指针在 ARM64 上高位可能为 1,强转 jlong (i64) 后变成负数。Kotlin 端 handle > 0 判断为 false,导致连接成功却显示失败。

修复:Kotlin 端改为 handle != 0L

2. ProGuard 混淆 JNI 类名

R8/ProGuard 会混淆 com.ssh.keepalive.SshNative 类名,导致 JNI FindClass 失败。

修复:精确添加 -keep class com.ssh.keepalive.SshNative { *; } 规则。

3. UnsatisfiedLinkError 继承 Error 非 Exception

JNI 库加载失败抛出 UnsatisfiedLinkError,继承自 Error 而非 Exceptioncatch (e: Exception) 捕获不到。

修复:改为 catch (e: Throwable)

4. russh 默认 features 含 aws-lc-rs

russh 默认 features 依赖 aws-lc-rs,Android NDK 交叉编译失败(CMake/汇编兼容性问题)。

修复default-features = false, features = ["ring", "flate2", "rsa"],用 ring 替代。

5. Vec<SocketAddr> 不满足 ToSocketAddrs

russh 的 connect_stream 需要 TcpStream,但 Vec<SocketAddr> 不实现 tokio::net::ToSocketAddrs

修复:转为 &[SocketAddr] 切片。

6. Docker apt 源 404

CNB CI runner 是 Debian bookworm,Dockerfile 里用了 Ubuntu 的 apt 源导致 404。

修复:基础镜像改为 debian:bookworm,apt 源自动检测 OS。

许可证

私有项目,未经授权不得使用。