Keycloak behind IIS Reverse Proxy - iis

I just cant get my reverse proxy to work properly and I hope that someone can help me with that.
A few Informations:
1 VM Windows Server 2019,IIS 10 (with ARR and URL Rewrite Module),"keycloak" App under the "Default Website", proxy-address-forwarding="true" were added in the standalone.xml
keycloak is locally available at "localhost:8080". The default website is available from intern network (Port 80 is open, 8080 is closed). I want to use the IIS as a reverse proxy for keycloak. It should look like the following URL.
Request: "http://server_fqdn/keycloak" -> IIS Reverse Proxy -> localhost:8080
At first I tried it without "/keycloak" and it worked perfectly. After I added "/keycloak" it just shows 404 Errors. I saw that it tries to open "http://server_fqdn/auth" instead of "http://server_fqdn/keycloak/auth". If I enter "/keycloak/auth" manually it works. My first thought was to just write another Blank Inbound Rule which redirects "http://server_fqdn/auth" to "http://server_fqdn/keycloak/auth". It works, but now there is another problem. If i want to enter the admin console I get an error which says "Invalid parameter: redirect_uri".
It stops at the following URL (not complete, but the necessary part is there)
http://server_fqdn/keycloak/auth/realms/master/protocol/openid-connect/auth?client_id=security-admin-console&redirect_uri=http%3A%2F%2Fserver_fqdn%2Fkeycloak%2Fauth%2Fadmin%2Fmaster%2Fconsole%2F&state=.....
If i remove %2Fkeycloak after "redirect_uri" it works and i get the keycloak login screen. Maybe someone can help me here.
Inbound Reverse Proxy Rule:
<rule name="ReverseProxyInboundRule1" enabled="true" stopProcessing="true">
<match url="^keycloak/(.*)" />
<conditions logicalGrouping="MatchAll" trackAllCaptures="false" />
<action type="Rewrite" url="http://localhost:8080/{R:1}" />
<serverVariables>
</serverVariables>
</rule>
Outbound Reverse Proxy Rule:
<outboundRules>
<clear />
<rule name="ReverseProxyOutboundRule1" preCondition="ResponseIsHtml1" enabled="true">
<match filterByTags="A, Area, Base, Form, Head, IFrame, Img, Input, Link, Script" pattern="^http://localhost:8080/(.*)" />
<conditions logicalGrouping="MatchAll" trackAllCaptures="true">
</conditions>
<action type="Rewrite" value="http://server_fqdn/keycloak/{R:1}" />
</rule>
<preConditions>
<preCondition name="ResponseIsHtml1">
<add input="{RESPONSE_CONTENT_TYPE}" pattern="^text/html" />
</preCondition>
</preConditions>
</outboundRules>
Inbound Reverse Proxy Rule:
<rule name="keycloak" enabled="true" stopProcessing="true">
<match url="^auth/(.*)" />
<conditions logicalGrouping="MatchAll" trackAllCaptures="false" />
<action type="Redirect" url="http://server_fqdn/keycloak/{R:0}" redirectType="Permanent" />
</rule>

Related

IIS reverse proxy outbound rule doesn't work

I have a web site under IIS 10.0 v1809. A specific link https://domain.name/report/ should be proxied to the local service http://localhost:7577/dashboard/. So I need to change the word dashboard to report in the server answer. To do this I set up ReverseProxy rule:
<rule name="MyReverseProxy" enabled="true" stopProcessing="true">
<match url="report(/)?(.*)" />
<action type="Rewrite" url="http://localhost:7577/dashboard/{R:2}" appendQueryString="true" />
</rule>
And the OutBound rule:
<outboundRules>
<rule name="MyReverseProxy" enabled="true" stopProcessing="false">
<match filterByTags="None" pattern="dashboard(/)?(.*)" />
<action type="Rewrite" value="report/{R:2}" />
</rule>
<preConditions>
<preCondition name="ResponseIsHtml1">
<add input="{RESPONSE_CONTENT_TYPE}" pattern="^text/html" />
</preCondition>
</preConditions>
</outboundRules>
but for some reason, the outbound rule does not work for me. I get a 404 error and https://domain.name/dashboard/ appears in the browser's address bar.
What am I doing wrong? My pattern test works correctly, but why does the browser switch to dashboard?

Remove Namespace Prefix From MediaWiki URL with IIS10 URL Rewrite

I have a MediaWiki installation hosted in IIS10. I am attempting to use the URL Rewrite module to remove the namespace prefix from some MediaWiki article URLs.
In MediaWiki, articles outside the "Main" namespace show as foo.com/wiki/Namespace:Article_Title
I only need to remove the 'Bar:' Namespace prefix, others can remain intact
Restriction: I am unable to move these articles into the Main namespace
The desired result would present content from foo.com/wiki/Bar:Article_Title but display as foo.com/wiki/Article_Title.
Existing Inbound URL Rewrite
I have one URL rewrite already in place, per Microsoft's doc for MediaWiki on IIS, which removes the query string from the url. Here is my rewrite rule based on that guidance:
<rule name="Clean-WikiArticlePages" stopProcessing="false">
<match url="^wiki/(.*)$" />
<conditions logicalGrouping="MatchAll" trackAllCaptures="false" />
<action type="Rewrite" url="/w/index.php?title={UrlEncode:{R:1}}" />
</rule>
What I've tried so far
I've tried both an Inbound and Outbound rule, to no effect.
Inbound rule:
<rule name="Clean-Namespace" enabled="true">
<match url="^wiki/(Meetings:)(.*)" />
<action type="Rewrite" url="/wiki/{R:2}" />
</rule>
Outbound rule:
<outboundRules>
<rule name="Remove-Namespace" preCondition="IsHTML" enabled="true">
<match filterByTags="A" pattern="^wiki/Bar:(.*)" />
<action type="Rewrite" value="/wiki/{R:1}" />
</rule>
<preConditions>
<preCondition name="IsHTML">
<add input="{RESPONSE_CONTENT_TYPE}" pattern="^text/html" />
</preCondition>
</preConditions>
</outboundRules>
I think it is possible to achieve this if you only want to rewrite for Bar.
You want the URL displayed as foo.com/wiki/Article_Title and load content from /wiki/Bar:Article_Title?
Then you should redirect all /wiki/Bar:Article_Title request to foo.com/wiki/Article_Title. Then rewrite foo.com/wiki/Article_Title back to /wiki/Bar:Article_Title request.
But please keep in mind this only work for static namespace. Because the rewritten request unable to get the namespace of original request before redirection.
Not sure if below rules achieve your requirement
When you access
foo.com/wiki/Bar:Article_Title ------redirect----->foo.com/wiki/Article_Title
-------rewrite------> foo.com/wiki/Bar:Article_Title-------rewrite---->foo.com/w/index.php?title=bar%3AArticle_Title
<rule name="redirect" stopProcessing="true">
<match url="(.*)" />
<conditions>
<add input="{REQUEST_URI}" pattern="^/wiki/bar:Article_Title$" />
</conditions>
<action type="Redirect" url="wiki/Article_Title" redirectType="Temporary" />
</rule>
<rule name="rewrite">
<match url="^wiki/Article_Title$" />
<action type="Rewrite" url="wiki/bar:Article_Title" />
</rule>
<rule name="Clean-WikiArticlePages" enabled="true" stopProcessing="true">
<match url="^wiki/(.*)$" />
<conditions logicalGrouping="MatchAll" trackAllCaptures="false" />
<action type="Rewrite" url="/w/index.php?title={UrlEncode:{R:1}}" />
</rule>

IIS Rewrite IP:Port to {domain}/{thing}

What I'm trying to do:
I own a domain, let's say blahblahblah.com
On the server, it points to :80 and :443 properly - this is working
I want to remap so that if a user visits blahblahblah.com/thing1 then it maps to localhost:5000
Similarly, I also want to remap so that if a user visits blahblahblah.com/thing2 then it maps to another server and port on my local network (not exposed to the internet)
I essentially want to use my webserver to communicate with other servers on my network and other ports through "sub directories" or whatever they are called in this instance (site/subdir, site2/subdir). I've been trying to solve this issue for days! I've installed ARR (and enabled the Proxy) and the URL Rewrite modules on my IIS.
I've attempted god knows how many different rewrite templates on SO. I can get partial success, in which the route /thingx/ resolves, but all the resources are referencing blahblahblah.com and (appropriately) can't find the resources for the site at that address.
Current rewrite (resolves some resources at blahblahblah.com/thing1, but others are misrouted):
<rewrite>
<rules>
<rule name="Thing1" enabled="true">
<match url="(thing1*)" />
<action type="Rewrite" url="http://localhost:5000/{R:0}" />
</rule>
</rules>
</rewrite>
EDIT: I've updated my rules to the below, as per this article from Microsoft Docs: Reverse Proxy with URL Rewrite v2 and Application Request Routing . These rewrites supposedly do exactly what I need, but the result is not the same in my case, and I still have no clue why.
<rules>
<rule name="Thing1" enabled="true">
<match url="^thing1/(.*)" />
<action type="Rewrite" url="https://localhost:5000/{R:0}" />
</rule>
</rules>
<outboundRules>
<rule name="Add application prefix" preCondition="IsHTML">
<match filterByTags="A" pattern="^/(.*)" />
<conditions>
<add input="{URL}" pattern="^/(thing1)/.*" />
</conditions>
<action type="Rewrite" value="/{C:1}/{R:1}" />
</rule>
<preConditions>
<preCondition name="IsHTML">
<add input="{RESPONSE_CONTENT_TYPE}" pattern="^text/html" />
</preCondition>
</preConditions>
</outboundRules>
More Notes:
localhost:5000 works as expected
localhost/thing1 resolves in 404 Not Found
blahblahblah.com/thing1 resolves in pulling in the default HTML page, but none of the assets (javascript, css, etc), they return 404 Not Found.
EDIT: It may be important to note that these rules have been written into the default site (bound to :80, :443) on IIS' web.config. Writing these rules in C:\Windows\System32\inetsrv\config\applicationHost.config results in a 500.
Any feedback on what I'm doing wrong?
Part of this was due to the way SPAs are handled (# and history mode), but I ended up solving with the below webconfig for IIS, which allows me to nest everything under a /foobar endpoint:
<rewrite>
<rules>
<rule name="Subdirectory Index" stopProcessing="true">
<match url="^foobar/index\.html$" ignoreCase="false" />
<action type="None" />
</rule>
<rule name="Subdirectory Routes" stopProcessing="true">
<match url="." ignoreCase="false" />
<conditions>
<add input="{REQUEST_FILENAME}" matchType="IsFile" ignoreCase="false" negate="true" />
<add input="{REQUEST_FILENAME}" matchType="IsDirectory" ignoreCase="false" negate="true" />
</conditions>
<action type="Rewrite" url="/foobar/index.html" />
</rule>
</rules>
</rewrite>

IIS Rewrite Rule is not setting HTTP_COOKIE

I'm working with a client who has a mobile site setup through a third party. Currently, we check through IIS whether a user agent matches any of your standard mobile agents in which case we redirect the user to the mobile version m.whatever.com.
One of the rules we have requires us to set the cookie to a value of 0 when the user wants to see the mobile site again.
<rules>
<rule name="if httpcookie is , set it to 0" stopProcessing="true">
<match url="^(.*)$" />
<conditions>
<add input="{HTTP_COOKIE}" pattern="mobileoptout=1" />
</conditions>
<serverVariables>
<set name="HTTP_COOKIE" value="mobileoptout=0" />
</serverVariables>
<action type="None" />
</rule>
</rules>
As per the above, we're matching on the URL and the cookie value. I have tested these independently and they work as expected. However, at the end of this rule, the value for the cookie MobileOptOut is still 1 and not 0.
I've searched and tried all the examples available on numerous sites but have been completely unable to understand why the value of the cookie is not being changed.
The domain for the cookie is [whatever.com] which is the same as www.whatever.com and based on previous tests it can read from the cookie to validate a condition.
Any ideas?
Including an additional attempt which does not work either:
<rules>
<rule name="set cookie">
<match url="(.*)" />
<serverVariables>
<set name="HTTP_COOKIE" value="optout=1" />
<set name="{HTTP_COOKIE}" value="optout=2" />
</serverVariables>
<action type="None" />
</rule>
</rules>
Thanks to #cheesemacfly I looked into using the outbound rules, which for some reason I completely overlooked.
The solution uses a single inbound rule to check two conditions.
<rule name="MobileOptOut=1 Stop Processing Rules" stopProcessing="true">
<match url="(.*)" />
<conditions logicalGrouping="MatchAny" trackAllCaptures="false">
<add input="{QUERY_STRING}" pattern="mobileoptout=1" />
<add input="{HTTP_COOKIE}" pattern="MobileOptOut=1" />
</conditions>
<action type="None" />
</rule>
This will handle the first request with the querystring and all subsequent requests with the cookie that will be created after this rule finishes.
The outbound rule is as follows:
<outboundRules>
<rule name="if querystring=1" preCondition="If mobileoptout query = 1">
<match serverVariable="RESPONSE_Set_Cookie" pattern="." />
<action type="Rewrite" value="mobileoptout=1; Domain=site.local; Path=/;" />
</rule>
<preConditions>
<preCondition name="If mobileoptout query = 1">
<add input="{QUERY_STRING}" pattern="mobileoptout=1" />
</preCondition>
</preConditions>
</outboundRules>
This will check that first condition of if querystring is mobileoptout=1. If true, it will set a cookie in the response named 'mobileoptout' with a value of 1 in the root domain that will expire with the session.
This is exactly what I was overlooking.
First, to make sure your rule can work, you have to setup the allowed server variables.
To do so, in the iis manager, under the URL Rewrite section, click on View Server Variables...:
You then can add a new variable by clicking Add... on the right.
In your case, you want to add HTTP_COOKIE:
From here, your rule (as following) should be working:
<rules>
<rule name="if httpcookie is , set it to 0" stopProcessing="true">
<match url="^(.*)$" />
<conditions>
<add input="{HTTP_COOKIE}" pattern="mobileoptout=1" />
</conditions>
<serverVariables>
<set name="HTTP_COOKIE" value="mobileoptout=0" />
</serverVariables>
<action type="None" />
</rule>
</rules>
Note that the cookie is rewritten only for the request.
Meaning the client cookies won't be changed but the request hitting the page will have the new cookie values when the rule is triggered.

IIS reverse proxy to nodejs app via https problem

I am building a nodejs api that is hosted by iis. It works perfectly over (using port 5000) http. However, when trying to use the https connection if fails(ssl are installed). I either get a connection failed message or a refused message.
<handlers accessPolicy="Read, Execute, Script" />
<rewrite>
<rules>
<rule name="ReverseProxyInboundRule1" stopProcessing="true">
<match url="(.*)" />
<action type="Rewrite" url="http://localhost:5000/{R:1}" />
<conditions>
<add input="{HTTPS}" pattern="off" />
</conditions>
</rule>
<rule name="ReverseProxyInboundRule2" stopProcessing="true">
<match url="(.*)" />
<action type="Rewrite" url="http://api.mysite.com:5000/{R:1}" />
</rule>
</rules>
<outboundRules>
<preConditions>
<preCondition name="ResponseIsHtml1">
<add input="{RESPONSE_CONTENT_TYPE}" pattern="^text/html" />
</preCondition>
</preConditions>
</outboundRules>
</rewrite>
Seems like IIS is refusing the connection. any help is appreciated.
According to your url rewrite rule, I found all your https request will match the ReverseProxyInboundRule2 and rewrite to "http://api.mysite.com:5000". I suggest you could firstly check you could access the "http://api.mysite.com:5000".
I guess there is something wrong with this url. It seems you doesn't bind the domain in the IIS binding configuration.
I suggest you could try to open the IIS web application binding to add the domain for the http://api.mysite.com.

Resources