0

EDIT I THINK THE ISSUE MUST BE MY BACK END CODE url configurations, e.g. look at this: webBuilder.UseUrls("http://*:5002") (I since changed to webBuilder.UseUrls("https://*:5002") although not working. surely the port 5002 has to match up with my listener????

Here it all is:

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)
            {
                // Copy all request headers
                await base.TransformRequestAsync(httpContext, proxyRequest, destinationPrefix, cancellationToken);

                // Customize the query string:
                var queryContext = new QueryTransformContext(httpContext.Request);

                // Assign the custom uri. Be careful about extra slashes when concatenating here. RequestUtilities.MakeDestinationAddress is a safe default.
                proxyRequest.RequestUri = RequestUtilities.MakeDestinationAddress("https://maps.googleapis.com/maps/api", httpContext.Request.Path, queryContext.QueryString);

                // Suppress the original request header, use the one from the destination Uri.
                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");
            }


            var claims = new Dictionary<string, object>
            {
                { ClaimTypes.Role, "User" }
            };

            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, RestaurantItmEstsService>();
            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 =>
            {
                return new SearchService(
                        Configuration["openSearchEndpoint"],
                        Configuration["openSearchUsername"],
                        Configuration["openSearchPassword"],
                        Configuration["openSearchIndexName"]);
            });

            var x = Configuration["openSearchEndpoint"];

            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//,
            /*ILogger logger*/)
        {

            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(); // or HttpTransformer.Default;
            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);
                    // Check if the operation was successful
                    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");
        }
    }
}

EDIT: Once my server is HTTPS, does it mean my front end on localhost has to be served via https to talk to it? As mine is http://localhost:62650

I'm trying to make my single instance Elastic Beanstalk environment for my asp.net core back end hosted on Linux use HTTPS via converting to being load balanced and having a certificate.

However, when I try to hit the endpoint from my front end, it stays pending and then fails after my 200-second timeout in my front end. These requests take 2 seconds with a single instance un-load-balanced elastic beanstalk.

When I change it back to single instance, not load balanced, it works again.

It is currently reachable from my front end (when the front end is hosted on HTTP at http://localhost:57276/) and the beanstalk back end is at http://vepo-qa-env.eba-xxx.ap-southeast-2.elasticbeanstalk.com:5002. But I am getting a blocked mixed content issue when I host the front end on HTTPS which I need to do. So I need to go from this http://vepo-qa-env.eba-xxx.ap-southeast-2.elasticbeanstalk.com:5002 to https://vepo-qa-env.eba-xxx.ap-southeast-2.elasticbeanstalk.com:5002 OR into a completely different url that uses HTTPS like https://vepo-qa.com.

I have created a domain in Route 53 for vepo-qa.com and a certificate in the AWS Certificate Manager console, valid for the domain vepo-qa.com and *.vepo-qa.com, and it is issued. Here are the route 53 records:enter image description here

In order to use my certificate, I have converted my Elastic Beanstalk environment from single instance to load balanced and added an HTTPS listener based on the recommended settings for the docs:

enter image description here

If my URL was http://vepo-qa-env.eba-xxx.ap-southeast-2.elasticbeanstalk.com:**5002** do I need to have 5002 as one of the ports in the listener? My back-end code has this which only allows port 5002 webBuilder.UseUrls("http://*:5002"); should I be adding other ports that are already in my listener to this back-end line of code?

Is simply assigning the certificate to the listener enough to make the server capable of HTTPS protocol, or do I need to enter a private key into my back-end code base or add config files like these?

.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"]}

Am I supposed to change my URL in the front-end app HTTP request from

http://vepo-qa-env.eba-xxx.ap-southeast-2.elasticbeanstalk.com:5002

to

https://vepo-qa-env.eba-xxx.ap-southeast-2.elasticbeanstalk.com:5002

or to

https://vepo-qa.com?

And if so, do I need to somehow let my elastic beanstalk environment know about https://vepo-qa.com?

Here are some of my AWS configurations in case they are relevant:

enter image description here

enter image description here

enter image description here

enter image description here

enter image description here enter image description here

EDIT btw I also tried an AWS expert and it didn't get my app https requests working from my front end. So maybe it is a .net issue. I asked about the asp.net parts of this question over here, where I seem to have made progress because I am now hitting my back end at https://vepo-qa.com from the front end at a firebase https address and getting a CORS error.

BeniaminoBaggins
  • 11,202
  • 41
  • 152
  • 287

1 Answers1

0

Taken some another SO answer:

Elastic Beanstalk's proxy per defaults forwards traffic on port 5000. So either you should configure nginx to forward request to the port, your app listens on - 80, or you should make your app listen on port 5000.

I took the route of least resistance and changed:

webBuilder.UseUrls("http://*:5002")
webBuilder.UseUrls("https://*:5002")

to

webBuilder.UseUrls("http://*:5000")

Note that I also deleted the https version of UseUrls. The bad gateway still occured with it in (even when I changed it to webBuilder.UseUrls("http://*:5000", "https://*:5000"). The SSL (HTTPS) is on the proxy server, and the actual server will not be SSL (it's just HTTP), if doing it the default way that AWS docs say to do it.

BeniaminoBaggins
  • 11,202
  • 41
  • 152
  • 287
  • But, do you event need this. I mean, even if someone does not specify this in code. Elastic Beanstalk will explicitly run the application's on these ports as 5000 is the default port used by kestrel server, and you also don't need to worry about the NGINX configuration used by Elastic Beanstalk internally. That's the whole purpose of this service, just upload the code, and rest will be taken care by AWS. – Ankush Jain Jul 24 '23 at 06:20
  • @AnkushJain I will look into this as soon as possible. – BeniaminoBaggins Jul 24 '23 at 06:35