IIS | Block page specific url except for specific internal IP address - iis

I am trying to block a url page specific (http://www.testdomain.com/login) for all IP addresses EXCEPT for an internal admin IP address. I have no issue blocking the pattern login but I want to test locally to make sure that the internal admin IP is excluded from the blocking rule for /login url. See what I have so far...
<rewrite>
<rules>
<rule name="RequestBlockingRule1" enabled="true" patternSyntax="Wildcard" stopProcessing="true">
<match url="*login*" negate="false" />
<conditions logicalGrouping="MatchAny" trackAllCaptures="true">
<add input="{HTTP_X_Forwarded_For}" pattern="92.102.130.65" />
</conditions>
<action type="None" />
</rule>
<rule name="RequestBlockingRule2" patternSyntax="Wildcard" stopProcessing="true">
<match url="*" />
<conditions>
<add input="{URL}" pattern="*login*" />
</conditions>
<action type="CustomResponse" statusCode="404" statusReason="File or directory not found." statusDescription="The resource you are looking for might have been removed, had its name changed, or is temporarily unavailable." />
</rule>
What I also want is to duplicate same rule but for a query string of http://www.testdomain.com/home.aspx?ctl=login
<rule name="RequestBlockingRule3" enabled="true" patternSyntax="Wildcard" stopProcessing="true">
<match url="*ctl=login*" negate="false" />
<conditions logicalGrouping="MatchAny" trackAllCaptures="true">
<add input="{HTTP_X_Forwarded_For}" pattern="93.107.170.85" />
</conditions>
<action type="None" />
</rule>
<rule name="RequestBlockingRule4" patternSyntax="Wildcard" stopProcessing="true">
<match url="*" />
<conditions>
<add input="{QUERY_STRING}" pattern="*ctl=login*" />
</conditions>
<action type="CustomResponse" statusCode="404" statusReason="File or directory not found." statusDescription="The resource you are looking for might have been removed, had its name changed, or is temporarily unavailable." />
</rule>
</rules>
</rewrite>
What I've done is tried to exclude internal IP for specific pattern and then followed with the actual blocking rule. Does anyone know either a) a better alternative or b) see what I may or may not be doing wrong (ideally I'd like to test these rules out locally before I use them on actual server using real IP address). Thanks

I want to suggest to use a bit different way:
Use rewrite maps for your whitelisted IPs list
Use only two rules, and do not use <action type="None" />
Config code is:
<rewrite>
<rules>
<rule name="Block login page" stopProcessing="true">
<match url="^login$" />
<conditions>
<add input="{Authorised Admin IPs:{REMOTE_ADDR}}" pattern="1" negate="true" />
</conditions>
<action type="CustomResponse" statusCode="403" statusReason="Forbidden" statusDescription="Forbidden" />
</rule>
<rule name="Block query string" stopProcessing="true">
<match url=".*" />
<conditions>
<add input="{Authorised Admin IPs:{REMOTE_ADDR}}" pattern="1" negate="true" />
<add input="{QUERY_STRING}" pattern="ctl=login" />
</conditions>
<action type="CustomResponse" statusCode="403" statusReason="Forbidden" statusDescription="Forbidden" />
</rule>
</rules>
<rewriteMaps>
<!-- This is your list of white-listed IP's-->
<rewriteMap name="Authorised Admin IPs">
<add key="92.102.130.65" value="1" />
<add key="93.107.170.85" value="1" />
<!-- local IPs-->
<add key="127.0.0.1" value="1" />
<add key="localhost" value="1" />
<add key="::1" value="1" />
</rewriteMap>
</rewriteMaps>
</rewrite>
This rule is blocking all requests to this URLs for all users, which has non white-listed IPs
http://www.testdomain.com/login
Any url, which has ctl=login in query string
In my config above, i am using {REMOTE_ADDR}. But you might need to use {HTTP_X_Forwarded_For}. It depends on you network infrastructure (if you have proxies or load balancers)
You can test this rules locally by adding/removing your local IP form rewrite map

Related

Create one rule to maintain multiple URLs

Windows Server 2012
I have 1 IIS container with 4 different URLs for one domain and 2 domains in total as below (set with the bindings)
www.site1.com
site1.com
www.site1.co.uk
site1.co.uk
www.site2.com
site2.com
www.site2.co.uk
site2.co.uk
www.site1.com - is the primary domain, meaning any combination of the URLs above must take the user to www.site1.com or www.site2.com
Below is the config i have for site 1 but site 2 is essentially the same. www.Site2.com not listed for brevity
<rule name="site1CoUk" patternSyntax="Wildcard" stopProcessing="true">
<match url="*" />
<conditions>
<add input="{HTTP_HOST}" pattern="site1.co.uk" />
</conditions>
<action type="Redirect" url="http://www.site1.com/{R:0}" />
</rule>
<rule name="site1WwwCoUk" patternSyntax="Wildcard" stopProcessing="true">
<match url="*" />
<conditions>
<add input="{HTTP_HOST}" pattern="www.site1.co.uk" />
</conditions>
<action type="Redirect" url="http://www.site1.com/{R:0}" />
</rule>
<rule name="site1Com" patternSyntax="Wildcard" stopProcessing="true">
<match url="*" />
<conditions>
<add input="{HTTP_HOST}" pattern="site1.com" />
</conditions>
<action type="Redirect" url="http://www.site1.com/{R:0}" />
</rule>
Everything seems to work but is there a way to reduce the configuration so i can maintain it within one rule (or 2 rules if i add in the www.site2.com). So i did something like this
<rule name="site1CoUk" patternSyntax="Wildcard" stopProcessing="true">
<match url="*" />
<conditions>
<add input="{HTTP_HOST}" pattern="site1.co.uk" />
<add input="{HTTP_HOST}" pattern="www.site1.co.uk" />
<add input="{HTTP_HOST}" pattern="site1.com" />
</conditions>
<action type="Redirect" url="http://www.site1.com/{R:0}" />
</rule>
but this doesnt work as soon as i add the second HTTP_HOST pattern.
The main reason is as i add more URLS it would easier to maintain under a one rule rather than adding a new rule each time round? Eventually the site would become https so i assume i could change the URL.

rewrite rule for folder to different host

There are 2 applications running on local network:
host headers:
http://ui.local, http://api.local
On another server, a website is already setup for url rewriting. Host header of the site is http://externalui.mydomain.com
Here is web.config contents:
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<system.webServer>
<rewrite>
<rules>
<rule name="ReverseProxyInboundRule1" stopProcessing="true">
<match url="(.*)" />
<action type="Rewrite" url="http://ui.local/{R:1}" />
</rule>
</rules>
</rewrite>
<httpProtocol>
<customHeaders>
<add name="Access-Control-Allow-Origin" value="*" />
<add name="Access-Control-Allow-Methods" value="GET,PUT,POST,DELETE,OPTIONS" />
<add name="Access-Control-Allow-Headers" value="Content-Type" />
</customHeaders>
</httpProtocol>
</system.webServer>
</configuration>
This url rewriting rule is running without a problem and rewrites all URLs made to http://externalui.mydomain.com to go to http://ui.local
However i want to write another rule so requests made to http://externalui.mydomain.com/api/.... will be forwarded to http://api.local/api/... instead.
How to write a rule for this condition?
You could use the below rule:
<rule name="redirect api" stopProcessing="true">
<match url="api/(.*)" />
<conditions logicalGrouping="MatchAll" trackAllCaptures="false" />
<action type="Redirect" url="http://api.local/api/{R:1}" />
</rule>
<rule name="all redirect" stopProcessing="true">
<match url="(.*)" />
<conditions logicalGrouping="MatchAll" trackAllCaptures="false">
</conditions>
<action type="Redirect" url="http://ui.local/{R:1}" />
</rule>
Note: make sure the API rule should be the first rule.

Priority for outboundRules for URL rewrite module

Will rule #2 get hit if rule #1 does first? Or will it stop at rule #1 for IIS Url Rewrite Module. I am trying to skip webfonts with the specific origin.
<outboundRules>
<rule name="Set Access-Control-Allow-Origin header">
<match serverVariable="RESPONSE_Access_Control_Allow_Origin" pattern=".*" />
<conditions logicalGrouping="MatchAll" trackAllCaptures="true">
<add input="{HTTP_ORIGIN}" pattern="(http(s)?://((.+\.)?(thestatbook\.com|localhost:3000)))" />
</conditions>
<action type="Rewrite" value="{C:0}" />
</rule>
<rule name="Enable CORS for Fonts">
<match serverVariable="RESPONSE_Access_Control_Allow_Origin" pattern=".*" />
<conditions>
<add input="{REQUEST_URI}" pattern="^[^\?]+\.(ttf|otf|eot|woff|woff2|svg)(\?.*)?$" />
</conditions>
<action type="Rewrite" value="*" />
</rule>
</outboundRules>
Will rule #2 get hit if rule #1 does first? Or will it stop at rule #1 for IIS Url Rewrite Module.
As far as I know, both outboundRules wil hit. It will firstly run "Set Access-Control-Allow-Origin header", then "Enable CORS for Fonts". You could write a simple rule to and use postman to test it.
Rule like below:
This rule will modify the Access-Control-Allow-Origin and Server variable.
<outboundRules>
<rule name="removingserverheader" enabled="true" stopProcessing="true">
<match serverVariable="RESPONSE_SERVER" pattern=".*" />
<action type="Rewrite" value="0" />
</rule>
<rule name="Enable CORS for Fonts">
<match serverVariable="RESPONSE_Access_Control_Allow_Origin" pattern=".*" />
<conditions>
<add input="{HTTP_ORIGIN}" pattern=".*" />
</conditions>
<action type="Rewrite" value="2" />
</rule>
</outboundRules>
Result:
If you just want only one rule is fired not hit another rule. I suggest you could try to use StopProcessing flag.
It means when the rule action is performed (i.e. the rule matched) and this flag is turned on, it means that no more subsequent rules will be processed and the request will be passed to the IIS request pipeline. By default, this flag is turned off.
Rule like below:
<rewrite>
<outboundRules>
<rule name="removingserverheader" enabled="true" stopProcessing="true">
<match serverVariable="RESPONSE_SERVER" pattern=".*" />
<action type="Rewrite" value="0" />
</rule>
<rule name="Enable CORS for Fonts" enabled="true">
<match serverVariable="RESPONSE_Access_Control_Allow_Origin" pattern=".*" />
<conditions>
<add input="{HTTP_ORIGIN}" pattern=".*" />
</conditions>
<action type="Rewrite" value="2" />
</rule>
</outboundRules>
</rewrite>
Result:

Limit IIS rewrite map for particular domain

My requirement is to need redirect url for particular domain.So i decided
to use rewrite map but what ever i tried below that will apply for all domain i have. Its not apply to particular domain.
Let me example more deeply. Suppose i have 3 domains on same IIS instance
www.abc.com
www.xyz.com
www.eee.com
In domain www.abc.com the url www.abc.com/legal should be redirected to www.abc.com/privacy.
Like above i have plenty of redirects url need to implement for the same domain.
Below listed things i had tried
<!--Test snippet 1-->
<rewrite>
<rules>
<rule name="Redirect rule1 for Redirects" enabled="true" stopProcessing="true">
<match url=".*" />
<conditions>
<add input="{HTTP_HOST}" pattern=".*www.abc.com.*" />
<add input="{ABCRedirects:{REQUEST_URI}}" pattern="(.+)" />
</conditions>
<action type="Redirect" url="{C:1}" appendQueryString="false" />
</rule>
</rules>
<rewriteMaps configSource="abc-rewritemaps.config" />
</rewrite>
<!--Test snippet 2-->
<rewrite>
<rules>
<rule name="Redirect rule1 for Redirects" enabled="true" stopProcessing="true">
<match url=".*www.abc.com.*" />
<conditions>
<add input="{HTTP_HOST}" pattern=".*www.abc.com.*" />
<add input="{ABCRedirects:{REQUEST_URI}}" pattern="(.+)" />
</conditions>
<action type="Redirect" url="{C:1}" appendQueryString="false" />
</rule>
</rules>
<rewriteMaps configSource="abc-rewritemaps.config" />
</rewrite>
<!--Test snippet 3-->
<rewrite>
<rules>
<rule name="Redirect rule1 for Redirects" enabled="true" stopProcessing="true">
<match url=".*" />
<conditions>
<add input="{ABCRedirects:{REQUEST_URI}}" pattern=".*www.abc.com.*" />
</conditions>
<action type="Redirect" url="{C:1}" appendQueryString="false" />
</rule>
</rules>
<rewriteMaps configSource="abc-rewritemaps.config" />
</rewrite>
<!--My Rewrite map-->
<rewriteMaps>
<rewriteMap name="ABCRedirects">
<add key="/legal" value="/privacy" />
<add key="/good" value="/bad" />
<add key="/hi" value="/bye" />
<add key="/no" value="/not" />
<add key="/123" value="/321" />
</rewriteMap>
</rewriteMaps>
Above all code i tried is applied for all domain, I cant able to limit its for only www.abc.com alone.
How to limit rewrite maps for particular domain?

One redirect to rule them all

After trying to help a fellow developer on the iis.net forums, I began to search for a better way to do my redirects. And thus I ended up with this article.
I took a lot of these ideas and began to implement my own version of it. Everything seemed to work just okay, but I hit a bump in the road and I hope you can help.
Okay so, a quick explanation on my rules:
My idea is to test the url, and if I find something wrong with it, I rewrite the url and let it go on, rather than redirect it instantly. If at any point I rewrite the url, I set a custom server variable "Redirect" to true. Then in the end, I test if my custom server variable is true and redirect the user.
The point of it is to only have one 301 redirect, rather than a chain of redirects.
These are my rules (Sorry for the wall):
<rules>
<rule name="WhiteList - resources" stopProcessing="true">
<match url="^resources/" />
<conditions logicalGrouping="MatchAll" trackAllCaptures="false" />
<action type="None" />
</rule>
<rule name="Redirect subdomains with www to non-www" stopProcessing="false">
<match url="(.*)" />
<conditions logicalGrouping="MatchAll" trackAllCaptures="false">
<add input="{HTTP_HOST}" pattern=".*localhost.*" negate="true" />
<add input="{HTTP_HOST}" pattern="^www\.(.*)\.([^\.]+)\.([^\.]+)$" />
</conditions>
<action type="Rewrite" url="http://{C:1}.{C:2}.{C:3}{HTTP_URL}" />
<serverVariables>
<set name="Redirect" value="true" />
</serverVariables>
</rule>
<rule name="Redirect top domains with non-www to www" stopProcessing="false">
<match url="(.*)" />
<conditions logicalGrouping="MatchAll" trackAllCaptures="false">
<add input="{HTTP_HOST}" pattern=".*localhost.*" negate="true" />
<add input="{HTTP_HOST}" pattern="^([^\.]+)\.([^\.]+)$" />
</conditions>
<action type="Rewrite" url="http://www.{HTTP_HOST}{HTTP_URL}" />
<serverVariables>
<set name="Redirect" value="true" />
</serverVariables>
</rule>
<rule name="SEO - Remove trailing slash" stopProcessing="false">
<match url="(.*)/$" />
<conditions>
<add input="{REQUEST_FILENAME}" matchType="IsFile" negate="true" />
<add input="{REQUEST_FILENAME}" matchType="IsDirectory" negate="true" />
</conditions>
<action type="Rewrite" url="{R:1}" />
<serverVariables>
<set name="Redirect" value="true" />
</serverVariables>
</rule>
<rule name="SEO - ToLower" stopProcessing="false">
<match url="(.*)" ignoreCase="false" />
<conditions logicalGrouping="MatchAll" trackAllCaptures="false">
<add input="{URL}" pattern="[A-Z]" ignoreCase="false" />
<add input="{URL}" pattern="^.*?\.(axd|css|js|jpg|jpeg|png|gif|ashx|asmx|svc).*?$" negate="true" />
<add input="{URL}" pattern="^.*/(webshop)/.*$" negate="true" />
</conditions>
<action type="Rewrite" url="{ToLower:{R:1}}" />
<serverVariables>
<set name="Redirect" value="true" />
</serverVariables>
</rule>
<rule name="SEO - remove default.aspx" stopProcessing="false">
<match url="(.*?)/?default\.aspx$" />
<action type="Rewrite" url="{R:1}" />
<serverVariables>
<set name="Redirect" value="true" />
</serverVariables>
</rule>
<rule name="SEO - Trim aspx" stopProcessing="false">
<match url="(.*)\.aspx$" />
<action type="Rewrite" url="{R:1}" />
<serverVariables>
<set name="Redirect" value="true" />
</serverVariables>
</rule>
<rule name="SEO - non-canonical redirect" stopProcessing="true">
<match url="^(.*)" />
<conditions>
<add input="{Redirect}" pattern="true" />
</conditions>
<action type="Redirect" url="{R:1}" />
<serverVariables>
<set name="Redirect" value="false" />
</serverVariables>
</rule>
</rules>
Now most of it actually works pretty well, but I seam to have some issues with a naked domain.
If I take a subdomain with www (which should be redirected to non-www) then it appears to fail. Funny enough, if I go to a subpage, it works fine.
(I can't post more urls because I'm new here :( And I wanted to give credits to the others for starting me on this.)
I've traced it down to the server variable {HTTP_URL} because if I remove it, it doesn't fail. However it of course doesn't do what it is supposed to either (It always redirects to the naked domain). I've tried with various variables: {URL}, {REQUEST_URI} and they all seem to end up with the same error, which is getting a bit annoying.
Should anyone have some improvement for the rules, feel free to respond as well, I love working with them and I would like to make the near perfect redirects, so any suggestions are welcome.
This ended up being more of a version 1 and I have since improved on it to avoid the server variable.
Now I use a series of rewrite rules that transforms the url to something I like, appending underscore.
Then stripping the underscore and then redirecting.
It have worked very well for me for quite some time now and is being used by the company on almost all projects.
It have been made into a nuget package for easy setup.
http://www.nuget.org/packages/RedirectRules/
I believe I actually found the answer now. I worked around with it for some time and ended up with another issue, again with the www vs non-www. It simply didn't do anything.
So I ended up putting it at the end and making that redirects rather than rewrites as well. Apparently that works fine, why? No idea.
cheesemacfly suggested that I match on ^(.+)$ rather than (.*).
I thought it would do the very same thing, but realized that the match isn't on the hostname at all, it is everything after. Big surprise for me there, but it did open up a new possibility:
Instead of matching {URL}, {HTTP_URL} or some other variant of almost the same thing, I could simply refer to the match with {R:1}
I have put in two things since the first post. I put in conditions on some of the rules to exclude them if it contains "umbraco" as I work with that system and most of the rules I don't want to apply to the back-end. I also applied a rewrite map for SSL check, so it automatically puts you to https:// or http:// using the server variable {HTTPS}
So far I haven't had found any bugs in it, so I will mark this as solved and give you my last revision of the rules. Enjoy :)
<rewrite>
<rules>
<rule name="WhiteList - resources" stopProcessing="true">
<match url="^resources/" />
<conditions logicalGrouping="MatchAll" trackAllCaptures="false" />
<action type="None" />
</rule>
<rule name="SEO - Remove trailing slash" stopProcessing="false">
<match url="(.*)/$" />
<conditions>
<add input="{REQUEST_FILENAME}" matchType="IsFile" negate="true" />
<add input="{REQUEST_FILENAME}" matchType="IsDirectory" negate="true" />
</conditions>
<action type="Rewrite" url="{R:1}" />
<serverVariables>
<set name="Redirect" value="true" />
</serverVariables>
</rule>
<rule name="SEO - ToLower" stopProcessing="false">
<match url="(.*)" ignoreCase="false" />
<conditions logicalGrouping="MatchAll" trackAllCaptures="false">
<add input="{URL}" pattern="[A-Z]" ignoreCase="false" />
<add input="{URL}" pattern="^.*?\.(axd|css|js|jpg|jpeg|png|gif|ashx|asmx|svc).*?$" negate="true" />
<add input="{URL}" pattern="^.*/(webshop)/.*$" negate="true" />
<add input="{URL}" pattern="^/umbraco/" negate="true" />
</conditions>
<action type="Rewrite" url="{ToLower:{R:1}}" />
<serverVariables>
<set name="Redirect" value="true" />
</serverVariables>
</rule>
<rule name="SEO - remove default.aspx" stopProcessing="false">
<match url="(.*?)/?default\.aspx$" />
<conditions logicalGrouping="MatchAll" trackAllCaptures="false">
<add input="{URL}" pattern="^/umbraco/" negate="true" />
</conditions>
<action type="Rewrite" url="{R:1}" />
<serverVariables>
<set name="Redirect" value="true" />
</serverVariables>
</rule>
<rule name="SEO - Trim aspx" stopProcessing="false">
<match url="(.*)\.aspx$" />
<action type="Rewrite" url="{R:1}" />
<conditions logicalGrouping="MatchAll" trackAllCaptures="false">
<add input="{URL}" pattern="^/umbraco/" negate="true" />
</conditions>
<serverVariables>
<set name="Redirect" value="true" />
</serverVariables>
</rule>
<rule name="Redirect subdomains with www to non-www" stopProcessing="false">
<match url="(.*)" />
<conditions logicalGrouping="MatchAll" trackAllCaptures="false">
<add input="{HTTP_HOST}" pattern=".*localhost.*" negate="true" />
<add input="{HTTP_HOST}" pattern="^www\.(.*)\.([^\.]+)\.([^\.]+)$" />
</conditions>
<action type="Redirect" url="{MapSSL:{HTTPS}}{C:1}.{C:2}.{C:3}/{R:1}" redirectType="Permanent" />
<serverVariables>
<set name="Redirect" value="true" />
</serverVariables>
</rule>
<rule name="Redirect top domains with non-www to www" stopProcessing="false">
<match url="(.*)" />
<conditions logicalGrouping="MatchAll" trackAllCaptures="false">
<add input="{HTTP_HOST}" pattern=".*localhost.*" negate="true" />
<add input="{HTTP_HOST}" pattern="^([^\.]+)\.([^\.]+)$" />
</conditions>
<action type="Rewrite" url="{MapSSL:{HTTPS}}www.{HTTP_HOST}/{R:1}" />
<serverVariables>
<set name="Redirect" value="true" />
</serverVariables>
</rule>
<rule name="SEO - non-canonical redirect" stopProcessing="true">
<match url="(.*)" />
<conditions>
<add input="{Redirect}" pattern="true" />
</conditions>
<action type="Redirect" url="{R:1}" redirectType="Permanent"/>
<serverVariables>
<set name="Redirect" value="false" />
</serverVariables>
</rule>
</rules>
<rewriteMaps>
<rewriteMap name="MapSSL" defaultValue="OFF">
<add key="ON" value="https://" />
<add key="OFF" value="http://" />
</rewriteMap>
</rewriteMaps>
</rewrite>
There is a caveat though: Notice that a custom server variable is used. You cannot use a custom server variable out of the box. You need to add this variable to the <allowedServerVariables/> element in the applicationHost.config file, which is a global configuration file for IIS. (Source)
If you're using shared hosting (Including Azure Web Sites), chances are that you will not be able to change this setting.
If you're using an Azure Web Role, you'll need to add a startup task that modifies the aforementioned configuration file. If you don't do this, you'll get an IIS 500 error.
If someone have suggestions as to what could make the rules better, they are still welcome.

Resources