Publish Azure Web Role Without SSL Cert Fails - azure

I have an azure web role project that for the production site has an ssl cert, let's call it https://www.myapp.com. I have no issues with publishing this and it works as expected.
However, for testing purposes, we have another site spun up that we don't need ssl on. It runs on the default azure site name, http://myapp-test.cloudapp.net. Ever since I added the cert for the production configuration, the test publish refuses to run, stating that the cert isn't added on the target Azure deployment. This makes sense, I haven't added the cert to the test site. However, trying to remove the test configuration for the certificate throws another error, as the csdef file has defined a certificate and not providing one in the test config prevents the publish.
How do I setup a configuration with the cert and one without the cert?
Below is my production configuration, with sensitive data removed.
<Role name="MyApp">
<Instances count="1" />
<ConfigurationSettings>
<Setting name="Microsoft.WindowsAzure.Plugins.Diagnostics.ConnectionString" value="" />
<Setting name="DataConnectionString" value="" />
<Setting name="ServiceBusConnectionString" value="" />
<Setting name="ENVIRONMENT" value="production" />
</ConfigurationSettings>
<Certificates>
<Certificate name="MyAppSSL" thumbprint="thumbprint value" thumbprintAlgorithm="" />
</Certificates>
And the test configuration:
<Role name="MyApp">
<Instances count="1" />
<ConfigurationSettings>
<Setting name="Microsoft.WindowsAzure.Plugins.Diagnostics.ConnectionString" value="data" />
<Setting name="DataConnectionString" value="data" />
<Setting name="ServiceBusConnectionString" value="" />
<Setting name="ENVIRONMENT" value="staging" />
</ConfigurationSettings>
<!-- removing the certificate config prevents the publish from happening, but including it also prevents the publish since I don't have the cert on the test site-->

You can just create a dummy self-signed cert that you upload to the testing site and then update the CSCFG with that dummy cert thumbprint.

Related

Web.config not being parametrized on deploy

I have an ASP MVC app that I'm deploying to intranet servers via TeamCity, and I need some appSettings to be parametrized on deploy, so that client secrets stay hidden from developers etc.
I have the Parameters.xml file in the root of my project, the SetParameters.xml that is built with the package correctly contains all these parameters and their default values. However changing these values (even passing them to MSDeploy with -setParam) doesn't result in any changes in deployed web.config.
When I change the values in the SetParameters.xml file (which is passed to MSDeploy correctly afaik), the settings in the deployed web.config don't change and while there are "Verbose: Parameter entry 'IIS Web Application Name/1' is applicable" entries in log (IIS Web Application Name being another, standard, parameter), there's no mention of my appSettings parameters.
Also When I import the application with IIS Manager, it asks me for the values for these parameters, but I don't see them mentioned in verbose logs either, and the web.config doesn't get updated at all unless there really were some changes, and in that case the parameters aren't replaced.
My parameters.xml look like this:
<?xml version="1.0" encoding="utf-8" ?>
<parameters>
<parameter name="PiwikToken" defaultValue="__PIWIKTOKEN__">
<parameterEntry type="XMLFile" scope="\\web\.config$" match="/configuration/appSettings/add[#key='PiwikToken']/#value"/>
</parameter>
<parameter name="LoginClientSecret" defaultValue="__LOGINSECRET__">
<parameterEntry type="XMLFile" scope="\\web\.config$" match="/configuration/appSettings/add[#key='LoginClientSecret']/#value"/>
</parameter>
<parameter name="SecondClientSecret" defaultValue="__SECONDSECRET__">
<parameterEntry type="XMLFile" scope="\\web\.config$" match="/configuration/appSettings/add[#key='SecondClientSecret']/#value"/>
</parameter>
</parameters>
I've tried other scope variations ("web.config$", "web.config", "\\web.config$", "\\web\.config$", "Portal\web.config$"...) and it didn't change a thing.
The XPath expressions tested against my web.config work OK.
web.config:
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<appSettings>
<add key="webpages:Version" value="3.0.0.0" />
<add key="webpages:Enabled" value="false" />
<add key="ClientValidationEnabled" value="true" />
<add key="UnobtrusiveJavaScriptEnabled" value="true" />
<add key="LoginClientId" value="portalLocal" />
<add key="RedirectAfterAuthUrl" value="https://localhost:44321/" />
<add key="PiwikSiteId" value="12" />
<add key="PiwikToken" value="__PIWIKTOKEN__" />
<add key="LoginClientSecret" value="__LOGINSECRET__" />
<add key="SecondClientSecret" value="__SECONDSECRET__" />
</appSettings>
<connectionStrings>
<add name="appDB" connectionString="..." />
</connectionStrings>
...
Other things I've checked:
Parametrization in MSDeploy is switched on
The file "parameters.xml", which is generated inside the .zip package, contains
<parameters>
<parameter name="IIS Web Application Name" defaultValue="Portal" tags="IisApp">
<parameterEntry kind="ProviderPath" scope="IisApp" match="^D:\\BuildAgent\\work\\fec2f9c37ed1ec8e\\Portal\\obj\\DEV\\Package\\PackageTmp$" />
<parameterEntry kind="ProviderPath" scope="setAcl" match="^D:\\BuildAgent\\work\\fec2f9c37ed1ec8e\\Portal\\obj\\DEV\\Package\\PackageTmp$" />
</parameter>
<parameter name="Add write permission to App_Data Folder" description="Add write permission to App_Data folder" defaultValue="{IIS Web Application Name}/App_Data" tags="Hidden">
<parameterEntry kind="ProviderPath" scope="setAcl" match="^D:\\BuildAgent\\work\\fec2f9c37ed1ec8e\\Portal\\obj\\DEV\\Package\\PackageTmp\\App_Data$" />
</parameter>
<parameter name="PiwikToken" defaultValue="__PIWIKTOKEN__" />
<parameter name="LoginClientSecret" defaultValue="__LOGINSECRET__" />
<parameter name="SecondClientSecret" defaultValue="__SECONDSECRET__" />
</parameters>
According to various sources across the net, the common cause for this would be that the scope is wrong or that the match regexp is wrong, but I've tried all kinds of variants without success.
If anyone has any ideas about what to try, or what could be the cause, I'll be glad to explore further or try it out, but right now I've spent a few days on this and cannot think of what else to try. Thanks!
EDIT: Now I've created a fresh WebAPI project with MVC, added just parameters.xml, added those parameters to web.config, and the web.config still doesn't get parametrized on deploy. So is it some IIS setting somewhere?
EDIT2: After experimenting I found out that the generated parameters.xml inside the zip package really should containt "match" and "scope" attributes and they don't - since when I rewrite the xml so that it contains parameterEntry with proper match and scope, and repackage it into the zip, it starts working.
EDIT3: And the problem seems to be having attribute named "type", when it should've been named "kind". Now I only wonder, where did I first get that example I used...
The problem was I had an attribute named "type" in my parameters.xml, when it should've been named "kind".
Found out thanks to https://forums.iis.net/t/1177518.aspx

Where is Azure Service Definition Schema (.csdef File) located in Windows

I have a web app running in a Virtual Machine hosted in an Azure Cloud Service. I use Windows. I'm trying to secure the application by installing a SSL certificate.
Here it says that to do so:
"In your development environment, open the service definition file
(CSDEF)"
Development environment? what's that? Eclipse? (I don't use Visual Studio)
This is how this file typically looks like:
<?xml version="1.0" encoding="utf-8"?>
<ServiceDefinition name="CloudService1" xmlns="http://schemas.microsoft.com/ServiceHosting/2008/10/ServiceDefinition">
<WebRole name="WCFServiceWebRole2">
<Endpoints>
<InputEndpoint name="HttpIn" protocol="http" port="80" />
<InputEndpoint name="Https" protocol="https" port="443" certificate="SSL" />
</Endpoints>
<Imports>
<Import moduleName="Diagnostics" />
</Imports>
<Certificates>
<Certificate name="SSL" storeLocation="LocalMachine" storeName="My" />
<Certificate name="MSSecAuth" storeLocation="LocalMachine" storeName="CA" />
<Certificate name="MSInternetAuth" storeLocation="LocalMachine" storeName="CA" />
</Certificates>
<LocalResources>
<LocalStorage name="Logs" cleanOnRoleRecycle="false" sizeInMB="100"/>
</LocalResources>
</WebRole>
</ServiceDefinition>
The question is: Where is the service definition file (CSDEF) located?
If you're running in a Virtual Machine the cloud service definition doesn't apply to you. This is used for deployments to Web or Worker Roles and is normally created when you add an 'Azure Cloud Service' project to a Visual Studio solution.
If you want to protect your VM-based service using SSL you would do this as you would on-premise and ensure that you open port 443 as an endpoint to the VM (http://azure.microsoft.com/en-us/documentation/articles/virtual-machines-set-up-endpoints/).

Azure cloud service definition problems

I have a cloud service which was running fine for a while after upgrading to Azure 2.0 SDK. It has now mysteriously stopped working. I am getting this in the Azure machines event log.
The application '/' belonging to site '19369254' has an invalid
AppPoolId 'ddcc23fe-8eee-4412-a4dd-56b50e18d9f2' set.
Therefore, the application will be ignored.
Followed by :
Site 19369254 was disabled because the root application defined for the site is
invalid. See the previous event log message for information about why the
root application is invalid.
and :
A process serving application pool 'ddcc23fe-8eee-4412-a4dd-56b50e18d9f2'
terminated unexpectedly. The process id was '3696'.
The process exit code was '0x103'.
My service definition:
<?xml version="1.0" encoding="utf-8"?>
<ServiceDefinition name="SMEEDI.Cloud" xmlns="http://schemas.microsoft.com/ServiceHosting/2008/10/ServiceDefinition" schemaVersion="2013-03.2.0">
<WebRole name="SMEEDI.Portal" enableNativeCodeExecution="true">
<Startup>
<Task commandLine="startup.cmd" executionContext="elevated" taskType="simple"></Task>
</Startup>
<ConfigurationSettings>
<Setting name="DiagnosticsConnectionString" />
<Setting name="DataConnectionString" />
<Setting name="BaseUrl" />
<Setting name="DatabaseConnectionString" />
<Setting name="Environment" />
</ConfigurationSettings>
<Sites>
<Site name="Smeedi_WebRole" physicalDirectory="..\..\..\SMEEDI.Portal">
<Bindings>
<Binding name="HttpIn" endpointName="HttpIn" />
</Bindings>
</Site>
</Sites>
<Endpoints>
<InputEndpoint name="HttpIn" protocol="http" port="80" />
</Endpoints>
<Imports>
<Import moduleName="RemoteAccess" />
<Import moduleName="RemoteForwarder" />
</Imports>
</WebRole>
</ServiceDefinition>
How could this suddenly stop working?
What is wrong with the service definition?
The error you are receiving should have nothing to do with the CSDEF file. It appears to be an issue with the configuration of IIS on the Azure instance.
You can try to reimage the instance from the Azure portal or log into the instance to do further troubleshooting. If you choose to remote in, I would examine the IIS configuration, specifically the Application Pools and virtual directories, especially the path since you are changing it in the CSDEF.
Azure should take care of that configuration on its own, which is why reimage or delete and redeploy may be a better first step.

Mutual (2-way) SSL between Azure Cloud Service and Salesforce

Overview
So I've been jumping through flaming hoops to trying to create a Mutual (2-way) SSL connection between a WCF service hosted on a cloud service (using a webrole) and a Salesforce callout. I'm creating a mega question to outline all the steps I've taken and where I am currently stuck.
Process/Progress
1: Set up SSL on Azure Cloud Service (Success)
Created CNAME pointing the SSL domain (service.mydomain.com) to azure production endpoint (service.cloudapp.net)
Uploaded a signed Certificate to Azure
Added Certificate to WebRole and configured a Https Endpoint using certificate
Created WCF web.config binding with Security Mode="Transport"
After doing this 1-way SSL is working correctly
2: Create Client Certificate using with salesforce. This is an unsigned certificate issued by salesforce. You can download it as a .cer (DONE)
3: Install Salesforce Client Certificate using a Start-up Task (see this article) (DONE, but I'm not sure how to confirm it really worked other than the fact that no error was thrown on deployment)
4: Set up WCF to require Client Certificate (DONE, see web.config)
5: Unlock VM IIS webserver/security/access/sslflags using Startup task in WebRole and
Override setting in WCF web.config (DONE, see startup.cmd and web.config)
Apparently with Cloud services the Startup tasks will run before IIS is actually configured on the VM. Because of this I had to implement a hack using ping to delay and running in the background. This actually seems to work as before I was getting a 500 - Configuration Error.
6: Send Client Certificate in SF Request (DONE, see SF code)
Result:
I get a 403.7 - Forbidden error from the server. I believe that it has something to do with the SF Certificate not being trusted by the Service but I can be sure. It's obviously next to impossible to test because of the Cloud to Cloud nature of the system.
UPDATE
So I was able to resolve the 403.7 error by changing the store from 'root' to 'ca'. However now I'm dealing with an inconsistency issue where sometimes the start.cmd seems to work and other times it doesn't seem to take any or only partial effect. If my reboot my service it cycles between 500 errors for the configuration flags being locked by IIS, the 403.7 error mentioned above, and it actually working perfectly.
I found this blog post that uses a custom program (ExecWithRetries.exe) to delay and retry startup tasks until they complete successfully, but I'm still not getting inconsistent results (maybe because appcmd might not throw an error even if it runs before IIS is configured by the VM?).
What my startup.cmd now looks like:
%windir%\System32\inetsrv\appcmd.exe unlock config /section:system.webServer/security/access
REM certutil -addstore -enterprise -f -v root Startup\MagnetClient.cer
certutil -addstore ca Startup\MagnetClient.cer
And the task config like this:
<startup>
<Task commandLine="Startup/ExecWithRetries.exe "/c:AddCert.cmd" /d:60000 /r:20 /rd:5000" executionContext="elevated" taskType="background" />
</startup>
If anyone can has a solution to get consistent result I will award them the bounty.
Code References
WCF web.config
<configuration>
<system.web>
<customErrors mode="Off"></customErrors>
<compilation debug="true" targetFramework="4.0" />
</system.web>
<system.serviceModel>
<services>
<service name="WCFServiceWebRole1.Service1" behaviorConfiguration="metadata" >
<endpoint name="basicHttp"
binding="basicHttpBinding"
bindingConfiguration="https"
contract="WCFServiceWebRole1.IService1" >
</endpoint>
</service>
</services>
<bindings>
<basicHttpBinding>
<binding name="https">
<!-- Step 1 -->
<security mode="Transport">
<!-- step 4 -->
<transport clientCredentialType="Certificate" />
</security>
</binding>
</basicHttpBinding>
</bindings>
<behaviors>
<serviceBehaviors>
<behavior name="metadata">
<serviceMetadata httpsGetEnabled="true" />
<!--<serviceDebug includeExceptionDetailInFaults="true"/>-->
</behavior>
</serviceBehaviors>
</behaviors>
<serviceHostingEnvironment multipleSiteBindingsEnabled="true" />
</system.serviceModel>
<system.webServer>
<httpErrors errorMode="Detailed"/>
<asp scriptErrorSentToBrowser="true" />
<modules runAllManagedModulesForAllRequests="true"/>
<!-- Override iis config mentioned in step 5 -->
<security>
<access sslFlags="SslRequireCert"/>
</security>
</system.webServer>
</configuration>
WebRole Service Definitions
<ServiceDefinition name="WindowsAzureProject1" xmlns="http://schemas.microsoft.com/ServiceHosting/2008/10/ServiceDefinition" schemaVersion="2013-03.2.0">
<WebRole name="WCFServiceWebRole1">
<!-- step 3 & 5 -->
<Startup>
<Task commandLine="Startup/startup.cmd" executionContext="elevated" taskType="background">
</Task>
</Startup>
<Sites>
<Site name="Web">
<Bindings>
<Binding name="Endpoint1" endpointName="Endpoint1" />
<Binding name="Endpoint2" endpointName="Endpoint2" />
</Bindings>
</Site>
</Sites>
<Endpoints>
<InputEndpoint name="Endpoint1" protocol="http" port="80" />
<!-- step 1 -->
<InputEndpoint name="Endpoint2" protocol="https" port="443" certificate="example.example.com" />
</Endpoints>
<Imports>
<Import moduleName="Diagnostics" />
</Imports>
<LocalResources>
<LocalStorage name="WCFServiceWebRole1.svclog" sizeInMB="1000" cleanOnRoleRecycle="false" />
</LocalResources>
<Certificates>
<!-- Step 1 -->
<Certificate name="example.example.com" storeLocation="LocalMachine" storeName="My" />
<Certificate name="Go Daddy Secure Certification Authority" storeLocation="LocalMachine" storeName="Trust" />
<Certificate name="Go Daddy Class 2 Certification Authority" storeLocation="LocalMachine" storeName="Trust" />
</Certificates>
<ConfigurationSettings>
</ConfigurationSettings>
</WebRole>
</ServiceDefinition>
startup.cmd
REM *HACK to wait a ridiculously long time until we can be
REM *pretty sure the VM has initialized IIS
ping -n 600 127.0.0.1 nul
%windir%\System32\inetsrv\appcmd.exe unlock config /section:system.webServer/security/access
REM add SF client certy to root store
certutil -addstore -enterprise -f -v root Statup\ClientCert.cer
Sales Force Example Code
string b = '';
b = b + '<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/"><s:Body>';
b = b + '<TestService xmlns="http://tempuri.org/">';
b = b + '<echoString>test Message</echoString>';
b = b + '</TestService></s:Body></s:Envelope>';
Http h = new Http();
HttpRequest req = new HttpRequest();
//this should add the certificate created by salesforce
req.setClientCertificateName('MyClientCert');
req.setMethod('POST');
req.setEndpoint('https://service.mydomain.com/service1.svc');
req.setHeader('Content-type','text/xml');
req.setHeader('SoapAction', 'http://tempuri.org/IService1/TestService');
req.setBody( b );
HttpResponse res = h.send(req);
string m = res.getbody();
System.Debug(m);
I'll offer a couple suggestions:
Enable RemoteAccess to your azure deployment and redeploy. Then login and MMC to the Cert store to see if the SalesForce client cert actually got installed.
Add <Runtime executionContext="elevated" /> right after <WebRole name="WCFServiceWebRole1"> in your service configuration (see my answer to this SO Post) to ensure the worker process has access to the cert store.
NOTE: A reliable way to delay is to use the CHOICE command:
:: Delay for 60 seconds
%windir%\System32\choice.exe /D Y /T 60 > NUL:
To capture the output of commands redirect the error and standard output to a file you can get to:
certutil -addstore ca Startup\sfdc-client.cert.cer 2>&1 >> %temp%\AddCerts.out
the 2> tells the console to capture the "ERROUT" handle
the &1 binds that output to the STDOUT handle
the >> appends the STDOUT handle to a file

Configuration Errors upgrading from Azure 1.6 to 1.7

I am trying to migrate to Visual Studio 2012 and Azure 1.7 from 2010 and 1.6. When I upgraded my worker project, I am now getting a bunch of errors like:
CloudServices58 : Cannot load imported module named 'Antimalware.'
Which brings me to this in Microsoft.WindowsAzure.targets:
<Target Name="PreValidateServiceModel">
<ValidateServiceFiles
AllowLegacyWebRoles="$(AllowLegacyWebRoles)"
ServiceDefinitionFile="#(SourceServiceDefinition)"
ServiceConfigurationFile="#(SourceServiceConfiguration)">
</ValidateServiceFiles>
</Target>
And errors like this in my cscfg file:
The setting
'Microsoft.WindowsAzure.Plugins.Antimalware.ServiceLocation' for role
MyProject.ProcessLogs is specified in the service configuration file,
but it is not declared in the service definition file.
The error brings me to here:
<ConfigurationSettings>
<...stuff.../>
<Setting name="Microsoft.WindowsAzure.Plugins.Antimalware.ServiceLocation" value="" />
<Setting name="Microsoft.WindowsAzure.Plugins.Antimalware.EnableAntimalware" value="" />
<Setting name="Microsoft.WindowsAzure.Plugins.Antimalware.EnableRealtimeProtection" value="" />
<Setting name="Microsoft.WindowsAzure.Plugins.Antimalware.EnableWeeklyScheduledScans" value="" />
<Setting name="Microsoft.WindowsAzure.Plugins.Antimalware.DayForWeeklyScheduledScans" value="" />
<Setting name="Microsoft.WindowsAzure.Plugins.Antimalware.TimeForWeeklyScheduledScans" value="" />
<Setting name="Microsoft.WindowsAzure.Plugins.Antimalware.ExcludedExtensions" value="" />
<Setting name="Microsoft.WindowsAzure.Plugins.Antimalware.ExcludedPaths" value="" />
<Setting name="Microsoft.WindowsAzure.Plugins.Antimalware.ExcludedProcesses" value="" />
</ConfigurationSettings>
I am pretty lost, any ideas?
I just needed to download and install this: http://www.microsoft.com/en-us/download/details.aspx?id=29209

Resources