diff --git a/FastGithub.Dns/DnsHostedService.cs b/FastGithub.Dns/DnsHostedService.cs
index db2426a..5c268c6 100644
--- a/FastGithub.Dns/DnsHostedService.cs
+++ b/FastGithub.Dns/DnsHostedService.cs
@@ -61,16 +61,20 @@ namespace FastGithub.Dns
{
SystemDnsUtil.DnsSetPrimitive(IPAddress.Loopback);
SystemDnsUtil.DnsFlushResolverCache();
- this.logger.LogInformation($"设置为本机主DNS成功");
+ this.logger.LogInformation($"设置成本机主DNS成功");
}
catch (Exception ex)
{
- this.logger.LogWarning($"设置为本机主DNS为{IPAddress.Loopback}失败:{ex.Message}");
+ this.logger.LogWarning($"设置成本机主DNS为{IPAddress.Loopback}失败:{ex.Message}");
}
}
+ else if (OperatingSystem.IsLinux())
+ {
+ this.logger.LogWarning($"不支持自动设置本机DNS,手工添加{IPAddress.Loopback}做为/etc/resolv.conf的第一条记录");
+ }
else
{
- this.logger.LogWarning($"不支持自动设置DNS,请根据你的系统平台情况修改主DNS为{IPAddress.Loopback}");
+ this.logger.LogWarning($"不支持自动设置本机DNS,请手工添加{IPAddress.Loopback}做为连接网络的DNS的第一条记录");
}
foreach (var item in this.dnsValidators)
diff --git a/FastGithub.DomainResolve/TomlUtil.cs b/FastGithub.DomainResolve/TomlUtil.cs
index 97541b5..0b9f380 100644
--- a/FastGithub.DomainResolve/TomlUtil.cs
+++ b/FastGithub.DomainResolve/TomlUtil.cs
@@ -20,7 +20,7 @@ namespace FastGithub.DomainResolve
///
public static Task SetListensAsync(string tomlPath, IPEndPoint endpoint, CancellationToken cancellationToken = default)
{
- return SetAsync(tomlPath, "listen_addresses", $"['{endpoint}']");
+ return SetAsync(tomlPath, "listen_addresses", $"['{endpoint}']", cancellationToken);
}
///
diff --git a/FastGithub.ReverseProxy/CertService.cs b/FastGithub.ReverseProxy/CertService.cs
new file mode 100644
index 0000000..75a1656
--- /dev/null
+++ b/FastGithub.ReverseProxy/CertService.cs
@@ -0,0 +1,165 @@
+using FastGithub.Configuration;
+using Microsoft.Extensions.Caching.Memory;
+using Microsoft.Extensions.Logging;
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Security.Cryptography.X509Certificates;
+
+namespace FastGithub.ReverseProxy
+{
+ ///
+ /// 证书服务
+ ///
+ sealed class CertService
+ {
+ private const string CAPATH = "CACert";
+ private readonly IMemoryCache serverCertCache;
+ private readonly ILogger logger;
+
+ ///
+ /// 私钥长度
+ ///
+ public int KeySizeBits { get; } = 2048;
+
+ ///
+ /// 获取证书文件路径
+ ///
+ public string CaCerFilePath { get; } = $"{CAPATH}/{nameof(FastGithub)}.cer";
+
+ ///
+ /// 获取私钥文件路径
+ ///
+ public string CaKeyFilePath { get; } = $"{CAPATH}/{nameof(FastGithub)}.key";
+
+ ///
+ /// 证书服务
+ ///
+ ///
+ public CertService(
+ IMemoryCache serverCertCache,
+ ILogger logger)
+ {
+ this.serverCertCache = serverCertCache;
+ this.logger = logger;
+
+ Directory.CreateDirectory(CAPATH);
+ }
+
+ ///
+ /// 获取颁发给指定域名的证书
+ ///
+ ///
+ ///
+ public X509Certificate2 GetServerCert(string? domain)
+ {
+ var key = $"{nameof(CertService)}:{domain}";
+ return this.serverCertCache.GetOrCreate(key, GetOrCreateCert);
+
+ // 生成域名的1年证书
+ X509Certificate2 GetOrCreateCert(ICacheEntry entry)
+ {
+ var domains = GetDomains(domain).Distinct();
+ var validFrom = DateTime.Today.AddDays(-1);
+ var validTo = DateTime.Today.AddYears(1);
+
+ entry.SetAbsoluteExpiration(validTo);
+ return CertGenerator.GenerateByCa(domains, this.KeySizeBits, validFrom, validTo, this.CaCerFilePath, this.CaKeyFilePath);
+ }
+ }
+
+ ///
+ /// 获取域名
+ ///
+ ///
+ ///
+ private static IEnumerable GetDomains(string? domain)
+ {
+ if (string.IsNullOrEmpty(domain) == false)
+ {
+ yield return domain;
+ yield break;
+ }
+
+ yield return LocalMachine.Name;
+ foreach (var address in LocalMachine.GetAllIPv4Addresses())
+ {
+ yield return address.ToString();
+ }
+ }
+
+ ///
+ /// 生成10年的根证书
+ ///
+ public bool GenerateCaCert()
+ {
+ if (File.Exists(this.CaCerFilePath) && File.Exists(this.CaKeyFilePath))
+ {
+ return false;
+ }
+
+ File.Delete(this.CaCerFilePath);
+ File.Delete(this.CaKeyFilePath);
+
+ var validFrom = DateTime.Today.AddDays(-1);
+ var validTo = DateTime.Today.AddYears(10);
+ CertGenerator.GenerateBySelf(new[] { nameof(FastGithub) }, this.KeySizeBits, validFrom, validTo, this.CaCerFilePath, this.CaKeyFilePath);
+ return true;
+ }
+
+ ///
+ /// 安装根证书
+ ///
+ public void InstallCaCert()
+ {
+ if (OperatingSystem.IsWindows())
+ {
+ this.InstallCaCertAtWindows();
+ }
+ else if (OperatingSystem.IsLinux())
+ {
+ this.logger.LogWarning($"不支持自动安装证书{this.CaCerFilePath}:请手工安装证书然后设置信任证书");
+ }
+ else if (OperatingSystem.IsMacOS())
+ {
+ this.logger.LogWarning($"不支持自动安装证书{this.CaCerFilePath}:请手工安装证书然后设置信任证书");
+ }
+ else
+ {
+ this.logger.LogWarning($"不支持自动安装证书{this.CaCerFilePath}:请根据你的系统平台手工安装和信任证书");
+ }
+ }
+
+ ///
+ /// 安装根证书
+ ///
+ private void InstallCaCertAtWindows()
+ {
+ try
+ {
+ using var store = new X509Store(StoreName.Root, StoreLocation.LocalMachine);
+ store.Open(OpenFlags.ReadWrite);
+
+ var caCert = new X509Certificate2(this.CaCerFilePath);
+ var subjectName = caCert.Subject[3..];
+ foreach (var item in store.Certificates.Find(X509FindType.FindBySubjectName, subjectName, false))
+ {
+ if (item.Thumbprint != caCert.Thumbprint)
+ {
+ store.Remove(item);
+ }
+ }
+ if (store.Certificates.Find(X509FindType.FindByThumbprint, caCert.Thumbprint, true).Count == 0)
+ {
+ store.Add(caCert);
+ }
+ store.Close();
+ }
+ catch (Exception ex)
+ {
+ this.logger.LogWarning($"安装证书{this.CaCerFilePath}失败:请手动安装到“将所有的证书都放入下载存储”\\“受信任的根证书颁发机构”", ex);
+ }
+ }
+ }
+}
diff --git a/FastGithub.ReverseProxy/KestrelServerOptionsExtensions.cs b/FastGithub.ReverseProxy/KestrelServerOptionsExtensions.cs
index 77a50dd..aeb380e 100644
--- a/FastGithub.ReverseProxy/KestrelServerOptionsExtensions.cs
+++ b/FastGithub.ReverseProxy/KestrelServerOptionsExtensions.cs
@@ -2,17 +2,12 @@
using FastGithub.ReverseProxy;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Server.Kestrel.Core;
-using Microsoft.Extensions.Caching.Memory;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
-using Microsoft.Extensions.Options;
using System;
-using System.Collections.Generic;
-using System.IO;
using System.Linq;
using System.Net;
using System.Net.NetworkInformation;
-using System.Security.Cryptography.X509Certificates;
namespace FastGithub
{
@@ -21,20 +16,12 @@ namespace FastGithub
///
public static class KestrelServerOptionsExtensions
{
- ///
- /// 服务器证书缓存
- ///
- private static readonly IMemoryCache serverCertCache = new MemoryCache(Options.Create(new MemoryCacheOptions()));
-
///
/// 监听http的反向代理
///
///
public static void ListenHttpReverseProxy(this KestrelServerOptions kestrel)
{
- var loggerFactory = kestrel.ApplicationServices.GetRequiredService();
- var logger = loggerFactory.CreateLogger($"{nameof(FastGithub)}.{nameof(ReverseProxy)}");
-
const int HTTP_PORT = 80;
if (OperatingSystem.IsWindows())
{
@@ -43,7 +30,9 @@ namespace FastGithub
if (CanTcpListen(HTTP_PORT) == false)
{
- logger.LogWarning($"由于tcp端口{HTTP_PORT}已经被其它进程占用,{nameof(FastGithub)}无法进行http反向代理");
+ var loggerFactory = kestrel.ApplicationServices.GetRequiredService();
+ var logger = loggerFactory.CreateLogger($"{nameof(FastGithub)}.{nameof(ReverseProxy)}");
+ logger.LogWarning($"由于tcp端口{HTTP_PORT}已经被其它进程占用,http反向代理功能将受限");
}
else
{
@@ -57,17 +46,6 @@ namespace FastGithub
///
public static void ListenHttpsReverseProxy(this KestrelServerOptions kestrel)
{
- var loggerFactory = kestrel.ApplicationServices.GetRequiredService();
- var logger = loggerFactory.CreateLogger($"{nameof(FastGithub)}.{nameof(ReverseProxy)}");
-
- const string CAPATH = "CACert";
- Directory.CreateDirectory(CAPATH);
- var caPublicCerPath = $"{CAPATH}/{nameof(FastGithub)}.cer";
- var caPrivateKeyPath = $"{CAPATH}/{nameof(FastGithub)}.key";
-
- GeneratorCaCert(caPublicCerPath, caPrivateKeyPath);
- InstallCaCert(caPublicCerPath, logger);
-
const int HTTPS_PORT = 443;
if (OperatingSystem.IsWindows())
{
@@ -76,15 +54,17 @@ namespace FastGithub
if (CanTcpListen(HTTPS_PORT) == false)
{
- logger.LogError($"由于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)));
+ throw new FastGithubException($"由于tcp端口{HTTPS_PORT}已经被其它进程占用,{nameof(FastGithub)}无法进行必须的https反向代理");
}
+
+ var certService = kestrel.ApplicationServices.GetRequiredService();
+ certService.GenerateCaCert();
+ certService.InstallCaCert();
+
+ kestrel.Listen(IPAddress.Any, HTTPS_PORT, listen =>
+ listen.UseHttps(https =>
+ https.ServerCertificateSelector = (ctx, domain) =>
+ certService.GetServerCert(domain)));
}
///
@@ -97,110 +77,5 @@ namespace FastGithub
var tcpListeners = IPGlobalProperties.GetIPGlobalProperties().GetActiveTcpListeners();
return tcpListeners.Any(item => item.Port == port) == false;
}
-
- ///
- /// 生成根证书
- /// 10年
- ///
- ///
- ///
- private static void GeneratorCaCert(string caPublicCerPath, string caPrivateKeyPath)
- {
- if (File.Exists(caPublicCerPath) && File.Exists(caPublicCerPath))
- {
- return;
- }
-
- File.Delete(caPublicCerPath);
- File.Delete(caPrivateKeyPath);
-
- var validFrom = DateTime.Today.AddDays(-1);
- var validTo = DateTime.Today.AddYears(10);
- CertGenerator.GenerateBySelf(new[] { nameof(FastGithub) }, 2048, validFrom, validTo, caPublicCerPath, caPrivateKeyPath);
- }
-
-
- ///
- /// 安装根证书
- ///
- ///
- ///
- private static void InstallCaCert(string caPublicCerPath, ILogger logger)
- {
- if (OperatingSystem.IsWindows() == false)
- {
- logger.LogWarning($"不支持自动安装证书{caPublicCerPath}:请手动安装证书到根证书颁发机构");
- return;
- }
-
- try
- {
- using var store = new X509Store(StoreName.Root, StoreLocation.LocalMachine);
- store.Open(OpenFlags.ReadWrite);
-
- var caCert = new X509Certificate2(caPublicCerPath);
- var subjectName = caCert.Subject[3..];
- foreach (var item in store.Certificates.Find(X509FindType.FindBySubjectName, subjectName, false))
- {
- if (item.Thumbprint != caCert.Thumbprint)
- {
- store.Remove(item);
- }
- }
- if (store.Certificates.Find(X509FindType.FindByThumbprint, caCert.Thumbprint, true).Count == 0)
- {
- store.Add(caCert);
- }
- store.Close();
- }
- catch (Exception)
- {
- logger.LogWarning($"安装证书{caPublicCerPath}失败:请手动安装到“将所有的证书都放入下载存储”\\“受信任的根证书颁发机构”");
- }
- }
-
- ///
- /// 获取颁发给指定域名的证书
- ///
- ///
- ///
- ///
- ///
- private static X509Certificate2 GetServerCert(string? domain, string caPublicCerPath, string caPrivateKeyPath)
- {
- return serverCertCache.GetOrCreate(domain ?? string.Empty, GetOrCreateCert);
-
- // 生成域名的1年证书
- X509Certificate2 GetOrCreateCert(ICacheEntry entry)
- {
- var host = (string)entry.Key;
- var domains = GetDomains(host).Distinct();
- var validFrom = DateTime.Today.AddDays(-1);
- var validTo = DateTime.Today.AddYears(1);
-
- entry.SetAbsoluteExpiration(validTo);
- return CertGenerator.GenerateByCa(domains, 2048, validFrom, validTo, caPublicCerPath, caPrivateKeyPath);
- }
- }
-
- ///
- /// 获取域名
- ///
- ///
- ///
- private static IEnumerable GetDomains(string host)
- {
- if (string.IsNullOrEmpty(host) == false)
- {
- yield return host;
- yield break;
- }
-
- yield return LocalMachine.Name;
- foreach (var address in LocalMachine.GetAllIPv4Addresses())
- {
- yield return address.ToString();
- }
- }
}
}
diff --git a/FastGithub.ReverseProxy/ReverseProxyServiceCollectionExtensions.cs b/FastGithub.ReverseProxy/ReverseProxyServiceCollectionExtensions.cs
index 069a745..1e1bddd 100644
--- a/FastGithub.ReverseProxy/ReverseProxyServiceCollectionExtensions.cs
+++ b/FastGithub.ReverseProxy/ReverseProxyServiceCollectionExtensions.cs
@@ -18,6 +18,7 @@ namespace FastGithub
return services
.AddMemoryCache()
.AddHttpForwarder()
+ .AddSingleton()
.AddSingleton()
.AddSingleton();
}