What I am trying to do?
I am setting up blue-green deployment to achieve zero down-time deployment.
How is the implementation?
- I have one server.
- I created a web farm.
- I set 3 websites blue,green,main at different ports. Main handles all requests from the clients and rewrites requests to web farm.
- Web farm routes the request to the active website (blue or green) via ARR (Application Request Routing)
- Since blue/green websites receiving the request from Web Farm/ARR, the REMOTE_ADDR (client ip address) shows 127.0.0.1.
- I do not want it though it is expected, I have created a URL Rewrite rule that sets the HTTP_X_FORWARDED_FOR_INTERNAL server variable with REMOTE_ADDR at main website.
- I have created another rule on blue/green websites that sets REMOTE_ADDR server variable with HTTP_X_FORWARDED_FOR_INTERNAL that comes from main website.
- At result, I am getting the REMOTE_ADDR as real client ip address. YAY!
- Everything works fine with single client user.
What is not working as expected?
When I connect to the website from different ip addresses, -especially for static contents- blue/green applications receiving wrong ip addresses(REMOTE_ADDR) which are owned by other client.
Example:
- A and B are different IP addresses.
- A connects to foo.com/somestaticfile.css.
- B connects to foo.com/somestaticfile.css.
When I check the IIS Application logs, the log of the request coming from B shows the ip address as 'A' although it should be B!
I can also confirm that request is coming from B according the cookie, we are holding ip address at the cookie, and the ip address at the cookie is not equal to REMOTE_ADDR according to IIS app log.
Edit: I captured another problem, "sometimes" some of the javascript response's mime type is becoming "text/html" in responses, since we implement X-Content-Type-Options=nosniff, those scripts are not loading by browser.
Refused to execute script from 'https://foo.com/bundles/scripts/common?v=Pc3UWD-GF8GEIazC15mnIr_UYtcH3cQPlDPwAf2cXtU1' because its MIME type ('text/html') is not executable, and strict MIME type checking is enabled.
Edit 2: I have found something useful. When I visit some link that returns 40x status code. The error related with the IP address reproducing every time. And here is the URL that I have being redirected, after I got unauthenticated.
The decoded value of the returnUrl is: "/error/notfound/?404;https://foo.com:10003/error/notfound"
Weird?
What I have tried to solve the situation?
I thought it is some kind of caching issue then I disabled all the caching mechanisms I know which are;
- Disabled IIS Cache from global settings.
- Disabled ARR Cache via creating cache control rule.
- Disabled Web Farm Cache.
It did not work though.
I have cleared all the configurations and used ARR Helper which is developed for forwarding IP address to hindmost web application in configurations like this, I encountered with the same problem.
Configuration Files You May Need
Rule that sets HTTP_X_FORWARDED_FOR_INTERNAL in main web app.
<rule name="Set HTTP_X_FORWARDED_FOR_INTERNAL">
<match url="(.*)" />
<conditions logicalGrouping="MatchAll" trackAllCaptures="false" />
<serverVariables>
<set name="HTTP_X_FORWARDED_FOR_INTERNAL" value="{REMOTE_ADDR}" />
</serverVariables>
<action type="None" />
</rule>
Rule that sets REMOTE_ADDR in blue/green apps.
<rule name="Set REMOTE_ADDR">
<match url="(.*)" />
<conditions logicalGrouping="MatchAll" trackAllCaptures="false">
<add input="{HTTP_X_FORWARDED_FOR_INTERNAL}" pattern="^$" negate="true" />
</conditions>
<serverVariables>
<set name="REMOTE_ADDR" value="{HTTP_X_FORWARDED_FOR_INTERNAL}" />
<set name="REMOTE_HOST" value="{HTTP_X_FORWARDED_FOR_INTERNAL}" />
</serverVariables>
<action type="None" />
</rule>
Rule that disabled ARR caching at the global level.
<rewrite>
<globalRules>
<rule name="ARR_CacheControl_9dc69ea7-26f6-4654-af58-2e289d681463" enabled="true" patternSyntax="Wildcard" stopProcessing="true">
<match url="*" />
<serverVariables>
<set name="ARR_CACHE_CONTROL_OVERRIDE" value="1,no-cache" />
</serverVariables>
<action type="None" />
</rule>
</globalRules>
</rewrite>
Whole web farm configuration
<webFarms>
<webFarm name="iissucks" enabled="true">
<server address="bluecloud" enabled="true">
<applicationRequestRouting httpPort="880" httpsPort="8443" />
</server>
<server address="greencloud" enabled="true">
<applicationRequestRouting httpPort="980" httpsPort="9443" />
</server>
<applicationRequestRouting>
<healthCheck url="https://iissucks/account/signin" interval="00:00:01" statusCodeMatch="200-599" responseMatch="I_am_healthy." />
<protocol xForwardedForHeaderName="X-Forwarded-For-Farm" includePortInXForwardedFor="false">
<cache enabled="false" queryStringHandling="NoCaching" validationInterval="00:00:00" />
</protocol>
</applicationRequestRouting>
</webFarm>
<applicationRequestRouting>
<hostAffinityProviderList>
<add name="Microsoft.Web.Arr.HostNameRoundRobin" />
</hostAffinityProviderList>
</applicationRequestRouting>
</webFarms>