瀏覽代碼

增加http反向代理

陈国伟 4 年之前
父節點
當前提交
ae6b0d7260

+ 2 - 3
FastGithub.Dns/DnsServiceCollectionExtensions.cs

@@ -18,10 +18,9 @@ namespace FastGithub
         public static IServiceCollection AddGithubDns(this IServiceCollection services, IConfiguration configuration)
         {
             var assembly = typeof(DnsServiceCollectionExtensions).Assembly;
-            return services          
+            return services
                 .AddServiceAndOptions(assembly, configuration)
-                .AddHostedService<DnsHostedService>()
-                .AddGithubScanner(configuration);
+                .AddHostedService<DnsHostedService>();
         }
     }
 }

+ 1 - 5
FastGithub.Dns/FastGithub.Dns.csproj

@@ -2,11 +2,7 @@
 
 	<PropertyGroup>
 		<TargetFramework>net5.0</TargetFramework>
-	</PropertyGroup>
-
-	<ItemGroup>
-	  <None Remove="DnsOptions.cs~RF5dfc81de.TMP" />
-	</ItemGroup>
+	</PropertyGroup> 
 
 	<ItemGroup>
 		<ProjectReference Include="..\FastGithub.Scanner\FastGithub.Scanner.csproj" />

+ 2 - 0
FastGithub.Dns/GithubRequestResolver.cs

@@ -6,6 +6,7 @@ using Microsoft.Extensions.DependencyInjection;
 using Microsoft.Extensions.Logging;
 using Microsoft.Extensions.Options;
 using System.Linq;
+using System.Net;
 using System.Threading;
 using System.Threading.Tasks;
 
@@ -55,6 +56,7 @@ namespace FastGithub.Dns
 
                 if (address != null)
                 {
+                    address = IPAddress.Loopback;
                     var ttl = this.options.CurrentValue.GithubTTL;
                     var record = new IPAddressResourceRecord(question.Name, address, ttl);
                     response.AnswerRecords.Add(record);

+ 159 - 0
FastGithub.ReverseProxy/CertGenerator.cs

@@ -0,0 +1,159 @@
+using Org.BouncyCastle.Asn1;
+using Org.BouncyCastle.Asn1.Pkcs;
+using Org.BouncyCastle.Asn1.X509;
+using Org.BouncyCastle.Asn1.X9;
+using Org.BouncyCastle.Crypto;
+using Org.BouncyCastle.Crypto.Generators;
+using Org.BouncyCastle.Crypto.Operators;
+using Org.BouncyCastle.Crypto.Parameters;
+using Org.BouncyCastle.Math;
+using Org.BouncyCastle.OpenSsl;
+using Org.BouncyCastle.Pkcs;
+using Org.BouncyCastle.Security;
+using Org.BouncyCastle.X509;
+using Org.BouncyCastle.X509.Extension;
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Text;
+using X509Certificate2 = System.Security.Cryptography.X509Certificates.X509Certificate2;
+
+namespace FastGithub.ReverseProxy
+{
+    /// <summary>
+    /// 证书生成器
+    /// </summary>
+    static class CertGenerator
+    {
+        private static readonly SecureRandom secureRandom = new();
+
+        /// <summary>
+        /// 生成CA签名证书
+        /// </summary>
+        /// <param name="domains"></param>
+        /// <param name="keySizeBits"></param>
+        /// <param name="validFrom"></param>
+        /// <param name="validTo"></param>
+        /// <param name="caPublicCerPath"></param>
+        /// <param name="caPrivateKeyPath"></param>
+        /// <returns></returns>
+        public static X509Certificate2 Generate(IEnumerable<string> domains, int keySizeBits, DateTime validFrom, DateTime validTo, string caPublicCerPath, string caPrivateKeyPath)
+        {
+            if (File.Exists(caPublicCerPath) == false)
+            {
+                throw new FileNotFoundException(caPublicCerPath);
+            }
+
+            if (File.Exists(caPrivateKeyPath) == false)
+            {
+                throw new FileNotFoundException(caPublicCerPath);
+            }
+
+            using var pubReader = new StreamReader(caPublicCerPath, Encoding.ASCII);
+            var caCert = (X509Certificate)new PemReader(pubReader).ReadObject();
+
+            using var priReader = new StreamReader(caPrivateKeyPath, Encoding.ASCII);
+            var reader = new PemReader(priReader);
+            var caPrivateKey = ((AsymmetricCipherKeyPair)reader.ReadObject()).Private;
+
+            var caSubjectName = GetSubjectName(caCert);
+            var keys = GenerateRsaKeyPair(keySizeBits);
+            var cert = GenerateCertificate(domains, keys.Public, validFrom, validTo, caSubjectName, caCert.GetPublicKey(), caPrivateKey, null);
+
+            return GeneratePfx(cert, keys.Private, password: null);
+        }
+
+        /// <summary>
+        /// 生成私钥
+        /// </summary>
+        /// <param name="length"></param>
+        /// <returns></returns>
+        private static AsymmetricCipherKeyPair GenerateRsaKeyPair(int length)
+        {
+            var keygenParam = new KeyGenerationParameters(secureRandom, length);
+            var keyGenerator = new RsaKeyPairGenerator();
+            keyGenerator.Init(keygenParam);
+            return keyGenerator.GenerateKeyPair();
+        }
+
+        /// <summary>
+        /// 生成证书
+        /// </summary>
+        /// <param name="domains"></param>
+        /// <param name="subjectPublic"></param>
+        /// <param name="validFrom"></param>
+        /// <param name="validTo"></param>
+        /// <param name="issuerName"></param>
+        /// <param name="issuerPublic"></param>
+        /// <param name="issuerPrivate"></param>
+        /// <param name="CA_PathLengthConstraint"></param>
+        /// <returns></returns>
+        private static X509Certificate GenerateCertificate(IEnumerable<string> domains, AsymmetricKeyParameter subjectPublic, DateTime validFrom, DateTime validTo, string issuerName, AsymmetricKeyParameter issuerPublic, AsymmetricKeyParameter issuerPrivate, int? CA_PathLengthConstraint)
+        {
+            var signatureFactory = issuerPrivate is ECPrivateKeyParameters
+                ? new Asn1SignatureFactory(X9ObjectIdentifiers.ECDsaWithSha256.ToString(), issuerPrivate)
+                : new Asn1SignatureFactory(PkcsObjectIdentifiers.Sha256WithRsaEncryption.ToString(), issuerPrivate);
+
+            var certGenerator = new X509V3CertificateGenerator();
+            certGenerator.SetIssuerDN(new X509Name("CN=" + issuerName));
+            certGenerator.SetSubjectDN(new X509Name("CN=" + domains.First()));
+            certGenerator.SetSerialNumber(BigInteger.ProbablePrime(120, new Random()));
+            certGenerator.SetNotBefore(validFrom);
+            certGenerator.SetNotAfter(validTo);
+            certGenerator.SetPublicKey(subjectPublic);
+
+            if (issuerPublic != null)
+            {
+                var akis = new AuthorityKeyIdentifierStructure(issuerPublic);
+                certGenerator.AddExtension(X509Extensions.AuthorityKeyIdentifier, false, akis);
+            }
+            if (CA_PathLengthConstraint != null && CA_PathLengthConstraint >= 0)
+            {
+                var extension = new X509Extension(true, new DerOctetString(new BasicConstraints(CA_PathLengthConstraint.Value)));
+                certGenerator.AddExtension(X509Extensions.BasicConstraints, extension.IsCritical, extension.GetParsedValue());
+            }
+
+            var names = domains.Select(domain => new GeneralName(GeneralName.DnsName, domain)).ToArray();
+            var subjectAltName = new GeneralNames(names);
+            certGenerator.AddExtension(X509Extensions.SubjectAlternativeName, false, subjectAltName);
+            return certGenerator.Generate(signatureFactory);
+        }
+
+        /// <summary>
+        /// 生成pfx
+        /// </summary>
+        /// <param name="cert"></param>
+        /// <param name="privateKey"></param>
+        /// <param name="password"></param>
+        /// <returns></returns>
+        private static X509Certificate2 GeneratePfx(X509Certificate cert, AsymmetricKeyParameter privateKey, string? password)
+        {
+            var subject = GetSubjectName(cert);
+            var pkcs12Store = new Pkcs12Store();
+            var certEntry = new X509CertificateEntry(cert);
+            pkcs12Store.SetCertificateEntry(subject, certEntry);
+            pkcs12Store.SetKeyEntry(subject, new AsymmetricKeyEntry(privateKey), new[] { certEntry });
+
+            using var pfxStream = new MemoryStream();
+            pkcs12Store.Save(pfxStream, password?.ToCharArray(), secureRandom);
+            return new X509Certificate2(pfxStream.ToArray());
+        }
+
+
+        /// <summary>
+        /// 获取Subject
+        /// </summary>
+        /// <param name="cert"></param>
+        /// <returns></returns>
+        private static string GetSubjectName(X509Certificate cert)
+        {
+            var subject = cert.SubjectDN.ToString();
+            if (subject.StartsWith("cn=", StringComparison.OrdinalIgnoreCase))
+            {
+                subject = subject[3..];
+            }
+            return subject;
+        }
+    }
+}

+ 17 - 0
FastGithub.ReverseProxy/FastGithub.ReverseProxy.csproj

@@ -0,0 +1,17 @@
+<Project Sdk="Microsoft.NET.Sdk">
+
+  <PropertyGroup>
+    <TargetFramework>net5.0</TargetFramework>
+  </PropertyGroup>
+  
+  <ItemGroup>
+    <FrameworkReference Include="Microsoft.AspNetCore.App" />
+    <PackageReference Include="Portable.BouncyCastle" Version="1.8.10" /> 
+    <PackageReference Include="Yarp.ReverseProxy" Version="1.0.0-preview.12.21328.2" />
+  </ItemGroup>
+  
+  <ItemGroup>
+    <ProjectReference Include="..\FastGithub.Scanner\FastGithub.Scanner.csproj" />
+  </ItemGroup>
+
+</Project>

+ 55 - 0
FastGithub.ReverseProxy/GithubDnsHttpHandler.cs

@@ -0,0 +1,55 @@
+using FastGithub.Scanner;
+using Microsoft.Extensions.DependencyInjection;
+using System;
+using System.Net.Http;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace FastGithub.ReverseProxy
+{
+    /// <summary>
+    /// Github的dns解析的httpHandler
+    /// 使扫描索结果作为github的https请求的域名解析
+    /// </summary> 
+    sealed class GithubDnsHttpHandler : DelegatingHandler
+    {
+        private readonly IGithubScanResults scanResults;
+
+        /// <summary>
+        /// Github的dns解析的httpHandler
+        /// </summary>
+        /// <param name="scanResults"></param>
+        /// <param name="handler"></param>
+        public GithubDnsHttpHandler(IGithubScanResults scanResults, HttpMessageHandler handler)
+            : base(handler)
+        {
+            this.scanResults = scanResults;
+        }
+
+        /// <summary>
+        /// 发送消息
+        /// </summary>
+        /// <param name="request"></param>
+        /// <param name="cancellationToken"></param>
+        /// <returns></returns>
+        protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
+        {
+            var uri = request.RequestUri;
+            if (uri != null && uri.HostNameType == UriHostNameType.Dns)
+            {
+                var address = this.scanResults.FindBestAddress(uri.Host);
+                if (address != null)
+                {
+                    var builder = new UriBuilder(uri)
+                    {
+                        Host = address.ToString()
+                    };
+                    request.RequestUri = builder.Uri;
+                    request.Headers.Host = uri.Host;
+                }
+            }
+
+            return await base.SendAsync(request, cancellationToken);
+        }
+    }
+}

+ 36 - 0
FastGithub.ReverseProxy/ListenOptionsHttpsExtensions.cs

@@ -0,0 +1,36 @@
+using FastGithub.ReverseProxy;
+using Microsoft.AspNetCore.Hosting;
+using Microsoft.AspNetCore.Server.Kestrel.Core;
+using System;
+using System.Collections.Concurrent;
+using System.Security.Cryptography.X509Certificates;
+
+namespace FastGithub
+{
+    public static class ListenOptionsHttpsExtensions
+    {
+        /// <summary>
+        /// 应用fastGihub的https
+        /// </summary>
+        /// <param name="listenOptions"></param>
+        /// <param name="caPublicCerPath"></param>
+        /// <param name="caPrivateKeyPath"></param>
+        /// <returns></returns>
+        public static ListenOptions UseGithubHttps(this ListenOptions listenOptions, string caPublicCerPath, string caPrivateKeyPath)
+        {
+            return listenOptions.UseHttps(https =>
+            {
+                var certs = new ConcurrentDictionary<string, X509Certificate2>();
+                https.ServerCertificateSelector = (ctx, domain) =>
+                    certs.GetOrAdd(domain, d =>
+                        CertGenerator.Generate(
+                            new[] { d },
+                            2048,
+                            DateTime.Today.AddYears(-1),
+                            DateTime.Today.AddYears(1),
+                            caPublicCerPath,
+                            caPrivateKeyPath));
+            });
+        }
+    }
+}

+ 62 - 0
FastGithub.ReverseProxy/NoneSniHttpClient.cs

@@ -0,0 +1,62 @@
+using FastGithub.Scanner;
+using System.IO;
+using System.Net.Http;
+using System.Net.Security;
+using System.Net.Sockets;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace FastGithub.ReverseProxy
+{
+    /// <summary>
+    /// 去掉Sni的HttpClient
+    /// </summary> 
+    sealed class NoneSniHttpClient : HttpMessageInvoker
+    {
+        /// <summary>
+        /// 去掉Sni的HttpClient
+        /// </summary>
+        /// <param name="githubScanResults"></param>
+        public NoneSniHttpClient(IGithubScanResults githubScanResults)
+            : base(CreateNoneSniHttpHandler(githubScanResults), disposeHandler: false)
+        {
+        }
+
+        /// <summary>
+        /// 去掉Sni的HttpHandler
+        /// </summary>  
+        private static HttpMessageHandler CreateNoneSniHttpHandler(IGithubScanResults githubScanResults)
+        {
+            var httpHandler = new SocketsHttpHandler
+            {
+                AllowAutoRedirect = false,
+                UseCookies = false,
+                UseProxy = false,
+                ConnectCallback = ConnectCallback
+            };
+
+            return new GithubDnsHttpHandler(githubScanResults, httpHandler);
+        }
+
+        /// <summary>
+        /// 连接回调
+        /// </summary>
+        /// <param name="context"></param>
+        /// <param name="cancellationToken"></param>
+        /// <returns></returns>
+        private static async ValueTask<Stream> ConnectCallback(SocketsHttpConnectionContext context, CancellationToken cancellationToken)
+        {
+            var socket = new Socket(SocketType.Stream, ProtocolType.Tcp);
+            await socket.ConnectAsync(context.DnsEndPoint, cancellationToken);
+            var stream = new NetworkStream(socket, ownsSocket: true);
+            if (context.InitialRequestMessage.Headers.Host == null)
+            {
+                return stream;
+            }
+
+            var sslStream = new SslStream(stream, leaveInnerStreamOpen: false, delegate { return true; });
+            await sslStream.AuthenticateAsClientAsync(string.Empty, null, false);
+            return sslStream;
+        }
+    }
+}

+ 34 - 0
FastGithub.ReverseProxy/ReverseProxyApplicationBuilderExtensions.cs

@@ -0,0 +1,34 @@
+using FastGithub.ReverseProxy;
+using Microsoft.AspNetCore.Builder;
+using Microsoft.Extensions.DependencyInjection;
+using Yarp.ReverseProxy.Forwarder;
+
+namespace FastGithub
+{
+    /// <summary>
+    /// gitub反向代理的中间件扩展
+    /// </summary>
+    public static class ReverseProxyApplicationBuilderExtensions
+    {
+        /// <summary>
+        /// 使用gitub反向代理中间件
+        /// </summary>
+        /// <param name="app"></param>
+        /// <returns></returns>
+        public static IApplicationBuilder UseGithubReverseProxy(this IApplicationBuilder app)
+        {
+            var httpForwarder = app.ApplicationServices.GetRequiredService<IHttpForwarder>();
+            var httpClient = app.ApplicationServices.GetRequiredService<NoneSniHttpClient>();
+
+            app.Use(next => async context =>
+            {
+                var hostString = context.Request.Host;
+                var port = hostString.Port ?? 443;
+                var destinationPrefix = $"http://{hostString.Host}:{port}/";
+                await httpForwarder.SendAsync(context, destinationPrefix, httpClient);
+            });
+
+            return app;
+        }
+    }
+}

+ 24 - 0
FastGithub.ReverseProxy/ReverseProxyServiceCollectionExtensions.cs

@@ -0,0 +1,24 @@
+using FastGithub.ReverseProxy;
+using Microsoft.Extensions.Configuration;
+using Microsoft.Extensions.DependencyInjection;
+
+namespace FastGithub
+{
+    /// <summary>
+    /// gitub反向代理的服务注册扩展
+    /// </summary>
+    public static class ReverseProxyServiceCollectionExtensions
+    {
+        /// <summary>
+        /// gitub反向代理
+        /// </summary>
+        /// <param name="services"></param> 
+        /// <returns></returns>
+        public static IServiceCollection AddGithubReverseProxy(this IServiceCollection services)
+        {
+            services.AddHttpForwarder();
+            services.AddSingleton<NoneSniHttpClient>();
+            return services;
+        }
+    }
+}

+ 1 - 0
FastGithub.Scanner/ScannerServiceCollectionExtensions.cs

@@ -30,6 +30,7 @@ namespace FastGithub
                 .SetHandlerLifetime(TimeSpan.FromMinutes(5d))
                 .ConfigureHttpClient(httpClient =>
                 {
+                    httpClient.Timeout = TimeSpan.FromSeconds(10d);
                     httpClient.DefaultRequestHeaders.Accept.TryParseAdd("*/*");
                     httpClient.DefaultRequestHeaders.UserAgent.Add(defaultUserAgent);
                 })

+ 7 - 1
FastGithub.sln

@@ -11,7 +11,9 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FastGithub.Dns", "FastGithu
 EndProject
 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FastGithub.Scanner", "FastGithub.Scanner\FastGithub.Scanner.csproj", "{7F24CD2F-07C0-4002-A534-80688DE95ECF}"
 EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FastGithub.Upgrade", "FastGithub.Upgrade\FastGithub.Upgrade.csproj", "{8239A077-A84C-4FDF-A204-02A2DE4243F3}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FastGithub.Upgrade", "FastGithub.Upgrade\FastGithub.Upgrade.csproj", "{8239A077-A84C-4FDF-A204-02A2DE4243F3}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FastGithub.ReverseProxy", "FastGithub.ReverseProxy\FastGithub.ReverseProxy.csproj", "{28326D0F-B0FB-4B6B-A65A-C69ACB72CAD8}"
 EndProject
 Global
 	GlobalSection(SolutionConfigurationPlatforms) = preSolution
@@ -39,6 +41,10 @@ Global
 		{8239A077-A84C-4FDF-A204-02A2DE4243F3}.Debug|Any CPU.Build.0 = Debug|Any CPU
 		{8239A077-A84C-4FDF-A204-02A2DE4243F3}.Release|Any CPU.ActiveCfg = Release|Any CPU
 		{8239A077-A84C-4FDF-A204-02A2DE4243F3}.Release|Any CPU.Build.0 = Release|Any CPU
+		{28326D0F-B0FB-4B6B-A65A-C69ACB72CAD8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{28326D0F-B0FB-4B6B-A65A-C69ACB72CAD8}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{28326D0F-B0FB-4B6B-A65A-C69ACB72CAD8}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{28326D0F-B0FB-4B6B-A65A-C69ACB72CAD8}.Release|Any CPU.Build.0 = Release|Any CPU
 	EndGlobalSection
 	GlobalSection(SolutionProperties) = preSolution
 		HideSolutionNode = FALSE

+ 8 - 1
FastGithub/FastGithub.csproj

@@ -1,4 +1,4 @@
-<Project Sdk="Microsoft.NET.Sdk">
+<Project Sdk="Microsoft.NET.Sdk.Web">
 
 	<PropertyGroup>
 		<OutputType>Exe</OutputType>
@@ -19,6 +19,7 @@
 		<PackageReference Include="PInvoke.AdvApi32" Version="0.7.104" />
 		<PackageReference Include="Microsoft.Extensions.Hosting.WindowsServices" Version="5.0.1" />
 		<ProjectReference Include="..\FastGithub.Dns\FastGithub.Dns.csproj" />
+		<ProjectReference Include="..\FastGithub.ReverseProxy\FastGithub.ReverseProxy.csproj" />
 		<ProjectReference Include="..\FastGithub.Upgrade\FastGithub.Upgrade.csproj" />
 	</ItemGroup>
 
@@ -32,6 +33,12 @@
 		<None Update="appsettings.json">
 			<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
 		</None>
+		<None Update="FastGithub_CA.cer">
+		  <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+		</None>
+		<None Update="FastGithub_CA.key">
+		  <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+		</None>
 		<None Update="README.MD">
 			<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
 		</None>

+ 18 - 0
FastGithub/FastGithub_CA.cer

@@ -0,0 +1,18 @@
+-----BEGIN CERTIFICATE-----
+MIIC2DCCAcCgAwIBAgIQAKaUqwPmF9dDFtqNp34mRTANBgkqhkiG9w0BAQsFADAY
+MRYwFAYDVQQDDA1GYXN0R2l0aHViX0NBMCAXDTExMDcxMzAwMDAwMFoYDzI1MjEw
+NzEzMDAwMDAwWjAYMRYwFAYDVQQDDA1GYXN0R2l0aHViX0NBMIIBIjANBgkqhkiG
+9w0BAQEFAAOCAQ8AMIIBCgKCAQEA1vNFkTGacoKdOY+H8Va7BN/z+nXyUHI1aZGC
+11uzRVggi7EDxL2ThufzrQNEMqmh7aI6UbmnFMee+UtzexBSl2x8sH0uOPL31Pbh
+fsrrgjMW0p8doApaTlEVyrXI5SVEapf+B13y/Nu6e2PQ4gRT4WZaBfLgqQdcaT9/
+RdaexznlRYzet6HAKf/Hvs5tkbWkLY9mvctcMWm998wjVVD2vTyZ7Pe7s7L2w/fG
+mcz0svonr18zI+kHK/hK/2u/jvnmC8HpvhnDbNp7brOvR0TF6oYjKB20Zicy32UK
+0/PekVN9T8dzOwjkZXJ0xI7RRVLOLG1qkO8z8BhyznjQLLZSLwIDAQABoxwwGjAY
+BgNVHREEETAPgg1GYXN0R2l0aHViX0NBMA0GCSqGSIb3DQEBCwUAA4IBAQCYR68q
+oKqBiNKlWgaaY8o2w7PGL0NZ2QVGlZp4Yl8Jj60qx587TSN9YTjMgNpZnkCYJZbQ
+AhJAcwVspFsAq90SG/md0A6o3TRHSEV2HJIvAoMTiT/LLG+ZU61/NxMl0WxoQPKz
+OcleOo+fCklove9jYIhHsls30eQv/NGn+pKhnCI9VEC+sUxbxQd4LJQbBYouNV6I
+Sd8axEazqWsQrfX+CvNb2UjH3aaARaPWcacTcVlO5XJz2eDnUzVQOWwgv57sLUmY
+xn0HbGp4lATgNeUSBcn7pEHv/yjqraJn2cVl0l/ZaFcYcccYZFrA3qAi14aiGRtu
+SSC6OWHrtXwXZnkn
+-----END CERTIFICATE-----

+ 27 - 0
FastGithub/FastGithub_CA.key

@@ -0,0 +1,27 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIIEpAIBAAKCAQEA1vNFkTGacoKdOY+H8Va7BN/z+nXyUHI1aZGC11uzRVggi7ED
+xL2ThufzrQNEMqmh7aI6UbmnFMee+UtzexBSl2x8sH0uOPL31PbhfsrrgjMW0p8d
+oApaTlEVyrXI5SVEapf+B13y/Nu6e2PQ4gRT4WZaBfLgqQdcaT9/RdaexznlRYze
+t6HAKf/Hvs5tkbWkLY9mvctcMWm998wjVVD2vTyZ7Pe7s7L2w/fGmcz0svonr18z
+I+kHK/hK/2u/jvnmC8HpvhnDbNp7brOvR0TF6oYjKB20Zicy32UK0/PekVN9T8dz
+OwjkZXJ0xI7RRVLOLG1qkO8z8BhyznjQLLZSLwIDAQABAoIBABamHILviJgfRiTO
+CGN4IY2icWlHK0ipuBIPGIvLqEiawBcoCD8fQJ+66hSlXva9pAfPi7iXyNCqNgiL
+mDfz4Nf+wOax0gCDLXT9rIX5KLaX3oRD6tG1tY1CAvtQi0IF2r8mk8g/8H4PQweE
+XXqrPRFngP3WeTCmS2j1nVoFAsb4E7ZOKVVD3z6wILWHUfXuAeV/R1/pqbDvc4Yn
+i2+PZvHZVjftkOqy7FyNE18y2Q198TKWATV1WzrB/Tv0mVoQN/izM9HflERUvvMo
+v+I9ZTKrLgnHo5bHprVLRQnHfBJo/SDHJTCtP57sGElUaYl9nFM6k47k0Jp71zID
+2UrlY/kCgYEA+ZFD2L+q6y4nDXOngDUiRiLbRJDb2u5pOY6scRHa73KCvq+t+Bih
+fxtE9h4EDa1zei/tR6gkQnoIe9Nviikz2YyTrZkqHzAK55who9H6GT9pErNUvXF2
+dnje2KlRcjniMp8vcm1sbYMDEt+NFS2sL8rby97bZQ+V5WjLoYAhAacCgYEA3H2X
+IPDudA6cFPczn+19qw76nKdrc3LkxToYdscb/8tH6dX+yrt02t1Ezm81kcMcqeHd
+BzbxNeiN2NmBozLQ0SERMDkNJUUFlK3ALdx9USdwTTMywRMnkgLDuRaA6m1QDi24
+RoFDjhEQREbjRD4WxJuzgvLKhF60bQnsAf5R7DkCgYBDpWBii/FkNepX9xVb5wsX
+P7N3blxph132P/n13AUgCkXuMehR6zs7HMUggRpQKse2Qu9qEOVjL3jFN5ZwOKLZ
+QQV2dKG6Omd6SBPGN9A2r71nWDyL7QlTK4gb6iktcQsi9YsC1S4isPRQVVAEgZC+
+k5noNMv7JLJYsIMhj31i2QKBgQDbg9HVgujz9KOiH+Zuv4PQrQ4GrovEmct3K/q1
+LlAK33iOLnYHoo+ZYpehKojbwLOl0m86QpHtCMVH8mwlbW8F9fTl3LbgtxHyTvW4
+8v50sF4XDfTm0kogDM6NVAEu43vDUfNXhlQaeZSHVUfoZiRGw3j50vyawqrAsBMe
+fPNUyQKBgQCMLr1WLgnDiMx7CLXlmKdw45z0UeP+ngZOuPICX9d5DOfznuP8VgS9
+ZiwXHF8PbSdlMFPq9LkPbQTjfTLHXZx0mKl78PcFqwtryXTpUlP1qcfjE6Hl+POD
+2OkWyGz7vA80+7ilQscm0L/gLIgwiGQOdBv6akLF8qDwncOX4yWOVQ==
+-----END RSA PRIVATE KEY-----

+ 18 - 1
FastGithub/Program.cs

@@ -1,4 +1,5 @@
-using Microsoft.Extensions.Configuration;
+using Microsoft.AspNetCore.Hosting;
+using Microsoft.Extensions.Configuration;
 using Microsoft.Extensions.Hosting;
 using System.IO;
 
@@ -37,6 +38,22 @@ namespace FastGithub
                 {
                     services.AddAppUpgrade();
                     services.AddGithubDns(ctx.Configuration);
+                    services.AddGithubReverseProxy();
+                    services.AddGithubScanner(ctx.Configuration);
+                })
+                .ConfigureWebHostDefaults(web =>
+                {
+                    web.Configure(app =>
+                    {
+                        app.UseGithubReverseProxy();
+                    });
+
+                    web.UseKestrel(kestrel =>
+                    {
+                        const string caPublicCerPath = "FastGithub_CA.cer";
+                        const string caPrivateKeyPath = "FastGithub_CA.key";
+                        kestrel.ListenLocalhost(443, listen => listen.UseGithubHttps(caPublicCerPath, caPrivateKeyPath));
+                    });
                 });
         }
     }