64 lines
1.9 KiB
JavaScript
Raw Normal View History

/*
* 内部本机 socket 接口 · 给神笔马良 (zhuyuan-pen) 拉密钥用.
* 只允许 127.0.0.1 / ::1 (IPv6 loopback) 来源, 其他一律 403.
* 不需要 basic-auth (本机进程权限 = vault 用户已经够了).
*
* GET /internal/fetch/:key { value }
*
* baton-005 C : 神笔马良不走环境变量, 走本地 socket. 这里其实是 loopback HTTP,
* unix domain socket 简单 (2C2G 上少一份 socket 文件管理负担), 实质等价.
*/
"use strict";
const express = require("express");
const { KEY_RE } = require("./secrets");
function buildInternalRouter({ vault }) {
const router = express.Router();
// 只允许本机
router.use((req, res, next) => {
const ip = req.ip || (req.socket && req.socket.remoteAddress) || "";
// express 的 req.ip: 如果 trust proxy 没开, 是 socket.remoteAddress
// 我们 server.js 不开 trust proxy, 所以 req.ip 直接是 socket 地址.
if (
ip === "127.0.0.1" ||
ip === "::1" ||
ip === "::ffff:127.0.0.1" ||
ip === "localhost"
) {
return next();
}
return res
.status(403)
.json({ error: true, code: "forbidden_non_local", message: "只允许本机访问 (来源=" + ip + ")" });
});
router.get("/fetch/:key", (req, res) => {
const key = String(req.params.key || "");
if (!KEY_RE.test(key)) {
return res
.status(400)
.json({ error: true, code: "invalid_key", message: "密钥名格式不对: " + key });
}
let val;
try {
val = vault.get(key);
} catch (e) {
return res
.status(500)
.json({ error: true, code: "vault_decrypt_failed", message: e.message });
}
if (val == null) {
return res
.status(404)
.json({ error: true, code: "not_configured", message: "密钥未配置: " + key });
}
res.json({ ok: true, key, value: val });
});
return router;
}
module.exports = { buildInternalRouter };