Reverse Proxy to Secure Node Server with IIS

Mike Dodge 0 Reputation points
2023-03-06T19:25:58.5833333+00:00

This has been a long difficult journey of getting our local (not public facing) site secure. The only reason I need to do this is so that our ipads can take pictures through the app: https://github.com/mabelanger/react-html5-camera-photo#required-working-environment-for-getusermedia

So I have the front end self signed certificate set up and the site loads securely beautifully. I set a up a reverse proxy to my Node server that was unsecured and that was working nicely. Until I discovered that my websockets were no longer working. Its trying to load websockets from an unsecure websocket server which from my understanding cannot be done when the front end is secure. Mixed content issues. So I tried to create a second SSL certificate for my Node server using the steps I found here: https://stackoverflow.com/a/49784278/571723

So after doing all that and getting the cert trusted, I can no longer load the site because the backend api calls are erroring out with a "HTTP Error 502.3 - Bad Gateway - A security error occurred". I have a feeling the problem is happening in the URL Rewrite. I say this because if I go on the web server and type in the actual URL of the backend api call that it is supposed to reverse proxy to (https://example.domain.local:3008/getDatabaseName), that actually works just fine. But this call, https://example.domain.local/api/getDatabaseName, fails with the above error. And my rewrite/proxy is supposed to send that api call to the 3008 URL. I swapped in the "example.domain" section in those URL's to hide critical data.

If I am going down the wrong path completely here, I would love some input. From everything I have researched(A LOT!), this seems to me the right way to go. I was told by one person to use reverse proxy to the insecure Node server and another person said to secure the Node server but don't use reverse proxy. But it seems like I need both now.

Here is my full web.config (with swapped in "example.domain"):

<?xml version="1.0" encoding="UTF-8"?>
<configuration>
	<location path="index.html">
    <system.webServer>
		<httpProtocol>
			<customHeaders>
			  <add name="Cache-Control" value="no-store, max-age=0" />
			</customHeaders>
		  </httpProtocol>
		</system.webServer>
	</location>
    <system.webServer>
        <rewrite>
            <rules>
                <clear />
                <rule name="HTTPS Redirect" stopProcessing="true">
                    <match url="(.*)" />
                    <conditions logicalGrouping="MatchAll" trackAllCaptures="false">
                        <add input="{HTTPS}" pattern="^OFF$" />
                    </conditions>
                    <action type="Redirect" url="https://{HTTP_HOST}{REQUEST_URI}" appendQueryString="false" />
                </rule>
                <rule name="Static Assets" enabled="false" stopProcessing="true">
                    <match url="([\S]+[.](html|htm|svg|js|css|png|gif|jpg|jpeg))" />
                    <conditions logicalGrouping="MatchAll" trackAllCaptures="false" />
                    <action type="Rewrite" url="/{R:1}" />
                </rule>
                <rule name="Reverse Proxy Thumbnails to Node Server" stopProcessing="true">
                    <match url="^thumbs/(.*)" />
                    <conditions logicalGrouping="MatchAll" trackAllCaptures="false" />
                    <serverVariables>
                        <set name="HTTP_X_ORIGINAL_ACCEPT_ENCODING" value="{HTTP_ACCEPT_ENCODING}" />
                        <set name="HTTP_ACCEPT_ENCODING" value="" />
                    </serverVariables>
                    <action type="Rewrite" url="http://example.domain.local:3008/thumbs/{R:1}" />
                </rule>
                <rule name="Reverse Proxy Images to Node Server" stopProcessing="true">
                    <match url="^images/(.*)" />
                    <conditions logicalGrouping="MatchAll" trackAllCaptures="false" />
                    <serverVariables>
                        <set name="HTTP_X_ORIGINAL_ACCEPT_ENCODING" value="{HTTP_ACCEPT_ENCODING}" />
                        <set name="HTTP_ACCEPT_ENCODING" value="" />
                    </serverVariables>
                    <action type="Rewrite" url="http://example.domain.local:3008/images/{R:1}" />
                </rule>
                <rule name="Reverse Proxy API Calls to Node Server" enabled="true" stopProcessing="true">
                    <match url="^api/(.*)" />
                    <conditions logicalGrouping="MatchAll" trackAllCaptures="false" />
                    <serverVariables>
                        <set name="HTTP_X_ORIGINAL_ACCEPT_ENCODING" value="{HTTP_ACCEPT_ENCODING}" />
                        <set name="HTTP_ACCEPT_ENCODING" value="" />
                    </serverVariables>
                    <action type="Rewrite" url="https://example.domain.local:3008/{R:1}" logRewrittenUrl="true" />
                </rule>
                <rule name="React Routes" stopProcessing="true">
                    <match url=".*" />
                    <conditions logicalGrouping="MatchAll" trackAllCaptures="false">
                        <add input="{REQUEST_FILENAME}" matchType="IsFile" negate="true" />
                        <add input="{REQUEST_FILENAME}" matchType="IsDirectory" negate="true" />
                        <add input="{REQUEST_URI}" matchType="Pattern" pattern="api/(.*)" negate="true" />
                    </conditions>
                    <action type="Rewrite" url="/index.html" />
                </rule>
				<!--  this rule will route all requests through the index.html so React can load the requested URL correctly -->
			</rules>
            <outboundRules>
                <rule name="ReverseProxyOutboundRule1" preCondition="ResponseIsTextHtml" enabled="true" stopProcessing="false">
                    <match filterByTags="None" pattern="^(.*?)\s" />
                    <action type="Rewrite" value="{R:1}" />
                </rule>
                <rule name="RestoreAcceptEncoding" preCondition="NeedsRestoringAcceptEncoding" enabled="true">
                    <match serverVariable="HTTP_ACCEPT_ENCODING" pattern="^(.*)" />
                    <action type="Rewrite" value="{HTTP_X_ORIGINAL_ACCEPT_ENCODING}" />
                </rule>
                <rule name="AnchorTagsRule" preCondition="ResponseIsTextAnything" enabled="false">
                    <match pattern="href=(.*?)http://example.domain.local:3008/(.*?)\s" />
                    <action type="Rewrite" value="href={R:1}https://example.domain.local/{R:2}" />
                </rule>
                <rule name="FormTagsRule" preCondition="ResponseIsTextAnything" enabled="false">
                    <match pattern="action=(.*?)http://example.domain.local:3008/(.*?)\\" />
                    <action type="Rewrite" value="action={R:1}https://example.domain.local/{R:2}\" />
                </rule>
                <preConditions>
					<preCondition name="ResponseIsTextHtml">
                        <add input="{RESPONSE_CONTENT_TYPE}" pattern="^text/html" />
                    </preCondition>
                    <preCondition name="ResponseIsTextAnything">
                        <add input="{RESPONSE_CONTENT_TYPE}" pattern="^text/(.+)" />
                    </preCondition>
                    <preCondition name="NeedsRestoringAcceptEncoding">
                        <add input="{HTTP_X_ORIGINAL_ACCEPT_ENCODING}" pattern=".*" />
                    </preCondition>
                </preConditions>
            </outboundRules>
        </rewrite>
        <urlCompression doStaticCompression="false" />
        <tracing>
            <traceFailedRequests>
                <add path="*">
                    <traceAreas>
                        <add provider="WWW Server" areas="Rewrite,RequestRouting" verbosity="Verbose" />
                    </traceAreas>
                    <failureDefinitions statusCodes="200-399" />
                </add>
            </traceFailedRequests>
        </tracing>
    </system.webServer>
</configuration>

Windows development Internet Information Services
{count} votes

Your answer

Answers can be marked as Accepted Answers by the question author, which helps users to know the answer solved the author's problem.