{"id":19424836,"url":"https://github.com/dustinmoris/firewall","last_synced_at":"2025-04-04T08:06:23.476Z","repository":{"id":41103208,"uuid":"149897957","full_name":"dustinmoris/Firewall","owner":"dustinmoris","description":"ASP.NET Core middleware for IP address filtering.","archived":false,"fork":false,"pushed_at":"2023-06-11T08:33:24.000Z","size":2028,"stargazers_count":226,"open_issues_count":8,"forks_count":35,"subscribers_count":16,"default_branch":"master","last_synced_at":"2025-04-04T07:41:25.278Z","etag":null,"topics":["asp-net-core","aspnetcore","cloudflare","ipv4","ipv6","security"],"latest_commit_sha":null,"homepage":null,"language":"C#","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/dustinmoris.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2018-09-22T17:03:01.000Z","updated_at":"2025-02-27T15:49:46.000Z","dependencies_parsed_at":"2024-06-19T05:28:30.711Z","dependency_job_id":"c56aa687-8621-4f53-92da-5c4616be19d5","html_url":"https://github.com/dustinmoris/Firewall","commit_stats":{"total_commits":49,"total_committers":2,"mean_commits":24.5,"dds":"0.020408163265306145","last_synced_commit":"fb7e08be8738abb83207edcd53f6056edf0ec26a"},"previous_names":[],"tags_count":8,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dustinmoris%2FFirewall","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dustinmoris%2FFirewall/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dustinmoris%2FFirewall/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dustinmoris%2FFirewall/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/dustinmoris","download_url":"https://codeload.github.com/dustinmoris/Firewall/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247142063,"owners_count":20890652,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["asp-net-core","aspnetcore","cloudflare","ipv4","ipv6","security"],"created_at":"2024-11-10T13:45:38.045Z","updated_at":"2025-04-04T08:06:23.449Z","avatar_url":"https://github.com/dustinmoris.png","language":"C#","funding_links":["https://www.buymeacoffee.com/dustinmoris"],"categories":[],"sub_categories":[],"readme":"# ![Firewall](https://raw.githubusercontent.com/dustinmoris/Firewall/master/assets/firewall.png) Firewall\n\nASP.NET Core middleware for request filtering.\n\nFirewall adds IP address-, geo-location and custom filtering capabilities to an ASP.NET Core web application which gives control over which connections are allowed to access the web server.\n\n[![NuGet Info](https://buildstats.info/nuget/Firewall?includePreReleases=true)](https://www.nuget.org/packages/Firewall/)\n\n[![Build History](https://buildstats.info/github/chart/dustinmoris/Firewall?branch=develop)](https://github.com/dustinmoris/Firewall/actions?query=branch%3Adevelop)\n\n## Table of contents\n\n- [About](#about)\n- [Using with Cloudflare](#using-with-cloudflare)\n- [Getting Started](#getting-started)\n- [Documentation](#documentation)\n    - [Basics](#basics)\n    - [Cloudflare Support](#cloudflare-support)\n    - [Custom Rules](#custom-rules)\n    - [Custom Filter Rules](#custom-filter-rules)\n    - [Custom RequestDelegate for blocked requests](#custom-requestdelegate-for-blocked-requests)\n    - [Miscellaneous](#miscellaneous)\n        - [IP Address and CIDR Notation Parsing](#ip-address-and-cidr-notation-parsing)\n        - [X-Forwarded-For HTTP Header](#x-forwarded-for-http-header)\n        - [Loading Rules from Configuration](#loading-rules-from-configuration)\n    - [Diagnostics](#diagnostics)\n- [Contributing](#contributing)\n- [Support](#support)\n- [License](#license)\n- [Credits](#credits)\n\n## About\n\nFirewall is an ASP.NET Core middleware which enables IPv4 and IPv6 address-, geo-location and other request filtering features.\n\nRequest filtering can be added as an extra layer of security to a publicly exposed API or to force all API access through a certain set of proxy servers (e.g. [Cloudflare](https://www.cloudflare.com/)).\n\n### How is it different to ASP.NET Core's IP safelist feature?\n\nSimply ASP.NET Core's [safelist](https://docs.microsoft.com/en-us/aspnet/core/security/ip-safelist?view=aspnetcore-2.1) feature doesn't support IPv4 and IPv6 address ranges specified through CIDR notations, which makes is somewhat less usable in the real world where a web application might need to \"safelist\" a CIDR notation as part of its security configuration.\n\n## Using with Cloudflare\n\n[Cloudflare](https://www.cloudflare.com/) is a popular internet service which provides enhanced performance and security features to any website. It is currently being used by more than 8 million websites world wide and requires no additional hardware or software as it operates at the DNS level.\n\nThe typical request flow for a website which is not protected by Cloudflare looks a little bit like this:\n\n![without-cloudflare](https://raw.githubusercontent.com/dustinmoris/Firewall/master/assets/without-cloudflare.png)\n\n*Image source: [blog.christophetd.fr](https://blog.christophetd.fr/)*\n\nWhen a website is protected by Cloudflare then Cloudflare essentially acts as a man in the middle, shielding a website from all sorts of malicious internet activity and giving a website administrator enhanced performance and security features such as HTTPS, Caching, CDNs, API rate limiting and more:\n\n![with-cloudflare](https://raw.githubusercontent.com/dustinmoris/Firewall/master/assets/with-cloudflare.png)\n\n*Image source: [blog.christophetd.fr](https://blog.christophetd.fr/)*\n\nThe only problem with this configuration is that an attacker can still access the origin server by [sending requests directly to its IP address](http://www.chokepoint.net/2017/10/exposing-server-ips-behind-cloudflare.html) and therefore [bypassing all additional security and performance layers provided by Cloudflare](https://blog.christophetd.fr/bypassing-cloudflare-using-internet-wide-scan-data/).\n\nIn order to prevent anyone from talking directly to the origin server and forcing all access through Cloudflare one has to block all IP addresses which do not belong to Cloudflare.\n\nCloudflare [maintains two public lists](https://www.cloudflare.com/ips/) of all their [IPv4](https://www.cloudflare.com/ips-v4) and [IPv6](https://www.cloudflare.com/ips-v6) address ranges which can be used to configure an origin server's IP address filtering.\n\n[Firewall supports IP filtering for Cloudflare](#built-in-support-for-cloudflare-ip-ranges) out of the box.\n\n## Getting Started\n\nFirst install the [Firewall](https://www.nuget.org/packages/Firewall/) NuGet package using PowerShell:\n\n```powershell\nPM\u003e Install-Package Firewall\n```\n\n...or via the dotnet command line:\n\n```\ndotnet add [PROJECT] package Firewall --package-directory [PACKAGE_CIRECTORY]\n```\n\nThen add the Firewall middleware to your ASP.NET Core `Startup` class:\n\n```csharp\nusing Firewall;\n\nnamespace BasicApp\n{\n    public class Startup\n    {\n        public void Configure(IApplicationBuilder app)\n        {\n            var allowedIPs =\n                new List\u003cIPAddress\u003e\n                    {\n                        IPAddress.Parse(\"10.20.30.40\"),\n                        IPAddress.Parse(\"1.2.3.4\"),\n                        IPAddress.Parse(\"5.6.7.8\")\n                    };\n\n            var allowedCIDRs =\n                new List\u003cCIDRNotation\u003e\n                    {\n                        CIDRNotation.Parse(\"110.40.88.12/28\"),\n                        CIDRNotation.Parse(\"88.77.99.11/8\")\n                    };\n\n            app.UseFirewall(\n                FirewallRulesEngine\n                    .DenyAllAccess()\n                    .ExceptFromIPAddressRanges(allowedCIDRs)\n                    .ExceptFromIPAddresses(allowedIPs));\n\n            app.Run(async (context) =\u003e\n            {\n                await context.Response.WriteAsync(\"Hello World!\");\n            });\n        }\n    }\n}\n```\n\n## Documentation\n\n### Basics\n\nFirewall uses a rules engine to configure request filtering. The `FirewallRulesEngine` helper class should be used to configure an object of `IFirewallRule` which then can be passed into the `FirewallMiddleware`:\n\n```csharp\nvar rules =\n    FirewallRulesEngine\n        .DenyAllAccess()\n        .ExceptFromCloudflare()\n        .ExceptFromLocalhost();\n\napp.UseFirewall(rules);\n```\n\nCurrently the following rules can be configures out of the box:\n\n- `DenyAllAccess()`: This is the base rule which should be used at the beginning of the rules configuration. It specifies that if no other rule can be met by an incoming HTTP request then access should be denied.\n- `ExceptFromLocalhost()`: This rule specifies that HTTP requests from the local host should be allowed. This might be useful when debugging the application.\n- `ExceptFromIPAddresses(IList\u003cIPAddress\u003e ipAddresses)`: This rule enables access to a list of specific IP addresses.\n- `ExceptFromIPAddressRanges(IList\u003cCIDRNotation\u003e cidrNotations)`: This rule enables access to a list of specific IP address ranges (CIDR notations).\n- `ExceptFromCloudflare(string ipv4Url = null, string ipv6Url = null)`: This rule enables access to requests from Cloudflare servers.\n- `ExceptFromCountry(IList\u003cCountryCode\u003e allowedCountries)`: This rule enables access to requests which originated from one of the specified countries.\n- `ExceptWhen(Func\u003cHttpContext, bool\u003e filter)`: This rule enables a custom request filter to be applied (see [Custom Filter Rules](#custom-filter-rules) for more info).\n\nA HTTP request only needs to satisfy a single rule in order to pass the Firewall access control layer. The reverse order of the rules specifies the order in which an incoming HTTP request gets validated. It is advisable to specify simple/quick rules at the end as they will get executed first.\n\n### Cloudflare Support\n\nIf an ASP.NET Core web application is going to sit behind Cloudflare then Firewall can be configured with the built-in `ExceptFromCloudflare()` Firewall rule:\n\n```csharp\nusing Firewall;\n\nnamespace BasicApp\n{\n    public class Startup\n    {\n        public void Configure(IApplicationBuilder app)\n        {\n            app.UseFirewall(\n                FirewallRulesEngine\n                    .DenyAllAccess()\n                    .ExceptFromCloudflare());\n\n            app.Run(async (context) =\u003e\n            {\n                await context.Response.WriteAsync(\"Hello World!\");\n            });\n        }\n    }\n}\n```\n\nThe `ExceptFromCloudflare()` configuration method will automatically pull the latest list of IPv4 and IPv6 address ranges from Cloudflare and register the `FirewallMiddleware` with those values.\n\nOptionally one can specify custom URLs to load the correct IP address ranges from:\n\n```csharp\nusing Firewall;\n\nnamespace BasicApp\n{\n    public class Startup\n    {\n        public void Configure(IApplicationBuilder app)\n        {\n            app.UseFirewall(\n                FirewallRulesEngine\n                    .DenyAllAccess()\n                    .ExceptFromCloudflare(\n                        ipv4ListUrl: \"https://www.cloudflare.com/ips-v4\",\n                        ipv6ListUrl: \"https://www.cloudflare.com/ips-v6\"\n                    ));\n\n            app.Run(async (context) =\u003e\n            {\n                await context.Response.WriteAsync(\"Hello World!\");\n            });\n        }\n    }\n}\n```\n\n### Custom Rules\n\nCustom Firewall rules can be added by creating a new class which implements `IFirewallRule`.\n\nFor example, if one would like to create a new Firewall rule which filters requests based on [Cloudflare's `CF-IPCountry`](https://support.cloudflare.com/hc/en-us/articles/200170986-How-does-CloudFlare-handle-HTTP-Request-headers-) HTTP header, then you'd start by implementing a new class which implements the `IFirewallRule` interface:\n\n```csharp\npublic class IPCountryRule : IFirewallRule\n{\n    private readonly IFirewallRule _nextRule;\n    private readonly IList\u003cstring\u003e _allowedCountryCodes;\n\n    public IPCountryRule(\n        IFirewallRule nextRule,\n        IList\u003cstring\u003e allowedCountryCodes)\n    {\n        _nextRule = nextRule;\n        _allowedCountryCodes = allowedCountryCodes;\n    }\n\n    public bool IsAllowed(HttpContext context)\n    {\n        const string headerKey = \"CF-IPCountry\";\n\n        if (!context.Request.Headers.ContainsKey(headerKey))\n            return _nextRule.IsAllowed(context);\n\n        var countryCode = context.Request.Headers[headerKey].ToString();\n        var isAllowed = _allowedCountryCodes.Contains(countryCode);\n\n        return isAllowed || _nextRule.IsAllowed(context);\n    }\n}\n```\n\nThe constructor of the `IPCountryRule` class takes in a list of allowed country codes and the next rule in the pipeline. If a HTTP request originated from an allowed country then the custom rule will return `true`, otherwise it will invoke the next rule of the rules engine.\n\nIn order to chain this rule into the existing rules engine one can add an additional extension method:\n\n```csharp\npublic static class FirewallRulesEngineExtensions\n{\n    public static IFirewallRule ExceptFromCountryCodes(\n        this IFirewallRule rule,\n        IList\u003cstring\u003e allowedCountryCodes)\n    {\n        return new IPCountryRule(rule, allowedCountryCodes);\n    }\n}\n```\n\nAfterwards the rule can be enabled by calling `ExceptFromCountryCodes(allowedCountryCodes)` during application setup:\n\n```csharp\npublic class Startup\n{\n    public void Configure(IApplicationBuilder app)\n    {\n        app.UseFirewall(\n            FirewallRulesEngine\n                .DenyAllAccess()\n                .ExceptFromCountryCodes(new [] { \"US\", \"GB\", \"JP\" })\n                .ExceptFromCloudflare());\n\n        app.Run(async (context) =\u003e\n        {\n            await context.Response.WriteAsync(\"Hello World!\");\n        });\n    }\n}\n```\n\n### Custom Filter Rules\n\nAnother slightly less flexible, but much easier and quicker way of applying a custom rule is by using the `ExceptWhen(Func\u003cHttpContext, bool\u003e filter)` configuration method. The `ExceptWhen` method can be used to set up simple rules by providing a `Func\u003cHttpContext, bool\u003e` predicate:\n\n```csharp\nvar adminIP = IPAddress.Parse(\"1.2.3.4\");\n\napp.UseFirewall(\n    FirewallRulesEngine\n        .DenyAllAccess()\n        .ExceptFromCloudflare()\n        .ExceptWhen(ctx =\u003e ctx.Connection.RemoteIpAddress == adminIP));\n```\n\n### Custom RequestDelegate for blocked requests\n\nBy default the Firewall middleware will return a `403 Forbidden` plain text HTTP response for blocked requests. The (optional) `accessDeniedDelegate` parameter of the `UseFirewall` extension method can be used to override the default behaviour:\n\n```csharp\napp.UseFirewall(\n    FirewallRulesEngine\n        .DenyAllAccess()\n        .ExceptFromCloudflare(),\n    accessDeniedDelegate:\n        ctx =\u003e\n        {\n            ctx.Response.StatusCode = StatusCodes.Status403Forbidden;\n            return ctx.Response.WriteAsync(\"Forbidden\");\n        });\n```\n\n### Miscellaneous\n\n#### IP Address and CIDR Notation Parsing\n\nThe easiest way to generate a custom list of `IPAddress` or `CIDRNotation` objects is by making use of the `IPAddress.Parse(\"0.0.0.0\")` and `CIDRNotation.Parse(\"0.0.0.0/32\")` helper methods.\n\n#### X-Forwarded-For HTTP Header\n\nIf you have other proxies sitting between Cloudflare and the origin server (e.g. load balancer) then you'll have to enable the `ForwardedHeader` middleware, which will make sure that the correct IP address will be assigned to the `RemoteIpAddress` property of the `HttpContext.Connection` object:\n\n```csharp\npublic void Configure(IApplicationBuilder app)\n{\n    app.UseForwardedHeaders(\n        new ForwardedHeadersOptions\n        {\n            ForwardedHeaders = ForwardedHeaders.XForwardedFor,\n            ForwardLimit = 1\n        }\n    );\n\n    // Register Firewall after error handling and forwarded headers,\n    // but before other middleware:\n    app.UseCloudflareFirewall();\n\n    app.Run(async (context) =\u003e\n    {\n        await context.Response.WriteAsync(\"Hello World!\");\n    });\n}\n```\n\nPlease be aware that the `ForwardedHeaders` middleware must be registered before the `FirewallMiddleware` and also that it is not recommended to set the `ForwardedLimit` to a value greater than 1 unless you also provide a list of trusted proxies.\n\n#### Loading Rules from Configuration\n\nFirewall doesn't prescribe a certain way of how to configure rules outside of the rules engine. It is up to an application author to decide how rules should be loaded from an external configuration provider. [ASP.NET Core offers a wealth of default configuration providers](https://docs.microsoft.com/en-us/aspnet/core/fundamentals/configuration/?view=aspnetcore-2.1) which are recommended to use.\n\nExample:\n\n```csharp\npublic class Startup\n{\n    private readonly IConfiguration _config;\n\n    public void Configure(IApplicationBuilder app)\n    {\n        // Load custom config settings from whichever provider has been set up:\n        var enableLocalhost = _config.GetValue(\"AllowRequestsFromLocalhost\", false);\n        var adminIPAddress = _config.GetValue\u003cstring\u003e(\"AdminIPAddress\", null);\n\n        // Configure default Firewall rules:\n        var firewallRules =\n            FirewallRulesEngine.DenyAllAccess();\n\n        // Add rules according to the config:\n        if (enableLocalhost)\n            firewallRules = firewallRules.ExceptFromLocalhost();\n\n        if (adminIPAddress != null)\n            firewallRules = firewallRules.ExceptFromIPAddresses(new [] { IPAddress.Parse(adminIPAddress) });\n\n        // Enable Firewall with the configured rules:\n        app.UseFirewall(firewallRules);\n\n        app.Run(async (context) =\u003e\n        {\n            await context.Response.WriteAsync(\"Hello World!\");\n        });\n    }\n}\n```\n\n### Diagnostics\n\nFirewall logs all denied requests using ASP.NET Core's logging API with log level `Warning`.\n\nIf you're having troubles with Firewall and you want to get more insight into which requests are being blocked by the Firewall and for which reasons then you can turn up the log level to `Debug` and retrieve more detailed diagnostics:\n\n```csharp\n// In this example Serilog is used to log to the console,\n// but any .NET Core logger will work:\npublic class Program\n{\n    public static void Main(string[] args)\n    {\n        RunWebServer(args);\n    }\n\n    public static void RunWebServer(string[] args)\n    {\n        Log.Logger =\n            new LoggerConfiguration()\n                .MinimumLevel.Debug()\n                .WriteTo.Console()\n                .CreateLogger();\n        WebHost\n            .CreateDefaultBuilder(args)\n            .UseSerilog()\n            .UseStartup\u003cStartup\u003e()\n            .Build()\n            .Run();\n    }\n}\n```\n\nSample console output when log level is set to `Debug`:\n\n```\nHosting environment: Development\nContent root path: /Redacted/Firewall/samples/BasicApp\nNow listening on: https://localhost:5001\nNow listening on: http://localhost:5000\nApplication started. Press Ctrl+C to shut down.\n[09:04:31 DBG] Connection id \"0HLHNUJVHUQLD\" started.\n[09:04:31 DBG] Connection id \"0HLHNUJVHUQLE\" started.\n[09:04:31 INF] Request starting HTTP/1.1 GET http://localhost:5000/\n[09:04:31 DBG] Wildcard detected, all requests with hosts will be allowed.\n[09:04:31 DBG] Firewall.CountryRule: Remote IP Address '::1' has been denied access, because it couldn't be verified against the current GeoIP2 database..\n[09:04:31 DBG] Firewall.IPAddressRule: Remote IP Address '::1' has been denied access, because it didn't match any known IP address.\n[09:04:31 DBG] Firewall.IPAddressRangeRule: Remote IP Address '::1' has been denied access, because it didn't belong to any known address range.\n[09:04:31 DBG] Firewall.IPAddressRule: Remote IP Address '::1' has been denied access, because it didn't match any known IP address.\n[09:04:31 DBG] Firewall.IPAddressRangeRule: Remote IP Address '::1' has been denied access, because it didn't belong to any known address range.\n[09:04:31 DBG] Firewall.LocalhostRule: Remote IP Address '::1' has been granted access, because it originated on localhost.\n[09:04:31 DBG] Connection id \"0HLHNUJVHUQLD\" completed keep alive response.\n[09:04:31 INF] Request finished in 40.3263ms 200\n```\n\n## Contributing\n\nFeedback is welcome and pull requests get accepted!\n\n## Support\n\nIf you've got value from any of the content which I have created, but pull requests are not your thing, then I would also very much appreciate your support by buying me a coffee.\n\n\u003ca href=\"https://www.buymeacoffee.com/dustinmoris\" target=\"_blank\"\u003e\u003cimg src=\"https://www.buymeacoffee.com/assets/img/custom_images/yellow_img.png\" alt=\"Buy Me A Coffee\" style=\"height: auto !important;width: auto !important;\" \u003e\u003c/a\u003e\n\n## License\n\n[Apache 2.0](https://raw.githubusercontent.com/dustinmoris/Firewall/master/LICENSE)\n\n## Credits\n\nLogo is made by [Smashicons](https://www.flaticon.com/authors/smashicons) from [Flaticon](https://www.flaticon.com/) and is licensed under [Creative Commons 3.0](http://creativecommons.org/licenses/by/3.0/).","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdustinmoris%2Ffirewall","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdustinmoris%2Ffirewall","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdustinmoris%2Ffirewall/lists"}