@cliz/inlets
Version:
Cloud Native Tunnel
98 lines (97 loc) • 4.42 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.createTCPMonitor = void 0;
const doreamon_1 = require("@zodash/doreamon");
const hmac = require("@zodash/hmac");
const net = require("net");
const constants_1 = require("../../common/constants");
const debug = require('debug')('inlets:monitor:tcp');
const logger = doreamon_1.default.logger.getLogger('monitor:tcp ');
function createTCPMonitor(ctx, options) {
const emitter = new doreamon_1.default.event.Event();
const server = createTCPServer(options.domain, options.port, (server) => {
}, (socket) => {
}, async (id) => {
const container = ctx.container.get(id);
if (!container) {
return null;
}
return await options.token(container.authType, container.clientId);
}, (containerId, requestId, targetSocket, server) => {
logger.info(`[tunnel:client] connected (container id: ${containerId}, request id: ${requestId})`);
ctx.container.connectRequest(containerId, requestId, targetSocket);
});
return server;
}
exports.createTCPMonitor = createTCPMonitor;
function createTCPServer(domain, port, onServerCreate, onSocketConnect, onGetToken, onAuthenticate) {
const server = net.createServer((socket) => {
let requestId = null;
logger.info(`[tunnel:client][connect][1] ip ${socket.remoteAddress}`);
socket.on('error', error => {
logger.error(`socket error:`, error);
});
socket.on('end', () => {
logger.info(`[tunnel:client][close] request id: ${requestId}, ip ${socket.remoteAddress}`);
});
const s = socket;
socket.on('data', async (data) => {
if (s.isAuthenticated)
return;
const flag = data.slice(0, constants_1.TUNNEL_TCP_FLAG.length).toString();
if (flag !== constants_1.TUNNEL_TCP_FLAG) {
logger.error(`unknown flag: ${flag}, should be ${constants_1.TUNNEL_TCP_FLAG}`);
socket.write(`${constants_1.TUNNEL_TCP_FLAG}401 unauthorized`);
socket.destroy();
return;
}
const UID_LENGTH = 36;
const REQUEST_ID_LENGTH = 36;
const SIGNATURE_LENGTH = 64;
const ID_END = constants_1.TUNNEL_TCP_FLAG.length + UID_LENGTH;
const REQUEST_ID_END = ID_END + REQUEST_ID_LENGTH;
const SIGN_END = REQUEST_ID_END + SIGNATURE_LENGTH;
const _id = data.slice(constants_1.TUNNEL_TCP_FLAG.length, ID_END).toString();
const _request_id = data.slice(ID_END, REQUEST_ID_END).toString();
const _sign = data.slice(REQUEST_ID_END, SIGN_END).toString();
debug(`id: ${_id}, request_id: ${_request_id}, sign: ${_sign}`);
let signedSecret;
try {
const res = await onGetToken(_id);
signedSecret = res.token;
if (!signedSecret) {
logger.error(`invalid client(id:${_id}): ${_sign}`);
socket.write(`${constants_1.TUNNEL_TCP_FLAG}403 invlid client`);
socket.destroy();
return;
}
}
catch (error) {
logger.error(`invalid client(id:${_id}): ${_sign}:`, error);
socket.write(`${constants_1.TUNNEL_TCP_FLAG}404 invlid client`);
socket.destroy();
return;
}
const expectedSign = hmac.hmacSHA256(_id, signedSecret);
if (_sign !== expectedSign) {
logger.error(`invalid signature(id:${_id}): ${_sign}, expected: ${expectedSign}`);
socket.write(`${constants_1.TUNNEL_TCP_FLAG}402 invlid signature`);
socket.destroy();
return;
}
if (!requestId) {
requestId = _request_id;
socket.requestId = requestId;
logger.info(`[tunnel:client][connect][2] request id: ${requestId}, ip ${socket.remoteAddress}`);
}
s.isAuthenticated = true;
socket.write(constants_1.TUNNEL_TCP_OK_FLAG);
onAuthenticate(_id, _request_id, socket, server);
});
onSocketConnect(socket);
});
server.listen(port, '0.0.0.0', () => {
onServerCreate(server);
});
return server;
}