skip to Main Content

I have a web application running on CentOS 7 with an Apache server used as a proxy for Kestrel. In my Program.cs file I’m using the below, which seems to work fine:

    public static void Main(string[] args)
    {
        var config = new ConfigurationBuilder()
            .SetBasePath(Directory.GetCurrentDirectory())
            .AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
            .Build();

        var host = new WebHostBuilder()
            .UseKestrel()
            .UseConfiguration(config)
            .UseContentRoot(Directory.GetCurrentDirectory())
            .UseStartup<Startup>()
            .UseUrls("http://localhost:5555")
            .Build();

        host.Run();
    }

But it looks like things with .NET Core have been updated recently, and I’m trying to use the IHostBuilder in the same way as below, which seems to work fine locally, but on the production server I’m getting the error "Proxy Error 502: The proxy server received an invalid response from an upstream server".

    public static void Main(string[] args)
    {
        CreateHostBuilder(args).Build().Run();
    }

    public static IHostBuilder CreateHostBuilder(string[] args) =>
        Host.CreateDefaultBuilder(args)            
            .ConfigureWebHostDefaults(webBuilder =>
            {                    
                webBuilder.UseKestrel();
                webBuilder.UseContentRoot(Directory.GetCurrentDirectory());
                webBuilder.UseConfiguration(new ConfigurationBuilder()
                    .SetBasePath(Directory.GetCurrentDirectory())
                    .AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
                    .AddCommandLine(args).Build());
                webBuilder.UseUrls("https://localhost:5555");
                webBuilder.UseStartup<Startup>();
            });

I’m not sure if this has anything to do with the configuration of the domain on my server – which again works fine using the first example above – or if there’s something else I need to do in either the Program.cs or Startup.cs that would fix this problem using IHostBuilder.

In the meantime I’m going to keep using what I have since it’s working fine that way.

Thanks in advance to anyone who can help shed some light on this. And please let me know if there’s any other information I can provide to help.

2

Answers


  1. Chosen as BEST ANSWER

    Wow... it was because the service running my web app is running on http but forces redirect to https. The issue was this line:

    webBuilder.UseUrls("https://localhost:5555");

    Needs to be this:

    webBuilder.UseUrls("http://localhost:5555");

    I also tested Xavier's solution, which also works. So thanks for offering up a solution.


  2. It may be the localhost binding or the SSL security that is causing the issue.
    Make sure that the HTTPS cert you are using in your project can be trusted by the proxy, or disable checking in apache.

    From a .Net point of view, try adding the following to your project

    public class ConfigHelper
    {
        /// <summary>
        /// This method is used in the service startup to read hosting configuration options from the applications settings
        /// The following section can be included in your appsettings.json file to specify the binding, ports and certificate information.
        /// The certificate should be self-signed in .pfx format
        /// 
        ///   "Hosting": {
        ///       "HTTPBinding": "DISABLED",
        ///       "HTTPPort": 0,
        ///       "HTTPSBinding": "ANY",
        ///       "HTTPSPort": 5555,
        ///       "SSLCert": "PathToCert"
        ///       }
        /// </summary>
        /// <param name="configuration">The configuration option loaded from a local config file</param>
        /// <param name="options">The KestrelServerOptions from WebHost.CreateDefaultBuilder(args).UseKestrel(options => ConfigHelper.SetupHostingOptions(configuration, options);)</param>
        public static void SetupHostingOptions(IConfigurationRoot configuration, KestrelServerOptions options)
        {
            IPAddress httpBinding = ConfigHelper.GetAddressFromSetting(configuration.GetValue<string>("Hosting:HTTPBinding"));
            IPAddress httpsBinding = ConfigHelper.GetAddressFromSetting(configuration.GetValue<string>("Hosting:HTTPSBinding"));
            if (httpBinding != null && httpBinding != IPAddress.None)
            {
                if (configuration.GetValue<int?>("Hosting:HTTPPort") != null)
                    options.Listen(httpBinding, configuration.GetValue<int>("Hosting:HTTPPort"));
            }
            if (httpsBinding != null && httpsBinding != IPAddress.None)
            {
                int httpsPort = configuration.GetValue<int?>("Hosting:HTTPSPort") ?? 443;
                string sslCert = configuration.GetValue<string>("Hosting:SSLCert");
                string sslCertPassword = configuration.GetValue<string>("Hosting:SSLCertPassword");
                if (sslCert != null)
                {
                    if (sslCertPassword != null)
                        options.Listen(httpsBinding, httpsPort, listenOptions => { listenOptions.UseHttps(sslCert, sslCertPassword); });
                    else
                        options.Listen(httpsBinding, httpsPort, listenOptions => { listenOptions.UseHttps(sslCert); });
                }
            }
        }
        /// <summary>
        /// Gets the IP address from the configuration setting
        /// </summary>
        /// <param name="httpBinding"></param>
        /// <returns></returns>
        public static IPAddress GetAddressFromSetting(string httpBinding)
        {
            if (string.IsNullOrWhiteSpace(httpBinding) || httpBinding.ToUpper() == "DISABLED")
                return null;
            else
                httpBinding = httpBinding.ToUpper();
            IPAddress bindingIp = null;
            switch (httpBinding.ToUpper())
            {
                case "DISABLED":
                    bindingIp = null;
                    break;
                case "ANY":
                    bindingIp = IPAddress.Any;
                    break;
                case "IPV6ANY":
                    bindingIp = IPAddress.IPv6Any;
                    break;
                case "IPV6LOOPBACK":
                    bindingIp = IPAddress.IPv6Loopback;
                    break;
                case "IPV6NONE":
                    bindingIp = IPAddress.IPv6None;
                    break;
                case "LOOPBACK":
                    bindingIp = IPAddress.Loopback;
                    break;
                case "NONE":
                    bindingIp = IPAddress.None;
                    break;
                default:
                    bool ipParsed = false;
                    try
                    {
                        bindingIp = IPAddress.Parse(httpBinding);
                        ipParsed = true;
                    }
                    catch(System.Exception)
                    {
    
                    }
                    if(!ipParsed)
                    {
                        IPHostEntry hostEntry = Dns.GetHostEntry(httpBinding);
                        if (hostEntry.AddressList.Length > 0)
                        {
                            bindingIp = hostEntry.AddressList[0];
                            ipParsed = true;
                        }
                    }
                    if (!ipParsed)
                    {
                        throw new System.Exception("Failed to parse IP address from '" + httpBinding + "'");
                    }
                    break;
            }
            return bindingIp;
        }
    }
    

    Then in your main

        public static void Main(string[] args)
        {
            // Start the application
            CreateHostBuilder(args).Build().Run();
        }
        /// <summary>
        /// Create the service objects
        /// </summary>
        /// <param name="args">Command line arguments</param>
        /// <returns>The configured host builder object</returns>
        public static IHostBuilder CreateHostBuilder(string[] args)
        {
            // Load the configuration object
            var configuration = new ConfigurationBuilder()
                .AddCommandLine(args)
                .SetBasePath(Environment.CurrentDirectory)
                .AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
                .AddJsonFile($"appsettings.{Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT")}.json", optional: true)
                .AddEnvironmentVariables()
                .Build();
            // Create the host builder object
            return Host.CreateDefaultBuilder(args)
                .ConfigureWebHostDefaults(webBuilder =>
                {
                    webBuilder
                        .UseStartup<Startup>()
                        .UseConfiguration(configuration)
                        .UseKestrel(options => { ConfigHelper.SetupHostingOptions(configuration, options); });
                });
        }
    

    Then you appsettings.json should have a section like

     "Hosting": {
        "HTTPBinding": "DISABLED",
        "HTTPPort": 0,
        "HTTPSBinding": "ANY",
        "HTTPSPort": 5555,
        "SSLCert": "sslcert.pfx",
        "SSLCertPassword": "sslcertpasswordhere"
      }
    

    If you are proxying from apache to the kestrel service on the same centos instance, then you might consider running apache in HTTPS for the external world and using http on between the two (or just skip using the proxy all together)

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search