Ubuntu 22.04 安装 OTS 指南
Ubuntu 22.04 从零安装 OTS v1.23.1:官方二进制下载与 SHA256 校验、systemd 托管、防火墙放行、离线与源码编译备选方案及 nginx 反向代理,全程实测回显。
Ubuntu 22.04 安装 OTS 指南
目标:在 Ubuntu 22.04 服务器上从零安装一次性秘密分享平台 OTS(Luzifer/ots)v1.23.1,完成官方二进制下载与校验、运行账号创建、systemd 服务托管、防火墙放行与 Web/API 可用性验证,并给出离线安装、源码编译两种备选方案与 nginx 反向代理配置。全部命令与回显已在 Ubuntu 22.04(systemd 完整环境)上逐条实测通过。
一、环境信息
本文实测环境与关键信息如下:
- 操作系统:Ubuntu 22.04(systemd 正常运行,
systemctl is-system-running返回running) - 架构:x86_64(amd64)
- OTS 版本:v1.23.1(官方最新稳定版,GitHub Releases 2026-10-23 发布)
- 服务监听:
0.0.0.0:8146(本文自定义端口;OTS 默认端口为 3000) - 安装路径:二进制
/usr/local/bin/ots,定制配置/etc/ots/customize.yaml - 存储后端:
mem(默认,进程内存);生产环境建议 Redis(见第八章) - 下载渠道:GitHub Releases 官方源直连实测可用(下载耗时秒级,无需代理加速)
- 关联阅读:本专栏 《OTS 介绍与选型指南》、《OTS 1.23.1 配置参数详解》、《OTS 1.23.1 常用 CLI 命令与 API 详解》
二、前置条件
安装前确认以下条件满足:
- 一台 Ubuntu 22.04 服务器:具备 sudo/root 权限,systemd 可用(OTS 以 systemd 服务方式托管);
- 基础工具:
curl、tar(在线方案用)、git与 Go 工具链(仅源码编译方案用),缺失时安装:
apt-get update && apt-get install -y curl wget tar
实测回显(已安装时显示如下,输出节选):
Reading package lists... Done
Building dependency tree... Done
0 upgraded, 0 newly installed, 0 to remove and 0 not upgraded.
- 端口规划:本文使用
8146,确认未被占用:
ss -ltn | grep 8146 || echo "port 8146 free"
实测回显:
port 8146 free
- 网络连通性:服务器需能访问
github.com下载发行包(离线方案 B 除外)。
三、版本选型说明
OTS 在 GitHub Releases 同时发行多平台产物,v1.23.1 的官方 SHA256SUMS 清单(实测拉取)包含如下 Linux 产物:
| 文件名 | 适用场景 | 大小(实测) |
|---|---|---|
ots_linux_amd64.tgz | x86_64 Linux 服务器(本文所用) | 7,506,758 字节 |
ots_linux_arm64.tgz | ARM64 服务器/树莓派 4+ | — |
ots_linux_arm.tgz | 32 位 ARM 设备 | — |
ots-cli_linux_amd64.tgz | 配套 CLI 客户端 ots-cli(脚本化创建/读取秘密) | 3,138,784 字节 |
关键差异:ots 是服务端(内嵌 Web 前端的单二进制),ots-cli 是命令行客户端(本地加密后调用服务端 API),两者独立下载、可装在同一台或不同机器。版本选择上,OTS 更新节奏较快(2026-09 至 2026-10 连发 v1.22.0 / v1.23.0 / v1.23.1),建议直接选 Releases 最新稳定版并保持小步升级;所有版本见官方 Releases 页。
四、在线安装方案(A:官方 Releases 二进制 + systemd,推荐)
4.1 方案说明
这是官方主推方式:从 GitHub Releases 下载对应架构的发行包,校验 SHA256 后解压出单文件二进制 ots,装入 /usr/local/bin,创建专用系统账号,再交由 systemd 托管。优点是零依赖(Go 静态编译、前端已内嵌)、升级即替换二进制。全程约 5 分钟。
4.2 下载与校验
进入工作目录,下载发行包与官方校验清单:
mkdir -p /opt/ots-install && cd /opt/ots-install
curl -fsSLo ots_linux_amd64.tgz https://github.com/Luzifer/ots/releases/download/v1.23.1/ots_linux_amd64.tgz
curl -fsSLo SHA256SUMS https://github.com/Luzifer/ots/releases/download/v1.23.1/SHA256SUMS
确认文件落盘:
ls -l
实测回显:
total 7336
-rw-r--r-- 1 root root 1060 Oct 29 17:49 SHA256SUMS
-rw-r--r-- 1 root root 7506758 Oct 29 17:49 ots_linux_amd64.tgz
从官方清单中取出本架构的期望校验值:
grep "linux_amd64.tgz" SHA256SUMS
实测回显:
5e211e9e7770c83ff959e9e2036bf3d73c9444a52b67195aaddeb38e2d1241af ots-cli_linux_amd64.tgz
d94792b7374f10236efe159365050d0d5fee3e9083620393fdcca2cef5f1acac ots_linux_amd64.tgz
本地计算并与官方值比对(必须完全一致才继续):
sha256sum ots_linux_amd64.tgz
实测回显:
d94792b7374f10236efe159365050d0d5fee3e9083620393fdcca2cef5f1acac ots_linux_amd64.tgz
⚠️ 注意:本地值
d94792b7...1acac与官方清单一致,校验通过。若不一致说明下载损坏或来源被篡改,必须删除重下,禁止带病安装。
4.3 解压并安装二进制
发行包内就是单个 ots 二进制:
tar xzf ots_linux_amd64.tgz
ls -l ots
实测回显:
-rwxr-xr-x 1 root root 21659808 Oct 23 12:36 ots
约 21.7 MB 的静态二进制,装入系统路径并验证版本:
install -m 0755 ots /usr/local/bin/ots
ots --version
实测回显:
time="2026-10-29T17:49:40Z" level=info msg=ots version=v1.23.1
⚠️ 注意:
ots --version的版本信息走日志通道(logrus),因此输出带time=/level=前缀且写入 stderr,这是官方既定行为,不是报错。
4.4 创建专用运行账号
为服务创建无家目录、不可登录的系统账号:
useradd --system --no-create-home --shell /usr/sbin/nologin ots
id ots
实测回显:
uid=999(ots) gid=999(ots) groups=999(ots)
4.5 编写定制配置文件
OTS 的 Web 前端行为(标题、有效期选项、附件上限等)通过 YAML 定制文件控制(完整参数见 《OTS 1.23.1 配置参数详解》,此处用一份可用的最小定制):
mkdir -p /etc/ots
cat > /etc/ots/customize.yaml << 'EOF'
appTitle: "Corp Secrets - One Time Secrets"
disableExpiryOverride: false
expiryChoices: [300, 3600, 86400, 604800]
maxAttachmentSizeTotal: 10485760
metricsAllowedSubnets:
- 127.0.0.1/32
EOF
逐项说明:appTitle 覆盖页面标题与浏览器标签;expiryChoices 是前端有效期下拉框的可选项(秒值,顺序即展示顺序);maxAttachmentSizeTotal 限制附件总大小 10 MiB(0 为默认 64 MiB);metricsAllowedSubnets 允许本机抓取 Prometheus 指标(默认空列表=全拒绝)。
4.6 编写 systemd 服务单元
创建 /etc/systemd/system/ots.service:
cat > /etc/systemd/system/ots.service << 'EOF'
[Unit]
Description=OTS - One-Time-Secret sharing platform
After=network-online.target
Wants=network-online.target
[Service]
Type=simple
User=ots
Group=ots
ExecStart=/usr/local/bin/ots --listen=0.0.0.0:8146 --storage-type=mem --secret-expiry=604800 --log-level=info --customize=/etc/ots/customize.yaml
Environment=SECRET_EXPIRY=604800
Restart=on-failure
RestartSec=5
NoNewPrivileges=true
ProtectSystem=strict
ProtectHome=true
PrivateTmp=true
[Install]
WantedBy=multi-user.target
EOF
逐项说明:--listen=0.0.0.0:8146 指定监听地址与端口;--storage-type=mem 使用内存存储(重启即清空,生产切 Redis,见第八章);--secret-expiry=604800 设定秘密最长存活 7 天(0 表示永不过期),同时通过 Environment=SECRET_EXPIRY=604800 注入同名环境变量(Redis 后端读取的是环境变量形态,启动参数与环境变量等价);Restart=on-failure 异常退出自动拉起;末尾四行为基础加固(禁提权、只读系统目录、独立 /tmp)。
4.7 启动服务并设置开机自启
systemctl daemon-reload
systemctl enable ots
实测回显:
Created symlink /etc/systemd/system/multi-user.target.wants/ots.service → /etc/systemd/system/ots.service.
systemctl start ots
systemctl status ots --no-pager -l
实测回显(输出节选):
* ots.service - OTS - One-Time-Secret sharing platform
Loaded: loaded (/etc/systemd/system/ots.service; enabled; vendor preset: enabled)
Active: active (running) since Thu 2026-10-29 17:36:07 UTC; 2s ago
Main PID: 2777 (ots)
Tasks: 5 (limit: 2266)
Memory: 2.0M
CPU: 7ms
`-2777 /usr/local/bin/ots --listen=0.0.0.0:8146 --storage-type=mem --secret-expiry=604800 --log-level=info --customize=/etc/ots/customize.yaml
Oct 29 17:36:07 ots-server systemd[1]: Started OTS - One-Time-Secret sharing platform.
Oct 29 17:36:07 ots-server ots[2777]: time="2026-10-29T17:36:07Z" level=info msg="ots started" secret_expiry=168h0m0s version=v1.23.1
Oct 29 17:36:07 ots-server ots[2777]: time="2026-10-29T17:36:07Z" level=info msg="Starting HTTP server on 0.0.0.0:8146"
日志中 secret_expiry=168h0m0s(即 604800 秒)与 version=v1.23.1 确认启动参数生效;进程常驻内存仅 2 MB 量级。
4.8 验证监听与可用性
确认端口监听:
ss -ltnp | grep 8146
实测回显:
LISTEN 0 4096 *:8146 *:* users:(("ots",pid=3546,fd=3))
验证健康检查与 Web 首页(定制标题已生效):
curl -s -o /dev/null -w 'healthz=%{http_code}\n' http://127.0.0.1:8146/api/healthz
curl -s http://127.0.0.1:8146/ | grep -oE '<title>[^<]*</title>'
实测回显:
healthz=200
<title>Corp Secrets - One Time Secrets</title>
healthz=200 且首页 <title> 与 customize.yaml 中的 appTitle 一致,服务完全可用。浏览器打开 http://<服务器IP>:8146/ 即可进入分享页创建秘密。
4.9 防火墙放行
启用 ufw 的服务器放行 8146 端口:
ufw allow 8146/tcp comment "ots web"
实测回显:
Rules updated
Rules updated (v6)
⚠️ 注意:若服务器未启用 ufw(
ufw status显示inactive,本文实测环境即如此),此步可跳过,改由云安全组/iptables 控制。OTS 无内置认证,生产环境务必用防火墙或反向代理限制来源,避免暴露到公网任人创建匿名秘密。
五、离线安装方案(B:断网服务器)
5.1 方案说明
适用于服务器无法访问外网的环境:在一台有网的运维机上完成下载与校验,再把文件传输到目标服务器安装。产物与方案 A 完全相同。
5.2 运维机:下载与校验
在有网机器上执行与 4.2 节相同的两条 curl 下载与 sha256sum 校验(回显同上,校验值必须与官方 SHA256SUMS 一致)。
5.3 传输到目标服务器
用 scp 把发行包与校验清单传到目标服务器(示例内网地址 192.168.1.10,按实际替换):
scp ots_linux_amd64.tgz SHA256SUMS root@192.168.1.10:/opt/ots-install/
预期交互(输入服务器密码后,进度条为实测回显):
ots_linux_amd64.tgz 100% 7331KB 152.3MB/s 00:00
SHA256SUMS 100% 1060 6.2MB/s 00:00
5.4 目标服务器:安装
在目标服务器上先复验校验值(防传输损坏),随后步骤与方案 A 的 4.3–4.9 节完全一致(解压 → 安装 → 建账号 → 配置 → systemd → 验证):
cd /opt/ots-install
sha256sum ots_linux_amd64.tgz
实测回显(与官方清单一致):
d94792b7374f10236efe159365050d0d5fee3e9083620393fdcca2cef5f1acac ots_linux_amd64.tgz
六、源码编译安装方案(C:自定义构建)
6.1 方案说明
适合需要修改源码或无法使用预编译产物的场景。OTS 服务端是标准 Go 工程(go.mod 声明 go 1.26.0),但完整构建需要先编译前端(Vue3 产物通过 //go:embed 打进二进制,官方 Makefile 的 build-local 目标先执行 frontend 前端构建再 go build,依赖 Node 22+ 与 pnpm)。本节实测「直接 go build」的产物与差异,并给出真实排障过程。
6.2 安装 Go 工具链与源码
apt-get install -y git
curl -fsSLo /opt/go.tgz https://go.dev/dl/go1.26.0.linux-amd64.tar.gz
tar xzf /opt/go.tgz -C /opt && /opt/go/bin/go version
实测回显:
go version go1.26.0 linux/amd64
git clone --depth 1 --branch v1.23.1 https://github.com/Luzifer/ots.git /opt/ots-src
6.3 编译
直接编译会先尝试按 go.mod 的 toolchain 声明拉取 go1.27.3 工具链,若网络到 proxy.golang.org 不通会失败(真实报错如下):
export PATH=/opt/go/bin:$PATH && cd /opt/ots-src
go build -o /opt/ots-built .
实测报错回显:
go: downloading go1.27.3 (linux/amd64)
go: download go1.27.3: golang.org/toolchain@v0.0.1-go1.27.3.linux-amd64: Get "https://proxy.golang.org/golang.org/toolchain/@v/v0.0.1-go1.27.3.linux-amd64.zip": dial tcp 142.250.73.81:443: i/o timeout
定位:proxy.golang.org(Google 系域名)在当前网络不可达。解决:模块代理换国内公共代理 goproxy.cn,工具链固定用本地 go1.26.0(GOTOOLCHAIN=local):
export GOPROXY=https://goproxy.cn,direct GOTOOLCHAIN=local
go build -o /opt/ots-built .
实测回显(输出节选,模块经 goproxy.cn 下载后编译成功):
go: downloading google.golang.org/protobuf v1.36.12
go: downloading github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822
go: downloading go.uber.org/atomic v1.12.0
6.4 验证与已知差异
/opt/ots-built --version
实测回显:
time="2026-10-29T17:45:37Z" level=info msg=ots version=dev
⚠️ 注意两个与官方发行包的差异:①
version=dev是因为未注入版本号,正式构建应加-ldflags "-X main.version=v1.23.1"(官方 Makefile 的build-local目标即如此);② 直接go build只内嵌了仓库frontend/目录里的index.html,完整的 app.js/app.css 等前端产物需要先执行官方 Makefile 的frontend目标(pnpm node ci/build.mjs,依赖 Node 22+ 与 pnpm)再编译,否则页面只有骨架无法交互。日常部署请优先选方案 A/B 的官方发行包,本方案仅推荐给需要改代码的场景。
七、方案对比与选择建议
| 维度 | 方案 A:在线二进制 | 方案 B:离线二进制 | 方案 C:源码编译 |
|---|---|---|---|
| 版本 | 最新稳定版 | 最新稳定版(离线包自定) | 任意分支/tag |
| 是否需联网 | 服务器需访问 GitHub | 仅运维机需联网 | 全程联网(Go 模块+前端依赖) |
| 安装方式 | curl + tar + systemd | scp + tar + systemd | go build + systemd |
| systemd 服务名 | 自建 ots.service | 自建 ots.service | 自建 ots.service |
| 监听端口 | 自定(本文 8146) | 自定 | 自定 |
| 产物完整性 | 官方完整前端 | 官方完整前端 | 需自行构建前端,否则页面不可用 |
| 适用场景 | 绝大多数可联网服务器 | 内网隔离生产环境 | 二次开发/安全审计 |
选择建议:可联网选 A;内网隔离选 B;只有要改源码才选 C。三个方案最终都以同一个 systemd 单元形态运行,后续配置与升级路径一致。
八、配置说明
三个方案共用的运行配置集中在两处:systemd 单元的 ExecStart 启动参数与 /etc/ots/customize.yaml 定制文件。改完任一处都需要 systemctl daemon-reload && systemctl restart ots 生效。
8.1 常用启动参数
| 参数 | 默认值 | 说明 |
|---|---|---|
--listen | :3000 | 监听地址与端口 |
--storage-type | mem | 存储后端,mem 或 redis |
--secret-expiry | 0(永不过期) | 秘密最长存活秒数,创建时可被前端选项覆盖但不可超过它 |
--customize | 空 | 定制文件路径 |
--log-level | info | 日志级别(debug/info/warning/error) |
--enable-tls | false | 启用后需同时给 --cert-file 与 --key-file,服务直接以 HTTPS 提供 |
⚠️ 注意:所有启动参数都有同名大写环境变量形态(
LISTEN、STORAGE_TYPE、SECRET_EXPIRY、REDIS_URL等),Redis 后端的连接串只认环境变量REDIS_URL,在 systemd 单元里用Environment=注入。参数逐项深解与生产模板见 《OTS 1.23.1 配置参数详解》。
8.2 nginx 反向代理(可选,推荐生产使用)
前置:apt-get install -y nginx。创建站点配置 /etc/nginx/sites-available/ots:
server {
listen 8080;
server_name ots.example.com;
# 允许较大的加密秘密体(含附件,按 maxAttachmentSizeTotal 放大)
client_max_body_size 128m;
location / {
proxy_pass http://127.0.0.1:8146;
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 -sf /etc/nginx/sites-available/ots /etc/nginx/sites-enabled/ots
rm -f /etc/nginx/sites-enabled/default
nginx -t && systemctl restart nginx
curl -s -o /dev/null -w 'index_via_nginx=%{http_code}\n' http://127.0.0.1:8080/
curl -s -X POST -H 'content-type: application/json' -d '{"secret":"nginx-proxy-test"}' http://127.0.0.1:8080/api/create
实测回显:
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful
index_via_nginx=200
{"success":true,"expires_at":"2026-11-05T17:38:14.752353573Z","secret_id":"46238433-e7df-48cc-a31c-91ecfe0c2a3f"}
经代理的首页与创建接口均正常。生产再叠加 TLS(listen 443 ssl + 证书)即可;若同时要放行 Prometheus 抓取,注意 /metrics 的网段校验看到的是代理地址,需把代理所在网段加进 metricsAllowedSubnets 或只允许 127.0.0.1 并由代理鉴权。
九、常见问题(FAQ)
Q1:curl -I http://127.0.0.1:8146/ 返回 405 是坏了吗?
不是。首页路由只注册了 GET 方法,HEAD 请求得到 HTTP/1.1 405 Method Not Allowed(实测回显):
HTTP/1.1 405 Method Not Allowed
Date: Thu, 29 Oct 2026 17:36:44 GMT
用 curl -s -o /dev/null -D - http://127.0.0.1:8146/(GET)即可看到 HTTP/1.1 200 OK 与 CSP 等安全响应头。
Q2:服务重启后,之前创建的秘密全没了?
--storage-type=mem 是进程内存存储,官方 README 明确「wiped on restart of the daemon」——重启即清空。实测:创建秘密后 systemctl restart ots,再次 GET /api/get/<id> 返回 404({"success":false,"error":"9de91df6-...-98b5-6e3e287f0c52"})。需要重启不丢数据时切换 Redis 后端(见 《OTS 1.23.1 配置参数详解》存储一节)。
Q3:curl http://127.0.0.1:8146/metrics 返回 404?
Prometheus 指标端点默认拒绝一切来源(metricsAllowedSubnets 默认空列表),返回 404 而非 401/403 是官方设计(不暴露端点存在)。在 customize.yaml 加上抓取方网段(如 - 127.0.0.1/32,注意写子网掩码形态)并重启后,同请求返回 200(实测双向验证:拒绝 404 → 放行 200)。
Q4:ots --version / 启动日志里带 time=... level=info 前缀?
OTS 全部日志走 logrus 结构化输出到 stderr,版本查询也不例外(msg=ots version=v1.23.1)。systemd 会原样收进 journal,journalctl -u ots 查看。
Q5:端口 8146 被占用启动失败?
先用 ss -ltnp | grep 8146 找到占用进程(OTS 自身重复启动或历史进程),kill 或换端口(改 ExecStart 的 --listen 后 daemon-reload && restart)。启动失败时 systemctl status ots 会显示 HTTP server quit unexpectedly 的日志行。
十、卸载与清理
完全移除 OTS(按安装时落点逐个删除):
systemctl disable --now ots
rm -f /etc/systemd/system/ots.service /usr/local/bin/ots
rm -rf /etc/ots
userdel ots
systemctl daemon-reload
若装过 ots-cli,删除其二进制 rm -f /usr/local/bin/ots-cli;若有 nginx 站点,rm -f /etc/nginx/sites-enabled/ots /etc/nginx/sites-available/ots && systemctl reload nginx。验证残留:
ss -ltn | grep 8146 || echo "port 8146 free"
实测回显(卸载后):
port 8146 free
十一、参考链接
- 官方仓库与 README:https://github.com/Luzifer/ots
- 官方 Releases(v1.23.1 与 SHA256SUMS):https://github.com/Luzifer/ots/releases/tag/v1.23.1
- 官方 Wiki 定制文档:https://github.com/Luzifer/ots/wiki/Customization
- Go 下载页:https://go.dev/dl/(源码编译方案)
- 本系列文章:《OTS 介绍与选型指南》、《OTS 1.23.1 配置参数详解》、《OTS 1.23.1 常用 CLI 命令与 API 详解》
- 同类组件对照:《OneTimeSecret v0.26.19 介绍与选型指南》、《Debian 12 安装 OneTimeSecret v0.26.19 指南》——同为一次性秘密分享,可对照两者在加密模型与部署形态上的差异