This commit is contained in:
snltty
2025-07-05 15:47:10 +08:00
parent e322e656c5
commit 2a01734f58
20 changed files with 136 additions and 53 deletions

View File

@@ -22,30 +22,21 @@ server.json
//cdkey 加密密钥 //cdkey 加密密钥
"SecretKey": "snltty" "SecretKey": "snltty"
}, },
//中继
"Relay": {
//中继密钥,客户端密钥不准确时无法使用本中继
"SecretKey": "",
},
//信标服务器端口 //信标服务器端口
"ServicePort": 1802, "ServicePort": 1802,
//内网穿透配置 //内网穿透配置
"SForward": { "SForward": {
//内网穿透密钥
"SecretKey": "",
"BufferSize": 3, "BufferSize": 3,
//web端口用于按域名穿透 //web端口用于按域名穿透
"WebPort": 0, "WebPort": 0,
//隧道端口范围,用于按端口穿透 //隧道端口范围,用于按端口穿透
"TunnelPortRange": [ "TunnelPortRange": [ 10000,60000]
10000,
60000
]
}, },
//登入信标密钥,默认为空,即为所有客户端均可登入本信标服务器 //登入信标密钥,默认为空,即为所有客户端均可登入本信标服务器
"SignIn": { "SignIn": {
"SecretKey": "", "Anonymous": true, //允许匿名登录
"SuperKey": "snltty", //超级密钥
"SuperPassword": "snltty",//超级密码
"CleanDays": 7 //当一组内的所有设备都超过7天未上线则清理 "CleanDays": 7 //当一组内的所有设备都超过7天未上线则清理
}, },
//虚拟网卡 //虚拟网卡
@@ -56,11 +47,10 @@ server.json
"NetworkDays": 30 //网络租期 "NetworkDays": 30 //网络租期
} }
}, },
//更新密钥,客户端密钥不正确时,只能更新自己本身
"Updater": { "Updater": {
"SecretKey": "" "Sync2Server": false //客户端自动同步到服务器版本
} }
} }
``` ```
action.json这个可以不管等用得上自定义验证的时候就知道了 action.json这个可以不管等用得上自定义验证的时候就知道了
@@ -68,6 +58,7 @@ action.json这个可以不管等用得上自定义验证的时候就知道
{ {
"SignInActionUrl": "", //登入信标的验证接口 "SignInActionUrl": "", //登入信标的验证接口
"RelayActionUrl": "", //中继验证接口 "RelayActionUrl": "", //中继验证接口
"RelayNodeUrl": "", //中继节点验证接口
"SForwardActionUrl": ""//服务器穿透的验证接口 "SForwardActionUrl": ""//服务器穿透的验证接口
} }
``` ```

View File

@@ -20,6 +20,10 @@ sidebar_position: 8
对应配置文件 client.json 对应配置文件 client.json
``` ```
{ {
//自定义验证
"Action": {
"Args": {} //自定义验证的参数
},
"Name": "A", //客户端名 "Name": "A", //客户端名
//顶级满权限 //顶级满权限
"FullAccess": true, "FullAccess": true,
@@ -31,17 +35,16 @@ sidebar_position: 8
"WebPort": 1804, //web端口 "WebPort": 1804, //web端口
"WebRoot": "./web/" //web根目录 "WebRoot": "./web/" //web根目录
}, },
//cdkey
"Cdkey": {
"SecretKey": "snltty"
},
//服务器 //服务器
"Servers": [ "Servers": [
{ {
"Name": "Linker", //信标服务器名称 "Name": "Linker", //信标服务器名称
"Host": "127.0.0.1:1802", //信标服务器地址 "Host": "127.0.0.1:1802", //信标服务器地址
"SecretKey": null, //信标密钥 "Host1": "127.0.0.1:1802", //信标服务器地址1
"UserId": "8225e9d4-0ac7-4d76-9946-c4fe04ad4696" //用户标识,多个客户端可相同 "UserId": "8225e9d4-0ac7-4d76-9946-c4fe04ad4696", //用户标识,多个客户端可相同
"SuperKey": "", //服务器密钥
"SuperPassword": "" //服务器密码
} }
], ],
//分组 //分组
@@ -56,23 +59,17 @@ sidebar_position: 8
"Relay": { "Relay": {
"Servers": [ "Servers": [
{ {
"SecretKey": "snltty", //中继密钥
"Disabled": false, //是否禁用 "Disabled": false, //是否禁用
"SSL": true //启用ssl "SSL": true, //启用ssl
"RelayType": 0, //默认0
"UseCdkey": true //使用cdkey
} }
] ]
}, },
//内网穿透
"SForward": {
"SecretKey": "snltty" //服务器穿透密钥
},
//更新 //更新
"Updater": { "Updater": {
"SecretKey": "snltty" //更新密钥 "Sync2Server": true //自动同步到服务器版本
},
//自定义验证
"Action": {
"Args": {} //自定义验证的参数
} }
} }
``` ```

View File

@@ -8,11 +8,9 @@ sidebar_position: 2
:::tip[说明] :::tip[说明]
1. 如果你自建信标服务器,在服务端 `configs/server.json` 复制中继密钥(`Relay->SecretKey`),在客户端中填写,如果密钥正确则可以管理服务器的中继cdkey发布cdkey给别人使用 1. 如果你自建信标服务器,填写你服务器的管理密钥和管理密码则可以管理服务器的中继cdkey发布cdkey给别人使用
2. 如果你使用别人的服务器你没有密钥可以不填写则需要使用别人服务器的公开中继节点或使用他提供的cdkey 2. 如果你使用别人的服务器你没有密钥可以不填写则需要使用别人服务器的公开中继节点或使用他提供的cdkey
3. `按喜好调整好即可,往后的所有通信都是自动的,无需其它操作` 3. `按喜好调整好即可,往后的所有通信都是自动的,无需其它操作`
![Docusaurus Plushie](./img/relay.jpg)
::: :::

Binary file not shown.

Before

Width:  |  Height:  |  Size: 109 KiB

View File

@@ -11,14 +11,12 @@ sidebar_position: 4
3. 只在被访问端运行linker客户端访问端不需要运行客户端 3. 只在被访问端运行linker客户端访问端不需要运行客户端
::: :::
## 1、配置穿透密钥和端口 ## 1、配置穿透
:::tip[说明] :::tip[说明]
1. 在服务端`configs/server.json` 1. 在服务端`configs/server.json`
2. `WebPort` 用于单一端口承载多个HTTP服务因为HTTP Headers 中有Host字段可以用于区分不同的HTTP服务 2. `WebPort` 用于单一端口承载多个HTTP服务因为HTTP Headers 中有Host字段可以用于区分不同的HTTP服务
3. `TunnelPortRange` 用于开放一个端口范围提供给客户端动态添加端口监听每个端口对应不同的TCP+UDP服务 3. `TunnelPortRange` 用于开放一个端口范围提供给客户端动态添加端口监听每个端口对应不同的TCP+UDP服务
4. `SecretKey` 为密钥,客户端填写此密钥,才能使用穿透
![Docusaurus Plushie](./img/sforward1.jpg)
::: :::
## 2、配置端口转发 ## 2、配置端口转发

View File

@@ -71,6 +71,10 @@ public sealed class JsonArgSignInInfo
/// 分组id /// 分组id
/// </summary> /// </summary>
public string GroupId { get; set; } = string.Empty; public string GroupId { get; set; } = string.Empty;
/// <summary>
/// 是否超级管理员
/// </summary>
public bool Super { get; set; }
} }
public sealed class JsonArgRelayInfo public sealed class JsonArgRelayInfo
{ {

View File

@@ -11,6 +11,10 @@
/// </summary> /// </summary>
public string RelayActionUrl { get; } public string RelayActionUrl { get; }
/// <summary> /// <summary>
/// 中继节点验证地址
/// </summary>
public string RelayNodeUrl { get; }
/// <summary>
/// 内网穿透验证地址 /// 内网穿透验证地址
/// </summary> /// </summary>
public string SForwardActionUrl { get; } public string SForwardActionUrl { get; }
@@ -27,6 +31,7 @@
/// <param name="url"></param> /// <param name="url"></param>
/// <returns></returns> /// <returns></returns>
public bool SetRelayActionUrl(string url); public bool SetRelayActionUrl(string url);
public bool SetRelayNodeUrl(string url);
/// <summary> /// <summary>
/// 登录验证地址 /// 登录验证地址
/// </summary> /// </summary>

View File

@@ -1,4 +1,6 @@
using linker.libs.extends; using linker.libs.extends;
using linker.messenger.relay.client.transport;
using linker.messenger.relay.server;
using linker.messenger.relay.server.validator; using linker.messenger.relay.server.validator;
using linker.messenger.sforward; using linker.messenger.sforward;
using linker.messenger.sforward.server.validator; using linker.messenger.sforward.server.validator;
@@ -20,6 +22,10 @@ namespace linker.messenger.action
/// </summary> /// </summary>
public JsonArgRelayInfo Relay { get; set; } public JsonArgRelayInfo Relay { get; set; }
/// <summary> /// <summary>
/// 中继节点验证,
/// </summary>
public JsonArgRelayNodeInfo RelayNode { get; set; }
/// <summary>
/// 穿透信息非穿透验证时为null /// 穿透信息非穿透验证时为null
/// </summary> /// </summary>
public JsonArgSForwardInfo SForward { get; set; } public JsonArgSForwardInfo SForward { get; set; }
@@ -82,6 +88,24 @@ namespace linker.messenger.action
/// </summary> /// </summary>
public ulong FlowingId { get; set; } public ulong FlowingId { get; set; }
} }
public sealed class JsonArgRelayNodeInfo
{
/// <summary>
/// 来源设备id
/// </summary>
public string FromMachineId { get; set; }
/// <summary>
/// 来源设备名
/// </summary>
public string FromMachineName { get; set; }
public List<JsonArgRelayNodeItemInfo> Nodes { get; set; } = [];
}
public sealed class JsonArgRelayNodeItemInfo
{
public string Id { get; set; }
public string Name { get; set; }
public bool Public { get; set; }
}
public sealed class JsonArgSForwardInfo public sealed class JsonArgSForwardInfo
{ {
/// <summary> /// <summary>
@@ -204,6 +228,44 @@ namespace linker.messenger.action
} }
return string.Empty; return string.Empty;
} }
public async Task<List<RelayServerNodeReportInfo170>> Validate(string userid, SignCacheInfo fromMachine, List<RelayServerNodeReportInfo170> nodes)
{
if (string.IsNullOrWhiteSpace(actionServerStore.RelayNodeUrl) == false)
{
if (actionServerStore.TryGetActionArg(fromMachine.Args, out string str, out string machineKey) == false)
{
return [];
}
JsonArgInfo replace = new JsonArgInfo
{
RelayNode = new JsonArgRelayNodeInfo
{
FromMachineId = fromMachine.Id,
FromMachineName = fromMachine.MachineName,
Nodes = nodes.Select(c => new JsonArgRelayNodeItemInfo
{
Id = c.Id,
Name = c.Name,
Public = c.Public
}).ToList() ?? []
},
Signin = new JsonArgSignInInfo
{
GroupId = fromMachine.GroupId,
MachineId = fromMachine.MachineId,
MachineName = fromMachine.MachineName,
MachineKey = machineKey,
IPAddress = fromMachine.Connection.Address.Address,
Super = fromMachine.Super
}
};
string ids = await actionTransfer.ExcuteActions(Replace(replace, str), actionServerStore.RelayNodeUrl).ConfigureAwait(false);
if (string.IsNullOrWhiteSpace(ids)) return [];
return nodes.Where(c => ids.Split(',').Contains(c.Id)).ToList();
}
return nodes;
}
} }
public sealed class SForwardValidatorAction : JsonArgReplace, ISForwardValidator public sealed class SForwardValidatorAction : JsonArgReplace, ISForwardValidator

View File

@@ -52,7 +52,7 @@ namespace linker.messenger.cdkey
connection.Write(Helper.FalseArray); connection.Write(Helper.FalseArray);
return; return;
} }
if (cache.Super == false) if (cache.Super)
{ {
await cdkeyStore.Del(info.Id).ConfigureAwait(false); await cdkeyStore.Del(info.Id).ConfigureAwait(false);
} }

View File

@@ -103,7 +103,7 @@ namespace linker.messenger.flow.messenger
return; return;
} }
if (cache.Super == false) if (cache.Super)
{ {
info.GroupId = string.Empty; info.GroupId = string.Empty;
} }

View File

@@ -79,7 +79,7 @@ namespace linker.messenger.relay.messenger
connection.Write(serializer.Serialize(new List<RelayServerNodeReportInfo170> { })); connection.Write(serializer.Serialize(new List<RelayServerNodeReportInfo170> { }));
return; return;
} }
(List<RelayServerNodeReportInfo170> nodes, bool validated) = await GetNodes(cache.Super, info.UserId); List<RelayServerNodeReportInfo170> nodes = await GetNodes(info.UserId, cache);
connection.Write(serializer.Serialize(nodes)); connection.Write(serializer.Serialize(nodes));
} }
@@ -103,18 +103,18 @@ namespace linker.messenger.relay.messenger
} }
RelayAskResultInfo170 result = new RelayAskResultInfo170(); RelayAskResultInfo170 result = new RelayAskResultInfo170();
(result.Nodes, bool validated) = await GetNodes(from.Super, info.UserId).ConfigureAwait(false); result.Nodes = await GetNodes(info.UserId, from).ConfigureAwait(false);
if (result.Nodes.Count > 0) if (result.Nodes.Count > 0)
{ {
result.FlowingId = relayServerTransfer.AddRelay(from.MachineId, from.MachineName, to.MachineId, to.MachineName, from.GroupId, info.UserId, validated, info.UseCdkey); result.FlowingId = relayServerTransfer.AddRelay(from.MachineId, from.MachineName, to.MachineId, to.MachineName, from.GroupId, info.UserId, from.Super, info.UseCdkey);
} }
connection.Write(serializer.Serialize(result)); connection.Write(serializer.Serialize(result));
} }
private async Task<(List<RelayServerNodeReportInfo170>, bool)> GetNodes(bool super, string userid) private async Task<List<RelayServerNodeReportInfo170>> GetNodes(string userid, SignCacheInfo from)
{ {
return (await relayServerTransfer.GetNodes(super, userid), super); return await relayServerTransfer.GetNodes(from.Super, userid);
} }
@@ -248,7 +248,7 @@ namespace linker.messenger.relay.messenger
public async Task UpdateNodeForward(IConnection connection) public async Task UpdateNodeForward(IConnection connection)
{ {
RelayServerNodeUpdateWrapInfo info = serializer.Deserialize<RelayServerNodeUpdateWrapInfo>(connection.ReceiveRequestWrap.Payload.Span); RelayServerNodeUpdateWrapInfo info = serializer.Deserialize<RelayServerNodeUpdateWrapInfo>(connection.ReceiveRequestWrap.Payload.Span);
if (signCaching.TryGet(connection.Id, out SignCacheInfo cache) && cache.Super) if (signCaching.TryGet(connection.Id, out SignCacheInfo cache) && cache.Super)
{ {
await relayServerTransfer.UpdateNodeReport(info.Info).ConfigureAwait(false); await relayServerTransfer.UpdateNodeReport(info.Info).ConfigureAwait(false);
connection.Write(Helper.TrueArray); connection.Write(Helper.TrueArray);

View File

@@ -17,5 +17,13 @@ namespace linker.messenger.relay.server.validator
/// <param name="toMachine">目标客户端可能为null</param> /// <param name="toMachine">目标客户端可能为null</param>
/// <returns></returns> /// <returns></returns>
public Task<string> Validate(RelayInfo170 relayInfo, SignCacheInfo fromMachine, SignCacheInfo toMachine); public Task<string> Validate(RelayInfo170 relayInfo, SignCacheInfo fromMachine, SignCacheInfo toMachine);
/// <summary>
/// 验证节点
/// </summary>
/// <param name="relayInfo"></param>
/// <param name="fromMachine"></param>
/// <param name="nodes"></param>
/// <returns></returns>
public Task<List<RelayServerNodeReportInfo170>> Validate(string userid, SignCacheInfo fromMachine, List<RelayServerNodeReportInfo170> nodes);
} }
} }

View File

@@ -60,6 +60,18 @@ namespace linker.messenger.relay.server.validator
} }
return string.Empty; return string.Empty;
} }
public async Task<List<RelayServerNodeReportInfo170>> Validate(string userid, SignCacheInfo fromMachine, List<RelayServerNodeReportInfo170> nodes)
{
foreach (var item in validators)
{
nodes = await item.Validate(userid, fromMachine, nodes).ConfigureAwait(false);
if (nodes == null || nodes.Count == 0)
{
return [];
}
}
return nodes;
}
public sealed class RelayServerValidatorEqualityComparer : IEqualityComparer<IRelayServerValidator> public sealed class RelayServerValidatorEqualityComparer : IEqualityComparer<IRelayServerValidator>
{ {

View File

@@ -17,7 +17,7 @@ namespace linker.messenger.sforward.server.validator
public async Task<string> Validate(SignCacheInfo signCacheInfo, SForwardAddInfo sForwardAddInfo) public async Task<string> Validate(SignCacheInfo signCacheInfo, SForwardAddInfo sForwardAddInfo)
{ {
if (signCacheInfo.Super) if (signCacheInfo.Super == false)
{ {
return $"need super key and password"; return $"need super key and password";
} }

View File

@@ -7,7 +7,9 @@ namespace linker.messenger.store.file.action
public string SignInActionUrl => config.Data.Action.SignInActionUrl; public string SignInActionUrl => config.Data.Action.SignInActionUrl;
public string RelayActionUrl => config.Data.Action.RelayActionUrl; public string RelayActionUrl => config.Data.Action.RelayActionUrl;
public string RelayNodeUrl => config.Data.Action.RelayNodeUrl;
public string SForwardActionUrl => config.Data.Action.SForwardActionUrl; public string SForwardActionUrl => config.Data.Action.SForwardActionUrl;
private readonly FileConfig config; private readonly FileConfig config;
public ActionServerStore(FileConfig config) public ActionServerStore(FileConfig config)
@@ -26,6 +28,11 @@ namespace linker.messenger.store.file.action
config.Data.Action.RelayActionUrl = url; config.Data.Action.RelayActionUrl = url;
return true; return true;
} }
public bool SetRelayNodeUrl(string url)
{
config.Data.Action.RelayNodeUrl = url;
return true;
}
public bool SetSForwardActionUrl(string url) public bool SetSForwardActionUrl(string url)
{ {
@@ -47,7 +54,7 @@ namespace linker.messenger.store.file.action
return true; return true;
} }
} }
} }

View File

@@ -12,7 +12,9 @@ namespace linker.messenger.store.file
public ConfigActionInfo() { } public ConfigActionInfo() { }
public string SignInActionUrl { get; set; } = string.Empty; public string SignInActionUrl { get; set; } = string.Empty;
public string RelayActionUrl { get; set; } = string.Empty; public string RelayActionUrl { get; set; } = string.Empty;
public string RelayNodeUrl { get; set; } = string.Empty;
public string SForwardActionUrl { get; set; } = string.Empty; public string SForwardActionUrl { get; set; } = string.Empty;
public object Deserialize(string text) public object Deserialize(string text)
{ {

View File

@@ -58,7 +58,6 @@ namespace linker.messenger.store.file
public string SecretKey { get; set; } = Helper.GlobalString; public string SecretKey { get; set; } = Helper.GlobalString;
#else #else
public string SecretKey { get; set; } = Guid.NewGuid().ToString().ToUpper(); public string SecretKey { get; set; } = Guid.NewGuid().ToString().ToUpper();
public string SuperPassword { get; set; } = Guid.NewGuid().ToString().ToUpper();
#endif #endif
} }
} }

View File

@@ -229,7 +229,7 @@ namespace linker.messenger.updater
} }
//需要密钥 //需要密钥
if (confirm.All && cache.Super) if (confirm.All && cache.Super == false)
{ {
connection.Write(Helper.FalseArray); connection.Write(Helper.FalseArray);
return; return;

View File

@@ -19,7 +19,7 @@ export default {
const {t} = useI18n(); const {t} = useI18n();
const globalData = injectGlobalData(); const globalData = injectGlobalData();
const state = reactive({ const state = reactive({
sync2Server:false sync2Server:globalData.value.config.Client.Updater.Sync2Server,
}); });
const handleSync2ServerChange = ()=>{ const handleSync2ServerChange = ()=>{
setSync2Server(state.sync2Server).then(()=>{ setSync2Server(state.sync2Server).then(()=>{

View File

@@ -1,5 +1,5 @@
v1.8.6 v1.8.6
2025-07-05 13:23:49 2025-07-05 15:47:10
1. 一些累计更新 1. 一些累计更新
2. 白名单 2. 白名单
3. 优化防火墙 3. 优化防火墙