负载均衡Elastic Beanstalk下ASP.NET Core HTTPS请求挂起问题排查
问题描述
将部署在Linux上的ASP.NET Core后端从单实例Elastic Beanstalk环境转为负载均衡模式,并配置了AWS证书管理器(ACM)颁发的HTTPS证书,但前端请求后端时一直处于挂起状态,超时后失败;切换回单实例模式则正常。
当前前端通过HTTP访问http://vepo-qa-env.eba-xxx.ap-southeast-2.elasticbeanstalk.com:5002可正常连接,但以HTTPS访问时出现混合内容拦截问题,因此需要将后端切换为HTTPS访问(如https://vepo-qa-env.eba-xxx.ap-southeast-2.elasticbeanstalk.com:5002或自定义域名https://vepo-qa.com)。
已在Route 53配置vepo-qa.com域名记录,添加了HTTPS监听器,但存在以下疑问:
- 后端代码中
webBuilder.UseUrls("http://*:5002")是否需要修改,是否要匹配监听器端口? - 仅为监听器分配证书是否足够,是否需要在后端添加私钥或配置.ebextensions文件?
- 前端请求地址应改为HTTPS的EB地址还是自定义域名,是否需要告知Elastic Beanstalk自定义域名?
另外还有两个问题:
- 当后端为HTTPS时,本地HTTP前端是否无法访问?
- 目前从Firebase HTTPS前端访问
https://vepo-qa.com时出现CORS错误。
Program.cs
namespace Vepo.Web { public class Program { public static void Main(string[] args) { CreateHostBuilder(args).Build().Run(); } public static IHostBuilder CreateHostBuilder(string[] args) => Host.CreateDefaultBuilder(args) .ConfigureWebHostDefaults(webBuilder => { webBuilder.UseStartup<Startup>(); webBuilder.UseUrls("http://*:5002"); }); } }
Startup.cs
namespace Vepo.Web { public class Startup { private class CustomTransformer : HttpTransformer { public override async ValueTask TransformRequestAsync(HttpContext httpContext, HttpRequestMessage proxyRequest, string destinationPrefix, CancellationToken cancellationToken) { await base.TransformRequestAsync(httpContext, proxyRequest, destinationPrefix, cancellationToken); var queryContext = new QueryTransformContext(httpContext.Request); proxyRequest.RequestUri = RequestUtilities.MakeDestinationAddress("https://maps.googleapis.com/maps/api", httpContext.Request.Path, queryContext.QueryString); proxyRequest.Headers.Host = null; } } public Startup(IConfiguration configuration, IWebHostEnvironment env) { Configuration = configuration; Env = env; } public IConfiguration Configuration { get; } public IWebHostEnvironment Env { get; } public void ConfigureServices(IServiceCollection services) { services.AddDbContext<VepoContext>(opt => opt.UseNpgsql(Configuration.GetConnectionString("DefaultConnection"), o => o.UseNetTopologySuite()) .EnableSensitiveDataLogging().EnableDetailedErrors().LogTo(Console.WriteLine)); services.AddHttpContextAccessor(); services.AddHttpForwarder(); services.AddTransient<UserResolverService>(); services.AddTransient<ExtendedVepoContext>(); services.AddControllers().AddJsonOptions(opt => { opt.JsonSerializerOptions.Converters.Add(new JsonStringEnumConverter()); opt.JsonSerializerOptions.Converters.Add(new GeoJsonConverterFactory()); }); services.AddControllers(config => { var policy = new AuthorizationPolicyBuilder().RequireAuthenticatedUser().Build(); config.Filters.Add(new AuthorizeFilter(policy)); }).AddNewtonsoftJson(x => { x.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore; x.SerializerSettings.NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore; }).AddJsonOptions(opt => { opt.JsonSerializerOptions.Converters.Add(new JsonStringEnumConverter()); }); if (Env.IsDevelopment()) { FirebaseApp.Create(new AppOptions() { Credential = GoogleCredential.FromFile("firebase_admin_sdk_development.json"), }, "vepo-dev-e5b8a"); } if (Env.IsQa()) { FirebaseApp.Create(new AppOptions() { Credential = GoogleCredential.FromFile("firebase_admin_sdk_qa.json"), }, "vepo-qa"); } services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme) .AddJwtBearer(options => { options.Authority = Configuration["FirebaseAuthority"]; options.TokenValidationParameters = new TokenValidationParameters { ValidateIssuer = true, ValidIssuer = Configuration["FirebaseAuthority"], ValidateAudience = true, ValidAudience = Configuration["FirebaseProjectId"], ValidateLifetime = true }; }); services.AddMvc().AddJsonOptions(options => { options.JsonSerializerOptions.ReferenceHandler = ReferenceHandler.Preserve; }); // 仓储与服务依赖注入配置 services.AddScoped(typeof(IGroceryItmEstsRepository), typeof(GroceryItmEstsRepository)); services.AddScoped(typeof(IAllVgnItmEstsRepository), typeof(AllVgnItmEstsRepository)); services.AddScoped(typeof(IEventItmEstsRepository), typeof(EventItmEstsRepository)); services.AddScoped(typeof(IFashionItmEstsRepository), typeof(FashionItmEstsRepository)); services.AddScoped(typeof(IRecipeItmEstsRepository), typeof(RecipeItmEstsRepository)); services.AddScoped(typeof(IGroceryStoreItmEstsRepository), typeof(GroceryStoreItmEstsRepository)); services.AddScoped(typeof(IRestaurantItmEstsRepository), typeof(RestaurantItmEstsRepository)); services.AddScoped(typeof(IUsersRepository), typeof(UsersRepository)); services.AddScoped(typeof(IBookMarksRepository), typeof(BookMarksRepository)); services.AddScoped(typeof(IGroceryItmsRepository), typeof(GroceryItmsRepository)); services.AddScoped(typeof(IEventItmsRepository), typeof(EventItmsRepository)); services.AddScoped(typeof(IFashionItmsRepository), typeof(FashionItmsRepository)); services.AddScoped(typeof(IRecipeItmsRepository), typeof(RecipeItmsRepository)); services.AddScoped(typeof(IGroceryStoreItmsRepository), typeof(GroceryStoreItmsRepository)); services.AddScoped(typeof(IRestaurantItmsRepository), typeof(RestaurantItmsRepository)); services.AddScoped(typeof(IMenuItmsRepository), typeof(MenuItmsRepository)); services.AddScoped<IUsersService, UsersService>(); services.AddScoped<IBookMarksService, BookMarksService>(); services.AddScoped<IMenuItmEstsService, MenuItmEstsService>(); services.AddScoped<IGroceryStoreItmEstsService, GroceryStoreItmEstsService>(); services.AddScoped<IRestaurantItmEstsService, RestaurantItmsService>(); services.AddScoped<IEventItmEstsService, EventItmEstsService>(); services.AddScoped<IFashionItmEstsService, FashionItmEstsService>(); services.AddScoped<IRecipeItmEstsService, RecipeItmEstsService>(); services.AddScoped<IGroceryItmEstsService, GroceryItmEstsService>(); services.AddScoped<IAllVgnItmEstsService, AllVgnItmEstsService>(); services.AddScoped<IMenuItmsService, MenuItmsService>(); services.AddScoped<IGroceryStoreItmsService, GroceryStoreItmsService>(); services.AddScoped<IRestaurantItmsService, RestaurantItmsService>(); services.AddScoped<IEventItmsService, EventItmsService>(); services.AddScoped<IFashionItmsService, FashionItmsService>(); services.AddScoped<IRecipeItmsService, RecipeItmsService>(); services.AddScoped<IGroceryItmsService, GroceryItmsService>(); services.AddScoped(typeof(IMenuItmEstsRepository), typeof(MenuItmEstsRepository)); services.AddScoped<ISearchService, SearchService>(serviceProvider => new SearchService( Configuration["openSearchEndpoint"], Configuration["openSearchUsername"], Configuration["openSearchPassword"], Configuration["openSearchIndexName"])); services.AddScoped<IGroceryItmsSearchIndexService, GroceryItmsSearchIndexService>(); services.AddScoped<IRecipeItmsSearchIndexService, RecipeItmsSearchIndexService>(); services.AddScoped<IEventItmsSearchIndexService, EventItmsSearchIndexService>(); services.AddScoped<IFashionItmsSearchIndexService, FashionItmsSearchIndexService>(); services.AddScoped<IMenuItmsSearchIndexService, MenuItmsSearchIndexService>(); services.AddScoped<IRestaurantItmsSearchIndexService, RestaurantItmsSearchIndexService>(); services.AddScoped<IGroceryStoreItmsSearchIndexService, GroceryStoreItmsSearchIndexService>(); services.AddScoped<IAllVgnItmsSearchIndexService, AllVgnItmsSearchIndexService>(); services.AddAutoMapper( typeof(EstProfile), typeof(VgnItmEstProfile), typeof(VgnItmProfile), typeof(EventItmEstProfile), typeof(EventItmProfile), typeof(FashionItmEstProfile), typeof(FashionItmProfile), typeof(RecipeItmEstProfile), typeof(RecipeItmProfile), typeof(GroceryItmEstProfile), typeof(GroceryItmProfile), typeof(GroceryStoreItmEstProfile), typeof(GroceryStoreItmProfile), typeof(RestaurantItmEstProfile), typeof(RestaurantItmProfile), typeof(MenuItmEstProfile), typeof(MenuItmProfile)); services.AddSwaggerGen(); } [Obsolete] public void Configure( IApplicationBuilder app, IWebHostEnvironment env, VepoContext context, IGroceryItmsSearchIndexService searchIndexService, IHttpForwarder forwarder) { if (env.IsDevelopment()) app.UseDeveloperExceptionPage(); app.UseHttpsRedirection(); app.UseRouting(); app.UseCors(options => options.AllowAnyOrigin().AllowAnyHeader().AllowAnyMethod()); NpgsqlConnection.GlobalTypeMapper.UseNetTopologySuite(); app.UseAuthentication(); app.UseAuthorization(); var httpClient = new HttpMessageInvoker(new SocketsHttpHandler() { UseProxy = false, AllowAutoRedirect = false, AutomaticDecompression = DecompressionMethods.None, UseCookies = false, ActivityHeadersPropagator = new ReverseProxyPropagator(DistributedContextPropagator.Current), ConnectTimeout = TimeSpan.FromSeconds(15), }); var transformer = new CustomTransformer(); var requestConfig = new ForwarderRequestConfig { ActivityTimeout = TimeSpan.FromSeconds(100) }; app.UseEndpoints(endpoints => { endpoints.MapControllers(); RequestDelegate googleMapsApi = async httpContext => { var error = await forwarder.SendAsync(httpContext, "https://maps.googleapis.com/maps/api/", httpClient, requestConfig, transformer); if (error != ForwarderError.None) { var errorFeature = httpContext.GetForwarderErrorFeature(); var exception = errorFeature.Exception; } }; endpoints.Map("/place/{**catch-all}", googleMapsApi); endpoints.Map("/geocode/{**catch-all}", googleMapsApi); }); app.UseSwagger(); app.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.json", "My Test1 Api v1")); searchIndexService.ReIndex(context, SearchIndexes.All); } } public static class HostEnvironmentExtensions { public static bool IsQa(this IHostEnvironment hostingEnvironment) { return hostingEnvironment.IsEnvironment("Qa"); } } }
相关.ebextensions配置文件
securelistener-alb.config
option_settings: aws:elbv2:listener:443: ListenerEnabled: 'true'
https-instance-securitygroup.config
Resources: sslSecurityGroupIngress: Type: AWS::EC2::SecurityGroupIngress Properties: GroupId: {"Fn::GetAtt" : ["AWSEBSecurityGroup", "GroupId"]} IpProtocol: tcp ToPort: 443 FromPort: 443 CidrIp: 0.0.0.0/0
sg-ingressfromlb.config
Resources: sslSecurityGroupIngress: Type: AWS::EC2::SecurityGroupIngress Properties: GroupId: {"Fn::GetAtt" : ["AWSEBSecurityGroup", "GroupId"]} IpProtocol: tcp ToPort: 1000 FromPort: 1000 SourceSecurityGroupName: {"Fn::GetAtt" : ["AWSEBLoadBalancer" , "SourceSecurityGroup.GroupName"]}
内容的提问来源于stack exchange,提问作者BeniaminoBaggins
相关产品推荐
相关产品推荐

