增加TlsSniPattern配置
This commit is contained in:
parent
73b2ff58b7
commit
d3b01002cd
@ -12,6 +12,11 @@ namespace FastGithub
|
||||
/// </summary>
|
||||
public bool TlsSni { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// 自定义SNI值的表达式
|
||||
/// </summary>
|
||||
public string? TlsSniPattern { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// 请求超时时长
|
||||
/// </summary>
|
||||
@ -27,5 +32,22 @@ namespace FastGithub
|
||||
/// 自定义响应
|
||||
/// </summary>
|
||||
public ResponseConfig? Response { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// 获取TlsSniPattern
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public TlsSniPattern GetTlsSniPattern()
|
||||
{
|
||||
if (this.TlsSni == false)
|
||||
{
|
||||
return FastGithub.TlsSniPattern.None;
|
||||
}
|
||||
if (string.IsNullOrEmpty(this.TlsSniPattern))
|
||||
{
|
||||
return FastGithub.TlsSniPattern.Domain;
|
||||
}
|
||||
return new TlsSniPattern(this.TlsSniPattern);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
86
FastGithub.Core/TlsSniPattern.cs
Normal file
86
FastGithub.Core/TlsSniPattern.cs
Normal file
@ -0,0 +1,86 @@
|
||||
using System;
|
||||
using System.Net;
|
||||
|
||||
namespace FastGithub
|
||||
{
|
||||
/// <summary>
|
||||
/// Sni自定义值表达式
|
||||
/// @domain变量表示取域名值
|
||||
/// @ipadress变量表示取ip
|
||||
/// @random变量表示取随机值
|
||||
/// </summary>
|
||||
public struct TlsSniPattern
|
||||
{
|
||||
/// <summary>
|
||||
/// 获取表示式值
|
||||
/// </summary>
|
||||
public string Value { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 无SNI
|
||||
/// </summary>
|
||||
public static TlsSniPattern None { get; } = new TlsSniPattern(string.Empty);
|
||||
|
||||
/// <summary>
|
||||
/// 域名SNI
|
||||
/// </summary>
|
||||
public static TlsSniPattern Domain { get; } = new TlsSniPattern("@domain");
|
||||
|
||||
/// <summary>
|
||||
/// IP值的SNI
|
||||
/// </summary>
|
||||
public static TlsSniPattern IPAddress { get; } = new TlsSniPattern("@ipaddress");
|
||||
|
||||
/// <summary>
|
||||
/// 随机值的SNI
|
||||
/// </summary>
|
||||
public static TlsSniPattern Random { get; } = new TlsSniPattern("@random");
|
||||
|
||||
/// <summary>
|
||||
/// Sni自定义值表达式
|
||||
/// </summary>
|
||||
/// <param name="value">表示式值</param>
|
||||
public TlsSniPattern(string value)
|
||||
{
|
||||
this.Value = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 更新域名
|
||||
/// </summary>
|
||||
/// <param name="domain"></param>
|
||||
public TlsSniPattern WithDomain(string domain)
|
||||
{
|
||||
var value = this.Value.Replace(Domain.Value, domain, StringComparison.OrdinalIgnoreCase);
|
||||
return new TlsSniPattern(value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 更新ip地址
|
||||
/// </summary>
|
||||
/// <param name="address"></param>
|
||||
public TlsSniPattern WithIPAddress(IPAddress address)
|
||||
{
|
||||
var value = this.Value.Replace(IPAddress.Value, address.ToString(), StringComparison.OrdinalIgnoreCase);
|
||||
return new TlsSniPattern(value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 更新随机数
|
||||
/// </summary>
|
||||
public TlsSniPattern WithRandom()
|
||||
{
|
||||
var value = this.Value.Replace(Random.Value, Environment.TickCount64.ToString(), StringComparison.OrdinalIgnoreCase);
|
||||
return new TlsSniPattern(value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 转换为文本
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public override string ToString()
|
||||
{
|
||||
return this.Value;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -10,18 +10,18 @@ namespace FastGithub.ReverseProxy
|
||||
/// </summary>
|
||||
class HttpClient : HttpMessageInvoker
|
||||
{
|
||||
private readonly string tlsSniValue;
|
||||
private readonly TlsSniPattern tlsSniPattern;
|
||||
|
||||
/// <summary>
|
||||
/// YARP的HttpClient
|
||||
/// </summary>
|
||||
/// <param name="handler"></param>
|
||||
/// <param name="tlsSniValue"></param>
|
||||
/// <param name="tlsSniPattern"></param>
|
||||
/// <param name="disposeHandler"></param>
|
||||
public HttpClient(HttpMessageHandler handler, string tlsSniValue, bool disposeHandler = false) :
|
||||
public HttpClient(HttpMessageHandler handler, TlsSniPattern tlsSniPattern, bool disposeHandler = false) :
|
||||
base(handler, disposeHandler)
|
||||
{
|
||||
this.tlsSniValue = tlsSniValue;
|
||||
this.tlsSniPattern = tlsSniPattern;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -33,7 +33,7 @@ namespace FastGithub.ReverseProxy
|
||||
public override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
|
||||
{
|
||||
var isHttps = request.RequestUri?.Scheme == Uri.UriSchemeHttps;
|
||||
request.SetSniContext(new SniContext(isHttps, this.tlsSniValue));
|
||||
request.SetTlsSniContext(new TlsSniContext(isHttps, this.tlsSniPattern));
|
||||
return base.SendAsync(request, cancellationToken);
|
||||
}
|
||||
}
|
||||
|
||||
@ -35,13 +35,14 @@ namespace FastGithub.ReverseProxy
|
||||
Proxy = null,
|
||||
UseProxy = false,
|
||||
AllowAutoRedirect = false,
|
||||
ConnectCallback = async (ctx, ct) =>
|
||||
ConnectCallback = async (context, cancellationToken) =>
|
||||
{
|
||||
var socket = new Socket(SocketType.Stream, ProtocolType.Tcp);
|
||||
await socket.ConnectAsync(ctx.DnsEndPoint, ct);
|
||||
await socket.ConnectAsync(context.DnsEndPoint, cancellationToken);
|
||||
var stream = new NetworkStream(socket, ownsSocket: true);
|
||||
var sniContext = ctx.InitialRequestMessage.GetSniContext();
|
||||
if (sniContext.IsHttps == false)
|
||||
|
||||
var tlsSniContext = context.InitialRequestMessage.GetTlsSniContext();
|
||||
if (tlsSniContext.IsHttps == false)
|
||||
{
|
||||
return stream;
|
||||
}
|
||||
@ -49,9 +50,9 @@ namespace FastGithub.ReverseProxy
|
||||
var sslStream = new SslStream(stream, leaveInnerStreamOpen: false);
|
||||
await sslStream.AuthenticateAsClientAsync(new SslClientAuthenticationOptions
|
||||
{
|
||||
TargetHost = sniContext.TlsSniValue,
|
||||
TargetHost = tlsSniContext.TlsSniPattern.Value,
|
||||
RemoteCertificateValidationCallback = delegate { return true; }
|
||||
}, ct);
|
||||
}, cancellationToken);
|
||||
return sslStream;
|
||||
}
|
||||
};
|
||||
@ -76,6 +77,9 @@ namespace FastGithub.ReverseProxy
|
||||
};
|
||||
request.RequestUri = builder.Uri;
|
||||
request.Headers.Host = uri.Host;
|
||||
|
||||
var context = request.GetTlsSniContext();
|
||||
context.TlsSniPattern = context.TlsSniPattern.WithDomain(uri.Host).WithIPAddress(address).WithRandom();
|
||||
}
|
||||
return await base.SendAsync(request, cancellationToken);
|
||||
}
|
||||
|
||||
@ -56,10 +56,10 @@ namespace FastGithub.ReverseProxy
|
||||
var destinationPrefix = GetDestinationPrefix(host, domainConfig.Destination);
|
||||
var requestConfig = new ForwarderRequestConfig { Timeout = domainConfig.Timeout };
|
||||
|
||||
var tlsSniValue = domainConfig.TlsSni ? destinationPrefix.Host : string.Empty;
|
||||
using var httpClient = new HttpClient(this.httpClientHanlder, tlsSniValue);
|
||||
var tlsSniPattern = domainConfig.GetTlsSniPattern();
|
||||
using var httpClient = new HttpClient(this.httpClientHanlder, tlsSniPattern);
|
||||
|
||||
var error = await httpForwarder.SendAsync(context, destinationPrefix.ToString(), httpClient, requestConfig);
|
||||
var error = await httpForwarder.SendAsync(context, destinationPrefix, httpClient, requestConfig);
|
||||
await ResponseErrorAsync(context, error);
|
||||
}
|
||||
}
|
||||
@ -70,15 +70,16 @@ namespace FastGithub.ReverseProxy
|
||||
/// <param name="host"></param>
|
||||
/// <param name="destination"></param>
|
||||
/// <returns></returns>
|
||||
private Uri GetDestinationPrefix(string host, Uri? destination)
|
||||
private string GetDestinationPrefix(string host, Uri? destination)
|
||||
{
|
||||
var defaultValue = new Uri($"https://{host}/");
|
||||
var defaultValue = $"https://{host}/";
|
||||
if (destination == null)
|
||||
{
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
var result = new Uri(defaultValue, destination);
|
||||
var baseUri = new Uri(defaultValue);
|
||||
var result = new Uri(baseUri, destination).ToString();
|
||||
this.logger.LogInformation($"[{defaultValue}->{result}]");
|
||||
return result;
|
||||
}
|
||||
|
||||
@ -3,7 +3,7 @@
|
||||
/// <summary>
|
||||
/// Sni上下文
|
||||
/// </summary>
|
||||
sealed class SniContext
|
||||
sealed class TlsSniContext
|
||||
{
|
||||
/// <summary>
|
||||
/// 获取是否为https请求
|
||||
@ -11,19 +11,19 @@
|
||||
public bool IsHttps { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 获取Sni值
|
||||
/// 获取或设置Sni值的表达式
|
||||
/// </summary>
|
||||
public string TlsSniValue { get; }
|
||||
public TlsSniPattern TlsSniPattern { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Sni上下文
|
||||
/// </summary>
|
||||
/// <param name="isHttps"></param>
|
||||
/// <param name="tlsSniValue"></param>
|
||||
public SniContext(bool isHttps, string tlsSniValue)
|
||||
/// <param name="tlsSniPattern"></param>
|
||||
public TlsSniContext(bool isHttps, TlsSniPattern tlsSniPattern)
|
||||
{
|
||||
this.IsHttps = isHttps;
|
||||
this.TlsSniValue = tlsSniValue;
|
||||
this.TlsSniPattern = tlsSniPattern;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -6,32 +6,32 @@ namespace FastGithub.ReverseProxy
|
||||
/// <summary>
|
||||
/// SniContext扩展
|
||||
/// </summary>
|
||||
static class SniContextExtensions
|
||||
static class TlsSniContextExtensions
|
||||
{
|
||||
private static readonly HttpRequestOptionsKey<SniContext> key = new(nameof(SniContext));
|
||||
private static readonly HttpRequestOptionsKey<TlsSniContext> key = new(nameof(TlsSniContext));
|
||||
|
||||
/// <summary>
|
||||
/// 设置SniContext
|
||||
/// 设置TlsSniContext
|
||||
/// </summary>
|
||||
/// <param name="httpRequestMessage"></param>
|
||||
/// <param name="context"></param>
|
||||
public static void SetSniContext(this HttpRequestMessage httpRequestMessage, SniContext context)
|
||||
public static void SetTlsSniContext(this HttpRequestMessage httpRequestMessage, TlsSniContext context)
|
||||
{
|
||||
httpRequestMessage.Options.Set(key, context);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取SniContext
|
||||
/// 获取TlsSniContext
|
||||
/// </summary>
|
||||
/// <param name="httpRequestMessage"></param>
|
||||
/// <returns></returns>
|
||||
public static SniContext GetSniContext(this HttpRequestMessage httpRequestMessage)
|
||||
public static TlsSniContext GetTlsSniContext(this HttpRequestMessage httpRequestMessage)
|
||||
{
|
||||
if (httpRequestMessage.Options.TryGetValue(key, out var value))
|
||||
{
|
||||
return value;
|
||||
}
|
||||
throw new InvalidOperationException($"请先调用{nameof(SetSniContext)}");
|
||||
throw new InvalidOperationException($"请先调用{nameof(SetTlsSniContext)}");
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -11,6 +11,7 @@
|
||||
"DomainConfigs": { // 域名的*表示0到多个任意字符
|
||||
"github.com": {
|
||||
"TlsSni": false, // 指示tls握手时是否发送SNI
|
||||
"TlsSniPattern": null, // SNI表达式,@domain变量表示取域名值 @ipadress变量表示取ip @random变量表示取随机值,其它字符保留不替换
|
||||
"Timeout": null, // 请求超时时长,格式为"00:02:00",默认为null
|
||||
"Destination": null // 请求目的地,格式为绝对或相对Uri,默认null
|
||||
},
|
||||
|
||||
Loading…
Reference in New Issue
Block a user