17

I am trying to restrict a site by IP address. In previous MVC versions I'd add something like the following to the web.config:

<security>
  <ipSecurity allowUnlisted="false" denyAction="NotFound">
    <add allowed="true" ipAddress="XX.XX.XX.XX" subnetMask="255.255.255.0"/>
  </ipSecurity>
</security>

But adding this to a AspNetCore project causes the application to fails on startup with the error

Unable to start process The web server request failed with status code 500, internal server error

Evidently I broke the config as its not handled in here anymore. The error produces a HttpFailure log, which looks like:

enter image description here

What's the best way to handle this now, something built-in or otherwise

MartinM
  • 1,736
  • 5
  • 20
  • 33
  • 1
    Can you figure out what the internal server error is and add it to your post? – mason Nov 04 '16 at 16:14
  • 3
    ipSecurity is IIS specific, while ASP.NET Core ist about cross plattform serving web request via the Kestrel server. There might be a better way but via middleware pipeline it is possible to retrieve the IP address as described in http://stackoverflow.com/questions/28664686/how-do-i-get-client-ip-address-in-asp-net-core and return a NotFound result. – Ralf Bönning Nov 04 '16 at 16:17
  • @rboe ah yes, that would make sense. So you think its a case of adding it manually then? Was thinking that may be the case – MartinM Nov 04 '16 at 16:21
  • @mason editted, thanks – MartinM Nov 04 '16 at 16:24
  • It's it better to just restrict via firewall? – Evk Nov 04 '16 at 16:58
  • 1
    @rboe: Retrieving the IP via isn't so easy/consistent in ASP.NET Core, because the default variables where you'd expect it gives you the IP of the reverse proxy rather than the users IP and depending on how many hops it had its not so easy to determine. Doing it on the internet-facing server (IIS or nginx on Linux) makes more sense – Tseng Nov 04 '16 at 17:02
  • Are you using IIS? If yes then it should still work. – davidfowl Jun 29 '19 at 12:21

2 Answers2

16

Damian Bod has made a blog post demonstrating how to implement middleware to handle IP whitelisting.

He gives examples of global middleware or action filter.

Either way you need to add permitted IP addresses to your appsettings.json, and check the client IP address against them.

Client IP address is available via HttpContext (e.g. context.Connection.RemoteIpAddress).

If you want to whitelist IP address ranges, then you can use the Nuget package IPAddressRange, which supports various formats such as "192.168.0.0/24" and "192.168.0.0/255.255.255.0", including CIDR expressions and IPv6.

Here's an example of how to do that in a filter:

appsettings.json:

{
  "IPAddressWhitelistConfiguration": {
    "AuthorizedIPAddresses": [
      "::1", // IPv6 localhost
      "127.0.0.1", // IPv4 localhost
      "192.168.0.0/16", // Local network
      "10.0.0.0/16", // Local network
    ]
  }
}

IPWhiteListConfiguration.cs:

namespace My.Web.Configuration
{
    using System.Collections.Generic;

    public class IPWhitelistConfiguration : IIPWhitelistConfiguration
    {
        public IEnumerable<string> AuthorizedIPAddresses { get; set; }
    }
}

IIPWhiteListConfiguration.cs:

namespace My.Web.Configuration
{
    using System.Collections.Generic;

    public interface IIPWhitelistConfiguration
    {
        IEnumerable<string> AuthorizedIPAddresses { get; }
    }
}

Startup.cs:

public class Startup
{
    // ...
    public void ConfigureServices(IServiceCollection services)
    {
        // ...
        services.Configure<IPWhitelistConfiguration>(
           this.Configuration.GetSection("IPAddressWhitelistConfiguration"));
        services.AddSingleton<IIPWhitelistConfiguration>(
            resolver => resolver.GetRequiredService<IOptions<IPWhitelistConfiguration>>().Value);
        // ...
    }
 }

ClientIPAddressFilterAttribute.cs:

namespace My.Web.Filters
{
    using System.Collections.Generic;
    using System.Linq;
    using System.Net;
    using Microsoft.AspNetCore.Mvc;
    using Microsoft.AspNetCore.Mvc.Filters;
    using NetTools;
    using My.Web.Configuration;

    public class ClientIPAddressFilterAttribute : ActionFilterAttribute
    {
        private readonly IEnumerable<IPAddressRange> authorizedRanges;

        public ClientIPAddressFilterAttribute(IIPWhitelistConfiguration configuration)
        {
            this.authorizedRanges = configuration.AuthorizedIPAddresses
                .Select(item => IPAddressRange.Parse(item));
        }

        public override void OnActionExecuting(ActionExecutingContext context)
        {
            var clientIPAddress = context.HttpContext.Connection.RemoteIpAddress;
            if (!this.authorizedRanges.Any(range => range.Contains(clientIPAddress)))
            {
                context.Result = new UnauthorizedResult();
            }
        }
    }
Ergwun
  • 12,579
  • 7
  • 56
  • 83
  • Good answer but I think the blog post link is wrong. – SpruceMoose Nov 09 '17 at 23:40
  • @CalC Thanks. Fixed now! – Ergwun Nov 10 '17 at 03:09
  • I'm wondering about how the interface IIPWhitelistConfiguration used in IPWhiteListConfiguration.cs looks like. Does it inherit IConfiguration? – Harald S. Hanssen Oct 16 '18 at 07:50
  • @Verzada It's just an interface that exposes only the getter of the `AuthorizedIPAddresses` property. It's not important to the example, and is just there to deouple client code from the implementation of `IPWhitelistConfiguration`. I've added it in the answer for completeness, and to show how to resolve it. – Ergwun Oct 17 '18 at 02:52
  • @Ergwun I'm just getting into .NET Core and therefore I appreciate anything that can make things a lot clear when I'm looking at examples of how to do dependency injection in that particular framework. So thank you for taking your time to add the information :) – Harald S. Hanssen Oct 19 '18 at 09:20
9

I needed something similar, except that "safelisting" a single IP Address wasn't good enough for me, because I had to enable a whole range of IP addresses through a CIDR notation (for Cloudflare). I blogged about it yesterday, but in short you can install the Firewall NuGet package and then configure IP filter settings like this:

namespace BasicApp
{
    public class Startup
    {
        public void Configure(IApplicationBuilder app)
        {
            var allowedIPs =
                new List<IPAddress>
                    {
                        IPAddress.Parse("10.20.30.40"),
                        IPAddress.Parse("1.2.3.4"),
                        IPAddress.Parse("5.6.7.8")
                    };

            var allowedCIDRs =
                new List<CIDRNotation>
                    {
                        CIDRNotation.Parse("110.40.88.12/28"),
                        CIDRNotation.Parse("88.77.99.11/8")
                    };

            app.UseFirewall(
                FirewallRulesEngine
                    .DenyAllAccess()
                    .ExceptFromIPAddressRanges(allowedCIDRs)
                    .ExceptFromIPAddresses(allowedIPs));

            app.Run(async (context) =>
            {
                await context.Response.WriteAsync("Hello World!");
            });
        }
    }
}
dustinmoris
  • 3,195
  • 3
  • 25
  • 33