增加http反向代理;

使用IPGlobalProperties获取全局ip;
This commit is contained in:
陈国伟 2021-07-29 13:59:22 +08:00
parent ddb6747a7a
commit 48e994182f
14 changed files with 169 additions and 130 deletions

View File

@ -1,4 +1,5 @@
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Net;
using System.Net.NetworkInformation;
@ -52,17 +53,10 @@ namespace FastGithub.Configuration
/// <returns></returns>
private static bool IsLocalMachineIPAddress(IPAddress address)
{
foreach (var @interface in NetworkInterface.GetAllNetworkInterfaces())
{
foreach (var addressInfo in @interface.GetIPProperties().UnicastAddresses)
{
if (addressInfo.Address.Equals(address))
{
return true;
}
}
}
return false;
return IPGlobalProperties
.GetIPGlobalProperties()
.GetUnicastAddresses()
.Any(item => item.Address.Equals(address));
}
}
}

View File

@ -1,8 +1,10 @@
using DNS.Protocol;
using FastGithub.Configuration;
using Microsoft.Extensions.Logging;
using System;
using System.Diagnostics;
using System.Linq;
using System.Net;
using System.Net.NetworkInformation;
using System.Net.Sockets;
using System.Threading;
using System.Threading.Tasks;
@ -40,9 +42,15 @@ namespace FastGithub.Dns
/// <returns></returns>
public void Bind(IPAddress address, int port)
{
if (OperatingSystem.IsWindows() && UdpTable.TryGetOwnerProcessId(port, out var processId))
if (OperatingSystem.IsWindows())
{
Process.GetProcessById(processId).Kill();
UdpTable.KillPortOwner(port);
}
var udpListeners = IPGlobalProperties.GetIPGlobalProperties().GetActiveUdpListeners();
if (udpListeners.Any(item => item.Port == port))
{
throw new FastGithubException($"udp端口{port}已经被其它进程占用");
}
if (OperatingSystem.IsWindows())

View File

@ -84,15 +84,9 @@ namespace FastGithub.Dns
/// <returns></returns>
private static IEnumerable<IPAddress> GetLocalMachineIPAddress()
{
yield return IPAddress.Loopback;
yield return IPAddress.IPv6Loopback;
foreach (var @interface in NetworkInterface.GetAllNetworkInterfaces())
foreach (var item in IPGlobalProperties.GetIPGlobalProperties().GetUnicastAddresses())
{
foreach (var addressInfo in @interface.GetIPProperties().UnicastAddresses)
{
yield return addressInfo.Address;
}
yield return item.Address;
}
}
}

View File

@ -1,5 +1,6 @@
using System;
using System.Buffers.Binary;
using System.Diagnostics;
using System.Net;
using System.Net.Sockets;
using System.Runtime.InteropServices;
@ -18,6 +19,34 @@ namespace FastGithub.Dns
[DllImport("iphlpapi.dll", SetLastError = true)]
private static extern uint GetExtendedUdpTable(void* pUdpTable, ref int pdwSize, bool bOrder, AddressFamily ulAf, UDP_TABLE_CLASS tableClass, uint reserved = 0);
/// <summary>
/// 杀死占用进程
/// </summary>
/// <param name="port"></param>
/// <returns></returns>
public static bool KillPortOwner(int port)
{
if (TryGetOwnerProcessId(port, out var pid) == false)
{
return true;
}
try
{
Process.GetProcessById(pid).Kill();
return true;
}
catch (ArgumentException)
{
return true;
}
catch (Exception)
{
return false;
}
}
/// <summary>
/// 获取udp端口的占用进程id
/// </summary>

View File

@ -22,9 +22,34 @@ namespace FastGithub
public static class KestrelServerOptionsExtensions
{
/// <summary>
/// 域名证书缓存
/// 服务器证书缓存
/// </summary>
private static readonly IMemoryCache domainCertCache = new MemoryCache(Options.Create(new MemoryCacheOptions()));
private static readonly IMemoryCache serverCertCache = new MemoryCache(Options.Create(new MemoryCacheOptions()));
/// <summary>
/// 监听http的反向代理
/// </summary>
/// <param name="kestrel"></param>
public static void ListenHttpReverseProxy(this KestrelServerOptions kestrel)
{
var loggerFactory = kestrel.ApplicationServices.GetRequiredService<ILoggerFactory>();
var logger = loggerFactory.CreateLogger($"{nameof(FastGithub)}.{nameof(ReverseProxy)}");
const int HTTP_PORT = 80;
if (OperatingSystem.IsWindows())
{
TcpTable.KillPortOwner(HTTP_PORT);
}
if (CanTcpListen(HTTP_PORT) == false)
{
logger.LogWarning($"无法监听tcp端口{HTTP_PORT}{nameof(FastGithub)}无法http反向代理");
}
else
{
kestrel.Listen(IPAddress.Any, HTTP_PORT);
}
}
/// <summary>
/// 监听https的反向代理
@ -43,10 +68,34 @@ namespace FastGithub
GeneratorCaCert(caPublicCerPath, caPrivateKeyPath);
InstallCaCert(caPublicCerPath, logger);
kestrel.Listen(IPAddress.Any, 443, listen =>
listen.UseHttps(https =>
https.ServerCertificateSelector = (ctx, domain) =>
GetDomainCert(domain, caPublicCerPath, caPrivateKeyPath)));
const int HTTPS_PORT = 443;
if (OperatingSystem.IsWindows())
{
TcpTable.KillPortOwner(HTTPS_PORT);
}
if (CanTcpListen(HTTPS_PORT) == false)
{
logger.LogWarning($"无法监听tcp端口{HTTPS_PORT}{nameof(FastGithub)}无法https反向代理");
}
else
{
kestrel.Listen(IPAddress.Any, HTTPS_PORT, listen =>
listen.UseHttps(https =>
https.ServerCertificateSelector = (ctx, domain) =>
GetServerCert(domain, caPublicCerPath, caPrivateKeyPath)));
}
}
/// <summary>
/// 是否可以监听指定端口
/// </summary>
/// <param name="port"></param>
/// <returns></returns>
private static bool CanTcpListen(int port)
{
var tcpListeners = IPGlobalProperties.GetIPGlobalProperties().GetActiveTcpListeners();
return tcpListeners.Any(item => item.Port == port) == false;
}
/// <summary>
@ -81,25 +130,24 @@ namespace FastGithub
if (OperatingSystem.IsWindows() == false)
{
logger.LogWarning($"不支持自动安装证书{caPublicCerPath}:请手动安装证书到根证书颁发机构");
return;
}
else
try
{
try
var caCert = new X509Certificate2(caPublicCerPath);
using var store = new X509Store(StoreName.Root, StoreLocation.LocalMachine);
store.Open(OpenFlags.ReadWrite);
if (store.Certificates.Find(X509FindType.FindByThumbprint, caCert.Thumbprint, true).Count == 0)
{
var caCert = new X509Certificate2(caPublicCerPath);
using var store = new X509Store(StoreName.Root, StoreLocation.LocalMachine);
store.Open(OpenFlags.ReadWrite);
if (store.Certificates.Find(X509FindType.FindByThumbprint, caCert.Thumbprint, true).Count == 0)
{
store.Add(caCert);
store.Close();
}
}
catch (Exception)
{
logger.LogWarning($"安装证书{caPublicCerPath}失败:请手动安装到“将所有的证书都放入下载存储”\\“受信任的根证书颁发机构”");
store.Add(caCert);
store.Close();
}
}
catch (Exception)
{
logger.LogWarning($"安装证书{caPublicCerPath}失败:请手动安装到“将所有的证书都放入下载存储”\\“受信任的根证书颁发机构”");
}
}
/// <summary>
@ -109,9 +157,9 @@ namespace FastGithub
/// <param name="caPublicCerPath"></param>
/// <param name="caPrivateKeyPath"></param>
/// <returns></returns>
private static X509Certificate2 GetDomainCert(string? domain, string caPublicCerPath, string caPrivateKeyPath)
private static X509Certificate2 GetServerCert(string? domain, string caPublicCerPath, string caPrivateKeyPath)
{
return domainCertCache.GetOrCreate(domain ?? string.Empty, GetOrCreateCert);
return serverCertCache.GetOrCreate(domain ?? string.Empty, GetOrCreateCert);
// 生成域名的1年证书
X509Certificate2 GetOrCreateCert(ICacheEntry entry)
@ -138,17 +186,14 @@ namespace FastGithub
yield return host;
}
yield return Environment.MachineName;
yield return IPAddress.Loopback.ToString();
var globalPropreties = IPGlobalProperties.GetIPGlobalProperties();
foreach (var @interface in NetworkInterface.GetAllNetworkInterfaces())
yield return globalPropreties.HostName;
foreach (var item in globalPropreties.GetUnicastAddresses())
{
foreach (var addressInfo in @interface.GetIPProperties().UnicastAddresses)
if (item.Address.AddressFamily == AddressFamily.InterNetwork)
{
if (addressInfo.Address.AddressFamily == AddressFamily.InterNetwork)
{
yield return addressInfo.Address.ToString();
}
yield return item.Address.ToString();
}
}
}

View File

@ -21,11 +21,11 @@ namespace FastGithub
}
/// <summary>
/// 使用https反向代理中间件
/// 使用反向代理中间件
/// </summary>
/// <param name="app"></param>
/// <returns></returns>
public static IApplicationBuilder UseHttpsReverseProxy(this IApplicationBuilder app)
public static IApplicationBuilder UseReverseProxy(this IApplicationBuilder app)
{
var middleware = app.ApplicationServices.GetRequiredService<ReverseProxyMiddleware>();
return app.Use(next => context => middleware.InvokeAsync(context, next));

View File

@ -1,65 +0,0 @@
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using System;
using System.Diagnostics;
using System.Threading;
using System.Threading.Tasks;
namespace FastGithub.ReverseProxy
{
/// <summary>
/// 反向代理端口检测后台服务
/// </summary>
sealed class ReverseProxyHostedService : IHostedService
{
private readonly ILogger<ReverseProxyHostedService> logger;
/// <summary>
/// 反向代理端口检测后台服务
/// </summary>
/// <param name="logger"></param>
public ReverseProxyHostedService(ILogger<ReverseProxyHostedService> logger)
{
this.logger = logger;
}
/// <summary>
/// 服务启动时
/// </summary>
/// <param name="cancellationToken"></param>
/// <returns></returns>
public Task StartAsync(CancellationToken cancellationToken)
{
const int HTTPSPORT = 443;
if (OperatingSystem.IsWindows())
{
if (TcpTable.TryGetOwnerProcessId(HTTPSPORT, out var pid))
{
try
{
Process.GetProcessById(pid).Kill();
}
catch (ArgumentException)
{
}
catch (Exception)
{
var processName = Process.GetProcessById(pid).ProcessName;
this.logger.LogError($"由于进程{processName}({pid})占用了{HTTPSPORT}端口,{nameof(FastGithub)}的反向代理无法工作");
}
}
}
return Task.CompletedTask;
}
/// <summary>
/// 服务停止时
/// </summary>
/// <param name="cancellationToken"></param>
/// <returns></returns>
public Task StopAsync(CancellationToken cancellationToken)
{
return Task.CompletedTask;
}
}
}

View File

@ -23,7 +23,7 @@ namespace FastGithub.ReverseProxy
IHttpClientFactory httpClientFactory,
FastGithubConfig fastGithubConfig,
ILogger<ReverseProxyMiddleware> logger)
{
{
this.httpForwarder = httpForwarder;
this.httpClientFactory = httpClientFactory;
this.fastGithubConfig = fastGithubConfig;
@ -54,7 +54,8 @@ namespace FastGithub.ReverseProxy
}
else
{
var destinationPrefix = GetDestinationPrefix(host, domainConfig.Destination);
var scheme = context.Request.Scheme;
var destinationPrefix = GetDestinationPrefix(scheme, host, domainConfig.Destination);
var httpClient = this.httpClientFactory.CreateHttpClient(domainConfig);
var error = await httpForwarder.SendAsync(context, destinationPrefix, httpClient);
await HandleErrorAsync(context, error);
@ -64,12 +65,13 @@ namespace FastGithub.ReverseProxy
/// <summary>
/// 获取目标前缀
/// </summary>
/// <param name="host"></param>
/// <param name="scheme"></param>
/// <param name="host"></param>
/// <param name="destination"></param>
/// <returns></returns>
private string GetDestinationPrefix(string host, Uri? destination)
private string GetDestinationPrefix(string scheme, string host, Uri? destination)
{
var defaultValue = $"https://{host}/";
var defaultValue = $"{scheme}://{host}/";
if (destination == null)
{
return defaultValue;

View File

@ -19,8 +19,7 @@ namespace FastGithub
.AddMemoryCache()
.AddHttpForwarder()
.AddSingleton<RequestLoggingMilldeware>()
.AddSingleton<ReverseProxyMiddleware>()
.AddHostedService<ReverseProxyHostedService>();
.AddSingleton<ReverseProxyMiddleware>();
}
}
}

View File

@ -1,5 +1,6 @@
using System;
using System.Buffers.Binary;
using System.Diagnostics;
using System.Net;
using System.Net.Sockets;
using System.Runtime.InteropServices;
@ -18,6 +19,34 @@ namespace FastGithub.ReverseProxy
[DllImport("iphlpapi.dll", SetLastError = true)]
private static extern uint GetExtendedTcpTable(void* pTcpTable, ref int pdwSize, bool bOrder, AddressFamily ulAf, TCP_TABLE_CLASS tableClass, uint reserved = 0);
/// <summary>
/// 杀死占用进程
/// </summary>
/// <param name="port"></param>
/// <returns></returns>
public static bool KillPortOwner(int port)
{
if (TryGetOwnerProcessId(port, out var pid) == false)
{
return true;
}
try
{
Process.GetProcessById(pid).Kill();
return true;
}
catch (ArgumentException)
{
return true;
}
catch (Exception)
{
return false;
}
}
/// <summary>
/// 获取tcp端口的占用进程id
/// </summary>

View File

@ -29,7 +29,7 @@ namespace FastGithub
/// <returns></returns>
public Task StartAsync(CancellationToken cancellationToken)
{
this.logger.LogInformation($"{nameof(FastGithub)}启动完成访问https://127.0.0.1或本机其它任意ip可进入Dashboard");
this.logger.LogInformation($"{nameof(FastGithub)}启动完成访问http://127.0.0.1或本机其它任意ip可进入Dashboard");
return Task.CompletedTask;
}

View File

@ -41,7 +41,11 @@ namespace FastGithub
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
webBuilder.UseKestrel(kestrel => kestrel.ListenHttpsReverseProxy());
webBuilder.UseKestrel(kestrel =>
{
kestrel.ListenHttpReverseProxy();
kestrel.ListenHttpsReverseProxy();
});
});
}
}

View File

@ -43,7 +43,7 @@ namespace FastGithub
public void Configure(IApplicationBuilder app)
{
app.UseRequestLogging();
app.UseHttpsReverseProxy();
app.UseReverseProxy();
app.UseRouting();
app.UseEndpoints(endpoints =>
{

View File

@ -11,7 +11,7 @@ github加速神器
如果不能下载[releases](https://github.com/xljiulang/FastGithub/releases)里发布的程序可以到Q群`307306673`里面的群文件下载。
### 使用说明
运行FastGithub然后浏览器访问 https://127.0.0.1 或其它ip进入Dashboard
运行FastGithub然后浏览器访问 http://127.0.0.1 或其它ip进入Dashboard
### 安全性说明
FastGithub生成自签名CA证书要求安装到本机设备。FastGithub为每台不同设备生成的CA不同的证书保存在CACert文件夹下请不要将证书私钥泄露给他人以免造成损失。